注解和反射 注解 反射机制(Java.Reflection)

Annotation 是从JDK5.0开始引入的技术。

内置注解(在java.lang包中)

  • @Override,方法重写。
  • @Deprecated(已被淘汰),可用于修饰类、属性、方法。不推荐程序员使用这样的元素,因为它很危险或者已经存在更好的选择。
  • @SuppressWarnings:抑制编译时的警告信息,需要添加参数才能正常使用。比如:@SuppressWarnings("all")

元注解

负责注解其他注解,java定义了4个标准的meta-annotation类型。

  • @Target,描述注解的使用范围
  • @Retention,描述注解的生命周期
    • SOURCE < CLASS < RUNTIME
  • @Document,表示该注解将被包含在 javadoc中
  • Inherited,说明子类可以继承父类中的该注解

自定义注解

@interface + 注解名{ 参数类型 + 参数名(); }

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface DIY {
    String name() default "";
    int age();
}

反射机制(Java.Reflection)

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

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

类加载完之后,在堆内存的方法区中就产生了一个Class类对象(Class类是用来描述类的类,一个类只有一个Class类对象),这个Class类对象包含了完整的类的结构信息。

反射的功能

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

反射优点:

  • 可以动态创建对象,灵活性很强

缺:

  • 对性能有影响,执行速度比较慢

反射相关的主要API

API Description
java.lang.Class 代表一个类
java.lang.reflect.Method 代表类的方法
java.lang.reflect.Field 代表类的属性
java.lang.reflect.Constructor 代表类的构造器

获取Class对象的方式

        //1.通过全限定类名获得
        Class<?> c1 = Class.forName("reflect.User");
        //2.通过对象获得
        Class c2 = new User().getClass();
        //3.通过类名.class获得
        Class c3 = User.class;

所有类型的class

//      所有类型的class:
        Class<Object> objectClass = Object.class;
        Class<Comparable> comparableClass = Comparable.class;//接口
        Class<String[]> aClass = String[].class;
        Class<String[][]> aClass1 = String[][].class;
        Class<Override> overrideClass = Override.class;//注解
        Class<ElementType> elementTypeClass = ElementType.class;//枚举
        Class<Integer> integerClass = Integer.class;
        Class<Void> voidClass = void.class;
        Class<Class> classClass = Class.class;
        Class<Double> doubleClass = Double.class;
        Class<Float> floatClass = Float.class;
        Class<Boolean> booleanClass = Boolean.class;

注解和反射
注解
反射机制(Java.Reflection)

java内存分析

  • 堆:
    • 存放new的对象和数组
    • 可以被所有线程共享,不会存放别的对象的引用
    • 存放基本数据类型
    • 存放引用对象的地址(引用在堆里面的具体地址)
  • 方法区(是一个特殊的堆)
    • 包含了所有的class和static变量
    • 可以被所有线程共享

类加载的过程

当程序主动使用某个类时,如果该类还没有被加载到内存中,系统就会通过以下三个步骤对该类进行初始化。

  1. 类的加载
    • 将类的class字节码文件加载到内存中,把静态数据转换成方法区的运行时数据,然后创建Class对象。这个过程由类加载器完成。
  2. 类的链接
    • 将类的二进制数据合并到jre中
      • 验证:确保加载的类符合JVM规范,不存在安全问题
      • 准备:为类变量分配内存并设置初始值,这些内存都在方法区中分配
      • 解析:虚拟机常量池内的常量名,替换为地址。
  3. 类的初始化
    • JVM负责对类进行初始化
      • 执行类构造器<clinit>()方法,负责构造类信息,不是构造该类的构造器
      • 初始化一个类的时候,如果发现父类还没有进行初始化,会先对父类进行初始化
      • JVM会保证一个类的<clinit>()方法在多线程环境中被正确加锁同步

类加载器

类加载器的作用:把类的class字节码文件加载到内存中,并且把静态数据转换成方法区的运行时数据,然后创建Class对象,作为方法区中类数据的访问入口。

  1. 类引导加载器:
    • C++编写的,JVM自带的,负责装载java平台核心库(rt .jar)。该加载器无法直接获取。
  2. 类扩展加载器:
    • 负责装载jre/lib。。下的jar包。
  3. 系统类加载器:
    • 负责java.class.path下的类和jar包的加载,是最常用的加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
    
        ClassLoader parent = systemClassLoader.getParent();
    
        ClassLoader parent1 = parent.getParent();

注解和反射
注解
反射机制(Java.Reflection)

获取当前类加载器:是最常用的系统类加载器

        //获取当前类的类加载器
        ClassLoader classLoader = Class.forName("reflect.Reflect").getClassLoader();
        System.out.println(classLoader);	

注解和反射
注解
反射机制(Java.Reflection)

获取类名:

        //获得类的名字
        System.out.println(aClass);//class + 包名+类名
        System.out.println(aClass.getName());//包名+类名
        System.out.println(aClass.getSimpleName());//类名

获取属性:

        //获得类的属性
        System.out.println("============");
        //getFields()只能获取public属性
        Field[] field = aClass.getFields();
        for (Field pub:field) {
            System.out.println(pub);
        }
        //getDeclaredFields()获取所有的属性
        System.out.println("============");
        Field[] fields = aClass.getDeclaredFields();
        for (Field f:fields) {
            System.out.println(f);
        }

获取方法:

        //获取该类的public方法和父类的public方法
        Method[] methods = aClass.getMethods();
        //获取该类的所有方法
        Method[] declaredMethods = aClass.getDeclaredMethods();

获取构造器:

        //获取该类的public构造器
        Constructor<?>[] constructors = aClass.getConstructors();

        //获取该类的所有构造器
        Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();

通过反射动态创建对象

有了Class类对象后,可以调用Class类对象的newInstance()方法,创建对象

  • 类必须有一个无参构造器
  • 类的构造器有足够的访问权限
        //通过无参构造创建对象
        Object o = aClass.newInstance();

        //通过构造器创建对象
        Constructor c = aClass.getDeclaredConstructor(String.class,int.class);
        Object newInstance = c.newInstance("柯柯", 18);

通过反射操作方法:

        //通过反射操作方法
        Method setSss = aClass.getDeclaredMethod("setSss", String.class);
        setSss.invoke(newInstance,"kkkkkk");//传入对象 + 参数值
        System.out.println(newInstance);

通过反射操作属性:

        //可以直接操作类的public属性
        Field sss = aClass.getDeclaredField("sss");
        sss.set(newInstance,"qqqqq");
        System.out.println(newInstance);

        //不能直接操作类的私有属性,需要关闭安全检测
        Field name = aClass.getDeclaredField("name");
        //设置为可进入的,可使用的,默认为false
        name.setAccessible(true);
        name.set(newInstance,"www");
        System.out.println(newInstance);

通过反射获取注解:

        Class<?> aClass = Class.forName("reflect.User");

//        通过过反射获取注解
        Annotation[] annotations = aClass.getDeclaredAnnotations();
        for (Annotation annotation: annotations) {
            System.out.println(annotation);
        }
//        通过过反射获取注解的值
        DIY diy = aClass.getDeclaredAnnotation(DIY.class);
        System.out.println(diy.value());
//        获得属性的注解
        Field name = aClass.getDeclaredField("name");
        DIYField diyField = name.getAnnotation(DIYField.class);
        System.out.println(diyField.value());

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface DIY{
    String value();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface DIYField{
    String value();
}