java基础笔记13 - 反射 注解(重新整理)

十三 注解,反射

之前整理到这里之后就没啥动力了,笔记也记得很乱,需要重新整理一下

为啥整理

注解+反射是很重要的部分,very import 因为他们是框架的基础‘

以后看的代码里面会有大量的注解

1.注解 Annotation\

1.1 啥是注解

  • jdk5.0引入的新技术,很简单,但是很重要

  • 注解不是程序本身,但是对程序进行解释

    • 可以被其他程序(比如:编译器 )读取
    • @朱世明,可以添加一些参数值
@SuppressWarnings(value="unchecked")
  • 可以使用在package,class,method,field等上面
  • 相当于添加了额外的信息
  • 可以通过反射机制变成实现对这些元数据的访问

1.2 内置注解

1.2.1常见的内置注解

  • @override 定义在java.lang.Override中,表示一个方法声明打算重写超类的方法
  • @Depreacted 废弃的,表示该方法不被推荐使用,或者有更好的替代品
    • 这个注解本身已经被废弃了,
  • @SuppressWarning 镇压警告
//重写的注解
@Override
public String toString() {
    return super.toString();
}
//不推荐程序员使用,但是可以用,调用时一般IDE会用删除线提醒
@Deprecated
public void test(){
    System.out.println("Deprecated");
}

//镇压警告, 没来li会警告未使用,但是使用@SuppressWarning后,不显示了
@SuppressWarnings("all")
public void test02(){
    List li=new ArrayList();
}

1.2.2 元注解

  • 元注解:解释其他注解的注解,官方定义四个meta-Annoatation

  • @Target 描述该注解的适用范围,能被用在哪

  • @Retentinon:表示需要在什么界别保存注释信息,用于表述注解的声明周期

    • (SOURCE<CLASS<RUNTIME)
    • 一般自定义的都是作用在RUNTIME上
  • @Documented : 是否将注解生成在文档JavaDoc中

  • @Inherited:表示子类可以继承父类的注解

1.3 自定义注解

1.3.1怎么做

  • ​ 使用@interface自定义注解,并自动竭诚了Annotation接口

  • public @interface 注解名

    • 其中每个方法实际上各一个配置参数
    • 方法的名称就是参数的名称
    • 注解必须要有值

1.3.2例子

public class StringTest extends Object{
    @MyAnnoatation(name="张酱",schools = {"东大,哈工程"})
    @MyAnnotation2("不用写value=")
    public  void test(){

    }
}

@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface MyAnnoatation{
    //注解的参数:参数类型+参数名();
    //注解可以有默认值,可以显示赋值
    //一定注意这里定义的不是方法,是注解的参数
    String name() default "";
    int age() default 0;
    int id() default -1;//如果默认值为-1,代表不存在
    String[] schools() default {"家里蹲","哈佛"};
}
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
    String value();//只有一个参数,约定命名成value
    //只有这样,使用注解的时候就可以隐式的赋值
    //否则,隐式赋值会报错
}

2.反射

2.1什么是java Reflection

2.1.1 静态vs动态语言

  • 动态:可以再运行时,改变自身结构
    • c#,js,php,python ,Object-c
  • 静态:java,c,c++
  • 因为java有了反射机制,所以成为了准动态语言

Reflection,反射机制允许程序在执行期间借助Reflection API获取任何类的内部信息,并能直接操作任意对象的内部属性,以及方法.

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(getClass方法返回的)(一个类只能有个一Class对象)

这个对象就包含了完整的类的结构信息。可一个通过这个对象看到类的结构,就像一面镜子,透过对象看到类的结构,所以称为:反射

Class c=Class.forName("java.lang.String")

![image-20220308203103335](java笔记 13 反射重新整理.assets/image-20220308203103335.png)

2.1.2 反射功能:

  • 运行时判断任意对象所属的类
  • 运行时构造人一个类的对象
  • 运行时判断任意一个类的成员属性
  • 运行时获取泛型信息
  • 运行时调用任意一个对象的成员变量和方法
  • 运行时处理注解
  • 生成动态代理 (AOP)

2.1.3 优缺点

  • 灵活性很大
  • 慢,效率低
public class StringTest {
    public static void main(String[] args) throws ClassNotFoundException {
        //通过反射获取类的class对象
        Class c1 = Class.forName("User");
        System.out.println(c1);
        Class c2 = Class.forName("User");
        //一个类在内存中只有一个Class对象
        //一个类被加载后,类的真个结构都会被封装在Class对象中
        System.out.println(c1.hashCode());
        System.out.println(c2.hashCode());
    }
}
//实体类:pojo
//name,id,age属性+构造器+getset方法+toString
class User {
    ...
}

总结:通过对象反射求出类的信息

2.1.4 学反射能干啥

学之前:

//反射之前对一个类能干啥
@Test
public void test1(){
    Person p1=new Person("to",12);
    p1.age=14;
    System.out.println(p1.toString());
    p1.show();
}

反射之后

//反射能干啥
@Test
public void test2() throws InstantiationException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException {
    Class c = Person.class;
    //通过反射创建Person类的对象
    Constructor cons = c.getConstructor(String.class, int.class);
    Object obj = cons.newInstance("tom", 12);
    Person p = (Person)obj;
    System.out.println(obj.toString());

    //2.用过反射,调用对象指定的属性
    Field age = c.getDeclaredField("age");
    age.set(p,10);
    System.out.println(p.toString());
    //3.用过反射,调用对象指定的属性
    Method show = c.getDeclaredMethod("show");
    show.invoke(p);

    //4.通过反射,调用对象的私有结构!!
    //调用私有构造方法
    Constructor cons1 = c.getDeclaredConstructor(String.class);
    cons1.setAccessible(true);
    Person p1 = (Person)cons1.newInstance("jjj");
    System.out.println(p1.toString());
    //调用私有属性
    Field name = c.getDeclaredField("name");
    name.setAccessible(true);
    name.set(p1,"hanry");
    System.out.println(p1.toString());
    //调用私有方法
    Method showNation = c.getDeclaredMethod("showNation",String.class);
    showNation.setAccessible(true);
    String nation = (String) showNation.invoke(p1, "中国");
    System.out.println(nation);

}
class Person{
    private String  name;
    public int age;
	...
        
    public void show(){
        System.out.println("我是一个人");
    }
    private String showNation(String Nation){
        System.out.println("我来自"+Nation);
        return Nation;
    }
}

2.2 理解Class类并获取Class实例

2.2.1 自己的理解:

  • Class类可以理解成描述类的类,这个类的成员包括:类的名称,属性,方法等等
  • 编写的代码中,每一个class 类{....}都是Class类的一个对象(系统创建)
  • 比如cat类,Person类,car类,这些都是Class类的对象
  • 程序运行时,在内存中生成这些对象,每个编写出来的class都只能对应一个 对象(应该有点像单例)
  • 一个非法的代码用作比如:Class Person=new Class(); Class Cat = new Class();

而反射呢,就是通过Person类创建的对象p1,来获取Class类的一个对象(即Person)的信息

2.2.2 Class类的方法

  • ​ static ClassforName(String name)
    • 返回指定类名的Class对象
  • Obejct newInstance()
    • 通过反射,创建一个对象
  • getName()
    • 返回次Class对象所代表的的实体(类,接口,数组类,void)
  • Class getSuperClass()
    • 返回Class对象的父类的对象
  • Class[] getinterfaces()
  • ClassLoader getConstructors()
  • Method getMothed(String name,Class .. T)
  • Field[] getDeclaredFields()

2.2.3 获取Class类的实例

  • 已知具体的类,通过类的class属性获取,最安全
    • Class clazz = Person.class;
  • 已知一个类的实例,通过调用getClass方法获取
    • Class clazz= person.getClass();
  • 已知一个类的包+类名,通过Class类的静态方法forname()获取,可能抛ClassNotFount的异常
    • 谁知道你给的类名对不对啊
    • Class clazz =Class.forName("com.zhang.Person");
  • 基本内置类型的包装类都有一个TYPE属性
    • Class clazz=Integer.TYPE;

2.2.4 哪些类型有Class对象

  • ​ class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
  • interface:接口
  • [ ]:数组
  • enum:枚举
  • annotation:注解@interface
  • primitive type: 基本数据类型
  • void
Class c1 = Object.class;//普通类 结果:class java.lang.Object
Class c2 = Comparable.class;//接口 结果:interface java.lang.Comparable
Class c3 = String[].class;//一维数组 结果:class [Ljava.lang.String;
Class c4 = int[][].class;//二维数组 结果:class [[I
Class c5 = Override.class;//注解 结果:interface java.lang.Override
Class c6 = ElementType.class;//枚举类型 结果:class java.lang.annotation.ElementType
Class c7 = Integer.class;//基本数据类型 结果:class java.lang.Integer
Class c8 = void.class;//void 结果:void
Class c9 = Class.class;//Class 结果:class java.lang.Class

System.out.println(c1);

2.3 类的加载与ClassLoader

2.3.1 java内存分析

  • ​ java内存:
    • 堆:
      • 存放new出来的对象和数组
      • 可以被所有线程共享,不会存放别的对象引用
    • 栈:
      • 存放基本变量类型(包括类型的具体数值)
      • 引用对象的变量(包括引用在堆里面的地址)
    • 方法区:
      • 可以被所有的线程共享
      • 包含了所有的class和static变量

2.3.2 类的加载 链接 初始化

  • 加载:将class文件字节码内容加载到内存中,并将这些静态数据换成 方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象
  • 链接:将java类的二进制代码合并到jaaaaavm的运行状态之中:
    • 验证,确保加载的类的信息符合jvm规范
    • 准备:正式为类变量(static)分配内存,并设置类变量默认初始值,这些内存都分配在方法区中
    • 解析:虚拟机常量池内的符号引用(常量名)替换为引用(地址)的过程
  • 初始化:
    • 执行类构造器<clinit>()方法的过程,类构造器()方法是由编译期间自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)
    • 当初始化一个类的时候,如果发现父类还未初始哈, 先触发父类的初始化
    • 虚拟机 会保证一个类的<()方法在多线程环境中被正确加锁与同步
public class Test01 {
    public static void main(String[] args) {
        System.out.println(A.m);
        A a=new A();
        System.out.println(A.m);
        /*
            1.加载到内存,产生一个类对应Class对象
            2.链接,m=0(初始值
            3.初始化:
                <clinit>(){
                    Sout("A类静态代码")
                    m=30
                    m=10
                }
        */
    }
}
class A{
    static {
        System.out.println("A类静态代码块");
        m=30;
    }
    static int m=10;
    public A(){
        m=20;
        System.out.println("A的无参构造");
    }
}

结果:

A类静态代码块
30
A的无参构造
20

2.3.3 啥时类会初始化

类分为主动引用和被动引用:

  • ​ 主动引用:
    • 虚拟机启动时,main方法所在类
    • new一个类对象
    • 调用类的静态成员(除了final常量)和静态方法
    • 使用java.lang.reflect包的方法对类进行反射调用
    • 当初是话一个类的时候,父类没有初始化,则先初始化父类
  • 类的被动引用(不会发生类的初始化)
    • 访问静态域是,只有真正声明了这个域的类才会被初始化,如:子类调用父类的静态变量,不会导致子类初始化
    • 声明类数组Person[] ps=new Person[5];
    • 调用类的常量属性(常量在链接阶段就存入调用类的常量池了

2.3.4 类加载器的作用

类缓存:类被加载后会维持加载一段时间,提高效率,jvm可以使用垃圾回收干掉它

  • 加载器:
    • 引导类加载器:C++编写,jvm自带的类加载器,负责java平台核心库,用来装载核心类库,该加载器无法直接获取
    • 扩展类加载器:负责jre/lib/ext目录下的jar包或者-D java.ext.dirs 指定目录下 的jar包装入工作库
    • 系统类加载器:负责java -classpath 或 -D java.class.path所指的目录下的类与jar包装入工作,是最常用的加载器
public static void main(String[] args) {
    //获取系统类的加载器
    ClassLoader systeemClassLoader = ClassLoader.getSystemClassLoader();
    System.out.println(systeemClassLoader);

    //获取系统类加载器的父类加载器---->扩展类加载器
    ClassLoader parent=systeemClassLoader.getParent();
    System.out.println(parent);

    //获取扩展类加载器的父类加载器------>根加载器(c/c++)
    ClassLoader root=parent.getParent() ;
    System.out.println(root);
    
    //测试当前类的类加载器
    ClassLoader classLoader = Class.forName("Test01").getClassLoader();
    System.out.println(classLoader);
    
    //测试jdk内置的类的加载器(根加载器)
    classLoader=Class.forName("java.lang.Object").getClassLoader();
    System.out.println(classLoader);

    //获得系统类加载器可以加载的路径
    System.out.println(System.getProperty("java.class.path"));

}
jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc
jdk.internal.loader.ClassLoaders$PlatformClassLoader@6e8dacdf
null
jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc
null
C:\Users\NGI\IdeaProjects\test\out\production\test;C:\Users\NGI\.m2\repository\junit\junit\4.13.1\junit-4.13.1.jar;C:\Users\NGI\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar  
    

2.4 创建运行时类的对象

通过反射获取运行时类的完整结构

Field,Method,Constructor,SuperClass,Interface,Annotation

Class c1 = Class.forName("com.zhang.User");

//获得类的名字
System.out.println(c1.getName());//包名+类名
System.out.println(c1.getSimpleName());
//获得类的属性
Field[] fields=c1.getFields();//这个只能找到public的属性
fields= c1.getDeclaredFields();//找到全部属性
for(Field field:fields){
    System.out.println(field);
}
//获得指定属性的值
Field name = c1.getField("sex");//同上,这个只能找到public的
System.out.println(name);

System.out.println("------------------------------------------------------");
//获得类的方法
c1.getMethods();//获得本类或父类的全部public方法
Method[] methods = c1.getDeclaredMethods();//获得本类的全部方法(无父类)
for (Method m : methods ){
    System.out.println(m);
}
//获得指定的方法
//为啥要提供参数类型,因为这个方法可能被重载了
Method getAge = c1.getMethod("getAge", null);//穿的参数是方法的参数类型,不是参数值
Method setAge = c1.getMethod("setAge", int.class);
System.out.println(getAge);
System.out.println(setAge);

//获得指定的构造器
Constructor[] cons = c1.getConstructors();//获得全部public的构造方法
cons = c1.getDeclaredConstructors();//获得全部构造方法
//获得指定构造器
Constructor con = c1.getConstructor(int.class);

扯淡的来了啊,上面的东西一般开发都不直接用,

那么反射到底要干啥?

或者说有了Class对象,做啥?

2.4.1 Class对象的作用

a 创建类的对象
  • 必须有无参构造器才行
  • 构造器的权限要够
//通过反射动态的创建对象
public static void main(String[] args) throws Exception {
    Class c1 = Class.forName("com.zhang.User");
    //构造一个对象
    User user = (User)c1.newInstance();//返回的是Object类型
    //本质上是调用User的无参构造器,如果原类里面没提供,且被有参覆盖,则报错。。
    System.out.println(user);
}
User{age=0, name='null', sex='null'}

思考:没有无参构造怎么办,需要创建有具体属性的对象怎么办?

b 通过反射获取构造器并创建对象
Class c1 = Class.forName("com.zhang.User");

//通过构造器创建对象
Constructor con = c1.getDeclaredConstructor(int.class, String.class, String.class);
User user = (User)con.newInstance(18, "zhang", "汉子");
System.out.println(user);
User{age=18, name='zhang', sex='nan'}
c 通过反射调用普通方法
//user对象接上面的变量
//通过反射调用普通方法
Method setAge = c1.getMethod("setAge", int.class);
setAge.invoke(user,30);//激活函数,方法.invoke(方法所属对象,方法的参数值);
System.out.println(c1.getMethod("getAge").invoke(user));
d 通过反射使用属性
User user2= (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
name.setAccessible(true);//不能直接操作私有属性,需要关闭安全检测,不然会报错,属性和方法,构造器对象都有serAccessible方法
name.set(user2,"张大壮");
System.out.println(user2.getName());

2.4.2 反射性能对比

static int  cur = 100000000;//1亿

//通过反射动态的创建对象
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
    test01();
    test02();
    test03();
}

//普通方式
public static void test01() {
    User user = new User();
    long starttime = System.currentTimeMillis();
    for (int i = 0; i < cur; i++) {
        user.getName();
    }

    long endtime = System.currentTimeMillis();
    System.out.println((endtime - starttime) + "ms");
}

//反射方式
public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    User user = new User();
    Class c1 = user.getClass();
    Method getName = c1.getDeclaredMethod("getName", null);
    long starttime = System.currentTimeMillis();
    for (int i = 0; i < cur; i++) {
        getName.invoke(user,null);
    }

    long endtime = System.currentTimeMillis();
    System.out.println((endtime - starttime) + "ms");
}

//关闭检测后
public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    User user = new User();
    Class c1 = user.getClass();
    Method getName = c1.getDeclaredMethod("getName", null);
    long starttime = System.currentTimeMillis();
    getName.setAccessible(true);

    for (int i = 0; i < cur; i++) {

        getName.invoke(user,null);
    }

    long endtime = System.currentTimeMillis();
    System.out.println((endtime - starttime) + "ms");
}

2.4.3 反射操作泛型

  • 泛型;java的一种强制的约束,确保数据的安全性,免去强制类型转换的问题
  • 但是!编译成功之后,所有和泛型有关的类型全部擦除
  • 为了用反射操作泛型,java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType集中类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型
    • ParameterType:表示一种参数化类型,比如Collection
    • GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
    • TypeVariable:是各种类型变量的公共父接口
    • WildcardType:代表一种通配符类型表达式
public class Test02 {
    public void test01(Map<String,User> map, List<User> list){
        System.out.println("test01");
    }
    public Map<String,User> test02(){
        System.out.println("test02");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
        Method m = Test02.class.getMethod("test01", Map.class, List.class);
        Type[] genericParameterTypes = m.getGenericParameterTypes();
        for (Type g : genericParameterTypes) {
            System.out.println(g);
            if(g instanceof ParameterizedType){
                Type[] actualTypeArguments = ((ParameterizedType) g).getActualTypeArguments();//获取真实类型参数
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        }

        System.out.println("-------------------------------------------------------------------");
        Method m2 = Test02.class.getMethod("test02", null);
        Type g = m2.getGenericReturnType();
        if(g instanceof ParameterizedType){
            Type[] actualTypeArguments = ((ParameterizedType) g).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }

    }

}

2.4.4 反射操作注解

  • getAnnotations
  • getAnnotaion
a. 啥是ORM
  • Object realtionship Mapping -->对象关系映射
  • 类到数据库的映射
    • 类----表
    • 属性---字段
    • 对象----记录
b. 通过反射获取注解信息
public class Test02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("Student");
        //通过反射获得注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation a : annotations) {
            System.out.println(a);
        }

        //获得注解的value值
        DBTable table = (DBTable) c1.getAnnotation(DBTable.class);
        System.out.println(table.value());

        //获得类属性的指定注解
        Field f = c1.getDeclaredField("name");
        DBField annotation = f.getAnnotation(DBField.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }
}

@DBTable("db_student")
class Student{
    @DBField(columnName = "db_id",type = "int",length = 10)
    private int id;

    @DBField(columnName = "db_name",type="int",length = 10)
    private String name;
	...
}

//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface DBTable{
    String value();
}

//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface DBField{
    String columnName();
    String type();
    int length();
}
posted @ 2022-03-09 20:34  荧惑微光  阅读(44)  评论(0编辑  收藏  举报