• 反射
  • 1. 反射机制 反射机制的相关类除了一个java.lang.Class,其余都在java.lang.reflect包下。 反射机制用于读取class字节码文件,需要注意,JVM加载字节码到内存中时都只会保存一份,多次读取class文件时不用担心也会加载多次。 反射机制相关的常用类: java.lang.Class:代表整个类的字节码,表示一个类型。 java.lang.reflect.Method:代表字节码中的方法字节码,表示一个方法。 java.lang.reflect.Constructor:代表字节码中的构造方法字节码,表示一个构造方法。 java.lang.reflect.Field:代表字节码中的属性字节码,表示一个属性。  
  • 2. 反射类字节码Class(类/类型) 获取类的字节码(java.lang.Class类)有三种方式: 第一种方式:通过Class类的静态方法forName,例如Class c1 = Class.forName("java.lang.String");就表示获取到了String这个类的class字节码,注意,这是Class类下的一个静态方法,参数需要是完整的包名。另外,Class.forName方法的使用会导致类的加载,也就是说如果希望只是执行一个类的静态代码块,并不执行其他的代码,就可以使用这个方法来进行类的加载,此时,自然就会去执行对应的静态代码了。 第二种方式:通过Object类的getClass方法,例如String s = "abc"; Class c2 = s.getClass();,即通过任何类对象的getClass方法就可以拿到对应了类字节码了,并且因为JVM只会在内存中加载一份相同类的字节码,所以这个例子的c2和第一种方式的c1使用双等号判断返回结果是true。 第三种方式:通过类的class属性,例如Class c3 = String.class;,java中任何类型都有class属性。 Class中常用的方法: String getName():返回类的完整类名(包含包路径)。 String getsimpleName():返回类的简类名(类定义名称)。 Field[] getFields():获取Class中所有public类型的Field对象(属性)。 Field[] getDeclaredFields():获取Class中所有的Field对象(属性)。 Field getDeclaredField(String name):获取指定名称的Field对象(属性)。 Method[] getDeclaredMethods():获取所有的Method对象(方法)。 Method getDeclaredMethod(String name,Class<?>... parameterTypes):根据方法名称和参数类型列表获取Method对象(方法),例如“userClass.getDeclaredMethod("login",String.class,int.class);”。 Constructor<?>[] getDeclaredConstructors():获取所有的Constructor对象(构造方法)。 Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):获取指定参数类型列表的Constructor对象(构造方法),例如“userClass.getDeclaredConstructor(String.class,int.class);”。 Class<? super T> getSuperclass():获取类的父类。 Class<?>[] getInterfaces():获取所有类实现的接口。  
  • 3. 反射属性字节码Field(字段/属性) 获取Field需要先获取到对应的类字节码Class,然后才能从类中获取到对应的Field(java.lang.reflect.Field)。 Field常用方法: Class<?> getType():返回属性的数据类型。 String getName():返回属性的名称。 int getModifiers():返回修饰符列表的代号,此代号可以使用java.lang.reflect.Modifier的静态方法toString方法传入代号获取到具体的修饰符名称列表。 void set(Object obj,Object value):给指定对象的Field对象赋予值value。 Object get(Object obj):获取指定对象的Field值(属性值)。 void setAccessible(boolean flag):设置为true时表示打破封装,如果不调用这个方法设置为true的话就没办法获取对象的私有属性了,调用这个方法之后就可以获取到所有的属性了,包括私有属性。  
  • 4. 反射方法字节码Method(方法) 获取Method需要先获取到对应的类字节码Class,然后才能从类中获取到对应的Method(java.lang.reflect.Method)。 Method中的常用方法: Class<?> getReturnType():获取返回值的数据类型。 Class<?>[] getParameterTypes():获取方法的参数类型列表。 int getModifiers():返回修饰符列表的代号,此代号可以使用java.lang.reflect.Modifier的静态方法toString方法传入代号获取到具体的修饰符名称列表。 Object invoke(Object obj,Object... args):调用指定对象的方法。  
  • 5. 反射构造方法Constructor(构造方法) 获取Constructor需要先获取到对应的类字节码Class,然后才能从类中获取到对应的Method(java.lang.reflect.Constructor)。 Constructor中的常用方法: int getModifiers():返回修饰符列表的代号,此代号可以使用java.lang.reflect.Modifier的静态方法toString方法传入代号获取到具体的修饰符名称列表。 T newInstance(Object... initargs):使用newInstance方法创建一个实例对象。  
  • 二、注解
  • 1. 定义注解(Annotation) 注解,或者称之为注释,也是一种引用数据类型,编译之后也会生成class文件,具体用法见示例: [修饰符列表] @interface 注解类型名{     // 属性定义 }   注解定义示例:  无属性的注解定义 public @ MyAnnotation{      这里面什么都不写,表示没有属性 }  有属性的注解定义  MyAnnotation2{      定义一个没有默认值的属性,在使用这个注解的时候就必须给这个属性传值      注意,注解的属性定义是有小括号的,但它不是方法,就只是属性     String name();           使用default给属性指定默认值,有默认值的属性在使用时就可以不用给这个属性传值了     int id() default 2333; }  属性只有一个,且为value时,使用时可以不用指定属性名称  MyAnnotation3{     String value(); }   注解使用示例:  使用:直接在类、方法、属性、形参、注解等上面使用形如”@注解类型名“的格式即可。  注解的使用其实就像修饰符一样在定义的前面加上就可以,但是通常的使用习惯是在定义上一行进行添加 public class AnnotationTest {     static void main(String[] args) {     }      相当于:@MyAnnotation private int id;     @MyAnnotation     private int id;      注解只有一个属性,且属性名为value时,可以不用指定属性名
@MyAnnotation3("hello")
     public AnnotationTest() {
     }
      定义了没有默认值的属性的注解,就必须给这个属性传值,有默认值的属性可以传,也可以不传
     @MyAnnotation2(name = "zhangsan" func() {
         @MyAnnotation2(name = "lisi",id = 666)
         int i = 10;
     }
      相当于:@MyAnnotation public void func2(@MyAnnotation String name)
  func2(@MyAnnotation String name) {
         System.out.println(name);
     }
 }


  • 注解属性类型:定义注解的属性时,属性的类型可以是byte、short、int、long、float、double、boolean、char、String、Class、枚举类型,以及这几种类型的数组形式,不能是其他的类型。有一个小技巧,属性如果是数组,并且使用时传入的数组元素只有一个的话,定义数组的大括号是可以不写的。

     

    2. Java内置注解

    内置注解在java.lang包下,常用的有:

    @Override:这个注解只能注解方法,并且只是给编译器在编译阶段做参考用的,和运行阶段的代码没有关系,编译器在编译时会检查这个方法是否是重写的父类方法,如果不是则会报错。
    @Deprecated:表示被标注的类、方法等元素已经过时了,不建议使用。在IDEA中,被标注的方法等会出现一条横线,提示你这个方法已过时。
     

    3. 元注解

    用来标注“注解类型”的注解,即注解的注解,称之为元注解,在java.lang.annotation包下。常用的元注解有:

    @Target(ANNOTATION_TYPE):用来指定被标注的注解可以出现在哪些位置上,参数为枚举类型ElementType的数组,具体有哪些枚举值可以参考帮助文档,如“@Target(ElementType.METHOD)”表示被标注的注解只能出现在方法上。
    @Retention(RUNTIME):用来指定被标注的注解最终保存在哪里,参数是一个枚举类型RetentionPolicy,有三个枚举值,“@Retention(RetentionPolicy.SOURCE)”表示被标注的注解保存在java源文件中,并不会出现在编译之后的class文件中,“@Retention(RetentionPolicy.CLASS)”表示被标注的注解保存在class文件中,“@Retention(RetentionPolicy.RUNTIME)”表示被标注的注解保存在class文件中,并且可以被反射机制读取出来。
     

    4. 反射注解

    以类的注解为例,首先获取到类的字节码对象后,使用Class对象的方法进行反射,常用的方法有:

    boolean isAnnotationPresent(MyAnnotation.class):判断一个类是否有指定的注解,这里需要传入一个指定注解的类字节码对象。
    getAnnotation(MyAnnotation.class):获取类的指定注解对象,这里需要传入一个指定注解的类字节码对象。
    属性获取:通过反射拿到注解对象之后,就可以通过调用方法(其实是属性)的形式获取属性值,因为注解定义属性时,本身就自带小括号,所以看起来就是在调用方法了,如“String name = annotationObj.name();”
    注:方法、属性等的注解获取也是和上面的方法一样,而且调用的方法等大多也都是一样的。

     
    5. 注解的作用

    注解通常是通过反射机制去检查被注解的类、方法等是否满足要求,比如@Override就是检查被注解的方法是否是重写父类的方法,如果不是就会编译报错。