注解与反射

109 阅读6分钟

注解(Annotation)

  1. 不是程序本身,可以对程序做出解释,可被其他程序读取
  2. 注解是以“@注释名”在代码中存在的,还可以添加一些参数值
  3. 可以附加在package(包),class(类),method(方法),field(属性)等上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问

内置注解

  • @Override 表示此方法是重写的方法
  • @Deprecated 表示此方法可以使用,但是不推荐程序员使用。或存在更好的方式
  • @SupperssWarnings(“all”) 镇压警告,可以忽略一切警告信息

元注解

@Target(value = {ElementType.METHOD,ElementType.TYPE})//表示注解可以用在什么地方
@Retention(RetentionPolicy.RUNTIME)//使用范围  RUNTIME>CLASS>SOURCES
@Documented//表示是否将我们的注解生成在javadoc中
@Inherited//子类可以继承父类的注解
@interface myAnnotation {
//注解的参数:参数类型+参数名() +参数默认值(非必须);
String name() default "";
int age() default 0;
int id() default -1;
}
  • 元注解用来注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型做说明
  • @Target:用于描述注解的使用范围
  • @Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期
  • @Document:说明该注解将被包含在javadoc中
  • @Inherited:说明子类可以继承父类中的该注解

自定义注解

@Target(value = {ElementType.METHOD,ElementType.TYPE})//表示注解可以用在什么地方
@Retention(RetentionPolicy.RUNTIME)//使用范围  RUNTIME>CLASS>SOURCES
@Documented//表示是否将我们的注解生成在javadoc中
@Inherited//子类可以继承父类的注解
@interface myAnnotation {
//注解的参数:参数类型+参数名() +参数默认值(非必须);
String name() default "";
int age() default 0;
int id() default -1;
}
  • @interface用来声明一个注解,格式:public @ interface注解名{定义内容}
  • 其中的每个方法实际上是声明了一个配置参数.
  • 方法的名称就是参数的名称.
  • 返回值类型就是参数的类型(返回值只能是基本类型,Class, String,enum).
  • 可以通过default来声明参数的默认值
  • 如果只有一个参数成员, -般参数名为value
  • 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值.

反射(Java Reflection)

  • 反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到。

  • 加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个类就像是一面镜子,透过这个镜子看到类的结构,所以,形象的称之为“反射”

  • 反射相关主要类

    1. java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
    2. java.lang.reflect.Method:代表类的方法,Method对象表示某个类的方法
    3. java.lang.reflect.Field: 代表类的成员变量,Field对象表示某个类的成员变量
    4. java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器
  • 类的主动引用(一定会发生类的初始化)

    1. 当虚拟机启动,先初始化main方法所在的类
    2. new一个类的对象
    3. 调用类的静态成员(除了final常量)和静态方法
    4. 使用java.lang.reflect包的方法对类进行反射调用
    5. 当初始化一个类,如果其父类没有被初始化,则会优先初始化其父类
  • 类的被动引用(不会发生类的初始化)

    1. 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:通过子类引用父类的静态变量,不会导致子类初始化
    2. 通过数组定义类引用,不会触发此类的初始化
    3. 引用常量不会触发此类的初始化(常量在链接阶段就已经存入类的常量池中了)
  • 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。

  • 类缓存:标准的JavaSE类加载器可以按照要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。

  • 双亲委派机制

    1. 当Application ClassLoader 收到一个类加载请求时,他首先不会自己去尝试加载这个类,而是将这个请求委派给父类加载器Extension ClassLoader去完成。
    2. 当Extension ClassLoader收到一个类加载请求时,他首先也不会自己去尝试加载这个类,而是将请求委派给父类加载器Bootstrap ClassLoader去完成。
    3. 如果Bootstrap ClassLoader加载失败(在<JAVA_HOME>\lib中未找到所需类),就会让Extension ClassLoader尝试加载。
    4. 如果Extension ClassLoader也加载失败,就会使用Application ClassLoader加载。
    5. 如果Application ClassLoader也加载失败,就会使用自定义加载器去尝试加载。
    6. 如果均加载失败,就会抛出ClassNotFoundException异常。
//通过对象获取类
        Student student=new Student();
        Class c1=  student.getClass();
        //通过包名加类名获取类
        Class c2=Class.forName("suan.Study.Student");
        //通过类名获取类
        Class<Student> c3 = Student.class;
        //获取其父类
        Class c4 = c1.getSuperclass();

static ClassforName(String name ) -->返回指定类名name的Class对象

Object newInstance() -->调用缺省构造函数,返回Class对象的一个实例

getName() -->返回此Class对象所表示的实体(类,接口,数组类或void)的名称。

Class getSuperClass() -->返回当前Class对象的父类的Class对象

Class[] getinterfaces() -->获取当前Class对象的接口

ClassLoader getClassLoader() -->返回该类的类加载器

Constructor[] getConstructors() -->返回一个包含某些Constructor对象的数组

Method getMothed(String name,Class… T) -->返回一一个Method对象,此对象的形参类型为paramType

Field[] getDeclaredFields() -->返回Field对象的一个数组

反射获取类的信息

Class c1 = Class.forName("suan.Study.Student");

	System.out.println(c1.getName());//获取 包名加类名
    System.out.println(c1.getSimpleName());//获取类名
    System.out.println("---------------");
    Field[] fields = c1.getFields();//获取public属性
    for (Field field:fields){
        System.out.println(field);
    }
    System.out.println("---------------");
    fields=c1.getDeclaredFields();//获取所有属性
    for (Field field:fields){
        System.out.println(field);
    }
    System.out.println("---------------");//获得指定属性的值
    Field age = c1.getDeclaredField("age");
    System.out.println(age);
    System.out.println("---------------");//获得类的方法
    Method[] methods = c1.getMethods();//获取本类及其父类的全部public方法
    for (Method method : methods) {
        System.out.println(method);
    }
    System.out.println("---------------");
    methods = c1.getDeclaredMethods();//获取本类的所有方法
    for (Method method : methods) {
        System.out.println(method);
    }
    System.out.println("---------------");//获取指定方法
    Method getAge = c1.getMethod("getAge",null);//null可以不写,但建议写上
    Method setAge = c1.getMethod("setAge", int.class);
    System.out.println(getAge);
    System.out.println(setAge);
    System.out.println("---------------");//获取构造器
    Constructor[] constructors = c1.getConstructors();//public
    for (Constructor constructor : constructors) {
        System.out.println(constructor);
    }
    constructors = c1.getDeclaredConstructors();//全部
    for (Constructor constructor : constructors) {
        System.out.println(constructor);
    }
    Constructor constructor1 = c1.getConstructor(String.class, String.class,int.class);//获取指定构造器
    System.out.println(constructor1);
    constructor1 = c1.getConstructor(null);//获取指定构造器
    System.out.println(constructor1);

通过反射创建对象并设置基本信息

Class c1 = Class.forName("suan.Study.Student");

    Student Student1 = (Student)c1.newInstance();//通过无参构造器创建对象
    System.out.println(Student1);
    //通过有参构造器 传入参数 创建对象
    Constructor constructor = c1.getDeclaredConstructor(String.class, String.class, int.class);
    Student Student2 = (Student)constructor.newInstance("张三", "男", 23);
    System.out.println(Student2);

    //通过反射调用普通方法
    Student Student3 = (Student) c1.newInstance();
    Method setAge = c1.getDeclaredMethod("setAge", int.class);
    setAge.invoke(Student3,32);
    System.out.println(Student3.getAge());

    //通过反射操作属性
    Student Student4 = (Student) c1.newInstance();
    Field sex = c1.getDeclaredField("sex");
    //不能直接访问私有属性,先通过setAccessible()方法关闭安全检测后,在进行操作
    sex.setAccessible(true);//关闭之后会提高效率
    sex.set(Student4,"女");
    System.out.println(Student4.getSex());

获取类的注解信息

public static void t2() throws ClassNotFoundException, NoSuchMethodException {
    Class c1 = Class.forName("suan.Study.AnnotationStudy");//获取类
    Annotation[] annotations = c1.getDeclaredAnnotations();//获取类的所有注解
    for (Annotation anInterface : annotations) {
        System.out.println(anInterface);
    }
    //获取类的注解
    myAnnotation myAnnotation = (myAnnotation) c1.getAnnotation(myAnnotation.class);
    System.out.println(myAnnotation.name());//获取注解的值

    //获取方法的注解
    Method test = c1.getDeclaredMethod("test");
    suan.Study.myAnnotation annotation = test.getAnnotation(myAnnotation.class);
    System.out.println(annotation.name());
    System.out.println(annotation.age());
    System.out.println(annotation.id());
}