1.2 注解和反射--反射

138 阅读13分钟

一、Java反射机制概述

1.1 静态 VS 动态语言

动态语言:

  • 一类在运行时可以改变其结构的语言,比如新的函数、对象等,已有的函数可以被删除或者其他结构的变化。

  • 主要的动态语言:Object-CC#JavaScriptPHPPython

静态语言:

  • 运行时结构不变的语言,如JavaCC++

  • Java有一定的动态性,利用反射机制获得类似于动态语言的特性

1.2 Reflection

Reflection反射是Java被视为动态语言的关键,反射机制允许程序在执行期借助Reflection API获取任何类的内部信息,并能直接操作任何对象的内部属性以及方法。

image.png

  • 加载完类之后,在堆内存的方法区中就产生一个Class类型对象(一个类只有一个Class对象),这个对象就包含完整的类结构信息。我们通过这个对象看到类的结构,这个对象就像镜子一样,所以称之为反射。

image.png

1.3 反射机制的应用

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

1.4 反射的优缺点

优点:

  • 可以实现动态创建对象和编译,体现出很大的灵活性

缺点:

  • 对性能有影响,使用反射基本上是一种解释操作,告诉JVM希望做什么并且满足要求,这类操作慢于直接执行相同操作

1.5 反射的主要API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java,lang.reflect.Constructor:代表类的构造器

二、理解Class类并获取Class示例

2.1 Class类

Object类中定义了一个getClass()方法,此方法被所有子类继承

  • getClass返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射可以通过对象反射求出类的名称
  • Class本身也是一个类
  • Class对象只能有系统建立
  • 一个加载的类在JVM中只有一个Class实例
  • 一个Class对象对应一个加载到JVM中的一个.class文件
  • 每个类的实例都会记得自己是由哪个Class实例所生成
  • 通过Class可以完整得到一个类中所有被加载的结构
  • Class类是Reflection根源,针对任何想动态加载、运行的类,唯有先获得Class对象

2.2 Class类的常用方法

image.png

2.3 如何获取Class类的实例

image.png

实例:

 /**
  * @ClassName com.nick.Reflection1
  * @Description 什么叫反射
  * @Author wangwk-a
  * @Date 2022/1/4 21:30
  * @Version 1.0
  */
 public class Reflection1 {
     public static void main(String[] args) throws ClassNotFoundException {
         Person person = new Student();
         System.out.println(person.name);
 ​
         /* TODO: 方式一(通过对象获取Class对象) */
         Class c1 = person.getClass();
 ​
         /* TODO: 方式二(通过Class.forName获取Class对象) */
         Class c2 = Class.forName("com.nick.Student");
 ​
         /* TODO: 方式三(通过类名.class获取Class对象) */
         Class c3 = Student.class;
 ​
         /* TODO: 方式四(通过基本内置类型的包装类TYPE属性获取Class对象) */
         Class<Integer> c4 = Integer.TYPE;
         System.out.println(c4);
 ​
         /* TODO: 方式五(通过Class对象获取父类的Class对象) */
         Class c5 = c1.getSuperclass();
         System.out.println(c1 + "的父类是" + c5);
 ​
         // 一个类在内存中有一个Class对象
         System.out.println(c1.hashCode());
         System.out.println(c2.hashCode());
         System.out.println(c3.hashCode());
     }
 }
 ​
 @Data
 @AllArgsConstructor
 @NoArgsConstructor
 @ToString
 class Person {
     public String name;
 }
 ​
 @Data
 @ToString
 class Student extends Person{
     public Student() {
         this.name = "学生";
     }
 }
 ​
 @Data
 @ToString
 class Teacher extends Person{
     public Teacher() {
         this.name = "老师";
     }
 }
 学生
 int
 class com.nick.Student的父类是class com.nick.Person
 789451787
 789451787
 789451787

2.4 哪些类型可以有Class对象

  • class:外部类,成员(成员内部类、静态内部类)、局部内部类、匿名内部类
  • interface:接口
  • []:数组
  • enum:枚举
  • annotation:注解
  • primitive type:基本数据类型
  • void

实例:

 /**
  * @ClassName Reflection2
  * @Description 所有类型的Class对象
  * @Author wangwk-a
  * @Date 2022/1/4 22:10
  * @Version 1.0
  */
 public class Reflection2 {
     public static void main(String[] args) {
         // class
         Class c1 = Object.class;
         // interface
         Class c2 = Runnable.class;
         // 数组
         Class c3 = String[].class;
         Class c4 = String[][].class;
         // 注解
         Class c5 = Override.class;
         // 枚举
         Class c6 = ElementType.class;
         // 基本数据类型
         Class c7 = Integer.class;
         // 空
         Class c8 = void.class;
         // Class
         Class c9 = Class.class;
 ​
         System.out.println(c1);
         System.out.println(c2);
         System.out.println(c3);
         System.out.println(c4);
         System.out.println(c5);
         System.out.println(c6);
         System.out.println(c7);
         System.out.println(c8);
         System.out.println(c9);
     }
 }
 class java.lang.Object
 interface java.lang.Runnable
 class [Ljava.lang.String;
 class [[Ljava.lang.String;
 interface java.lang.Override
 class java.lang.annotation.ElementType
 class java.lang.Integer
 void
 class java.lang.Class

三、类的加载与ClassLoader

3.1 Java内存分析

image.png

3.2 类加载过程

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

image.png

1. 加载

class字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class

2.链接:将Java类的二进制代码合并到JRE中

  • 验证:确保加载的类信息符合JVM规范,没有安全问题
  • 准备:正式为类变量(static)分配内存并设置变量默认初始值的阶段,这些内存在方法区中进行分配(所以static在类初始化之前就做了
  • 解析:JVM常量池内的符号引用(常量名)替换为直接引用(地址)的过程

3.初始化

  • 执行类构造器<clinit>()方法的过程,其是由编译期自动收集类中所有变量的赋值动作和静态代码快中的语句合并(所有的static会合并到一起)产生。(类构造器是构造类信息的,不是构造类对象的构造器)
  • 当初始化一个类的时候,如果发现其父类还没有初始化,则需要先触发父类的初始化
  • JVM会保证一个类的构造器方法在多线程环境安全

示例:

 package com.nick;
 ​
 /**
  * @ClassName Reflection3
  * @Description 分析类加载过程
  * @Author wangwk-a
  * @Date 2022/1/4 22:34
  * @Version 1.0
  */
 public class Reflection3 {
     public static void main(String[] args) {
         A a = new A();
         System.out.println(A.m);
     }
 }
 
 class A {
     static {
         System.out.println("A类静态代码快初始化");
         m = 300;
     }
     static int m = 100;
     public A() {
         System.out.println("A类的无参构造方法");
     }
 }
 A类静态代码快初始化
 A类的无参构造方法
 100

执行过程示意图:

image.png

3.3 什么时候会发生类的初始化

  • 主动引用(一定会发生类的初始化

    • 当JVM启动,先初始化mina方法所在的类
    • new一个类的对象
    • 调用类静态成员和静态方法
    • 使用java.lang.reflect包方法对类进行反射调用
    • 初始化一个类,如果父类没有被初始化,则先会调用父类初始化
  • 被动引用(不会发生类的初始化)

    • 访问静态域时,只有真正生命这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
    • 通过数组定义类引用,不会触发此类初始化
    • 引用常量不会触发初始化
 package com.nick;
 ​
 /**
  * @ClassName Reflection4
  * @Description 类的主动引用--一定会调用初始化
  * @Author wangwk-a
  * @Date 2022/1/8 20:47
  * @Version 1.0
  */
 public class Reflection4 {
     static {
         System.out.println("Main类被加载");
     }
 ​
     public static void main(String[] args) throws ClassNotFoundException {
         /**
          * case 1 : 主动引用
          * result:
          * Main类被加载
          * 父类被加载
          * 子类被加载
          */
         // Son son = new Son();
 ​
         /**
          * case 2 : 反射也会产生初始化
          * result:
          * Main类被加载
          * 父类被加载
          * 子类被加载
          */
          // Class.forName("com.nick.Son");
 ​
         /**
          * case 3 : 子类访问父类的静态变量,子类不会被加载
          * result:
          * Main类被加载
          * 父类被加载
          * 2
          */
         // System.out.println(Son.b);
 ​
         /**
          * case 4 : 数组只是一个空间和空间的名字
          * result:
          * Main类被加载
          */
         // Son[] array = new Son[5];
 ​
         /**
          * case 5 : 调用常量池也不会初始化,在连接的时候赋值
          * result:
          * Main类被加载
          * 1
          */
         // System.out.println(Son.M);
     }
 }
 ​
 class Father {
     static int b = 2;
     static {
         System.out.println("父类被加载");
     }
 }
 ​
 class Son extends Father {
     static int m = 100;
     static {
         System.out.println("子类被加载");
         m = 300;
     }
     static final int M = 1;
 }

3.4 类加载器的作用

image.png

类加载器的作用

将class文件字节码内容加载到内存中,并将这些静态数据转化成方法区运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。

类缓存

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

image.png

类加载器作用是用来把类class装载进内存,JVM规范定义如下类加载器:

  • 引导类加载器(Bootstap Classloader):用c++编写,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库,该加载器无法直接获取

image.png

  • 扩展类加载器(Extension Classloader):负责jre/lib/ext目录下的jar包或者-D java.ext.dirs指定目录下的jar包装入工作库

image.png

  • 系统类加载器(System Classloader):负责java -classpath-D java.class.path所指目录下的类与jar包装入工作,是最常用的加载器
 package com.nick;
 ​
 /**
  * @ClassName Reflection5
  * @Description 获取类加载器
  * @Author wangwk-a
  * @Date 2022/1/8 21:10
  * @Version 1.0
  */
 public class Reflection5 {
     public static void main(String[] args) throws ClassNotFoundException {
         // 获取系统类加载器
         ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
         System.out.println(systemClassLoader);
 ​
         // 获取扩展类加载器
         ClassLoader parent = systemClassLoader.getParent();
         System.out.println(parent);
         
         // 获取引导类加载器
         ClassLoader bootstap = parent.getParent();
         System.out.println(bootstap);
         
         // 测试当前类是哪个加载器
         ClassLoader classLoader = Class.forName("com.nick.Reflection5").getClassLoader();
         System.out.println(classLoader);
 ​
         // 测试JDK类加载器
         ClassLoader classLoader2 = Class.forName("java.lang.Object").getClassLoader();
         System.out.println(classLoader2);
 ​
         /**
          * 获取系统类加载器可以加载的路径
          * result:
          * C:\JDK8\jre\lib\charsets.jar;
          * C:\JDK8\jre\lib\deploy.jar;
          * C:\JDK8\jre\lib\ext\access-bridge-64.jar;
          * C:\JDK8\jre\lib\ext\cldrdata.jar;
          * C:\JDK8\jre\lib\ext\dnsns.jar;
          * C:\JDK8\jre\lib\ext\jaccess.jar;
          * C:\JDK8\jre\lib\ext\jfxrt.jar;
          * C:\JDK8\jre\lib\ext\localedata.jar;
          * C:\JDK8\jre\lib\ext\nashorn.jar;
          * C:\JDK8\jre\lib\ext\sunec.jar;
          * C:\JDK8\jre\lib\ext\sunjce_provider.jar;
          * C:\JDK8\jre\lib\ext\sunmscapi.jar;
          * C:\JDK8\jre\lib\ext\sunpkcs11.jar;
          * C:\JDK8\jre\lib\ext\zipfs.jar;
          * C:\JDK8\jre\lib\javaws.jar;
          * C:\JDK8\jre\lib\jce.jar;
          * C:\JDK8\jre\lib\jfr.jar;
          * C:\JDK8\jre\lib\jfxswt.jar;
          * C:\JDK8\jre\lib\jsse.jar;
          * C:\JDK8\jre\lib\management-agent.jar;
          * C:\JDK8\jre\lib\plugin.jar;
          * C:\JDK8\jre\lib\resources.jar;
          * C:\JDK8\jre\lib\rt.jar;
          * D:\SpringLearning\Spring\ReflectionDemo\target\classes;
          * C:\Users\wangwk-a.m2\repository\org\springframework\spring-webmvc\5.3.13\spring-webmvc-5.3.13.jar;
          * C:\Users\wangwk-a.m2\repository\org\springframework\spring-aop\5.3.13\spring-aop-5.3.13.jar;
          * C:\Users\wangwk-a.m2\repository\org\springframework\spring-beans\5.3.13\spring-beans-5.3.13.jar;
          * C:\Users\wangwk-a.m2\repository\org\springframework\spring-context\5.3.13\spring-context-5.3.13.jar;
          * C:\Users\wangwk-a.m2\repository\org\springframework\spring-core\5.3.13\spring-core-5.3.13.jar;
          * C:\Users\wangwk-a.m2\repository\org\springframework\spring-jcl\5.3.13\spring-jcl-5.3.13.jar;
          * C:\Users\wangwk-a.m2\repository\org\springframework\spring-expression\5.3.13\spring-expression-5.3.13.jar;
          * C:\Users\wangwk-a.m2\repository\org\springframework\spring-web\5.3.13\spring-web-5.3.13.jar;
          * C:\Users\wangwk-a.m2\repository\mysql\mysql-connector-java\8.0.27\mysql-connector-java-8.0.27.jar;
          * C:\Users\wangwk-a.m2\repository\com\google\protobuf\protobuf-java\3.11.4\protobuf-java-3.11.4.jar;
          * C:\Users\wangwk-a.m2\repository\org\springframework\spring-jdbc\5.3.13\spring-jdbc-5.3.13.jar;
          * C:\Users\wangwk-a.m2\repository\org\springframework\spring-tx\5.3.13\spring-tx-5.3.13.jar;
          * C:\Users\wangwk-a.m2\repository\org\mybatis\mybatis\3.5.7\mybatis-3.5.7.jar;
          * C:\Users\wangwk-a.m2\repository\org\mybatis\mybatis-spring\2.0.6\mybatis-spring-2.0.6.jar;
          * C:\Users\wangwk-a.m2\repository\org\junit\jupiter\junit-jupiter-api\5.8.1\junit-jupiter-api-5.8.1.jar;
          * C:\Users\wangwk-a.m2\repository\org\opentest4j\opentest4j\1.2.0\opentest4j-1.2.0.jar;
          * C:\Users\wangwk-a.m2\repository\org\junit\platform\junit-platform-commons\1.8.1\junit-platform-commons-1.8.1.jar;
          * C:\Users\wangwk-a.m2\repository\org\apiguardian\apiguardian-api\1.1.2\apiguardian-api-1.1.2.jar;
          * C:\Users\wangwk-a.m2\repository\org\aspectj\aspectjweaver\1.9.7\aspectjweaver-1.9.7.jar;E:\IDEA\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar
          */
         System.out.println(System.getProperty("java.class.path"));
 ​
     }
 }
 sun.misc.Launcher$AppClassLoader@18b4aac2
 sun.misc.Launcher$ExtClassLoader@2f0e140b
 null
 sun.misc.Launcher$AppClassLoader@18b4aac2
 null
 C:\JDK8\jre\lib\charsets.jar;D:\SpringLearning\Spring\ReflectionDemo\target\classes; // 太长了这里省略

双亲委派机制

找包的时候会从下往上找(system->extension->bootstap),如果前后都找到了,前面的就无效了。例如自己写一个java.lang.String,该类并不会被加载。

四、创建运行时类对象

4.1 获取运行时类的完整结构

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

FieldMethodConstructorSuperclassinterfaceAnnotation

image.png

package com.nick;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @ClassName Reflection6
 * @Description 获取类的信息
 * @Author wangwk-a
 * @Date 2022/1/8 21:32
 * @Version 1.0
 */
public class Reflection6 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1 = Class.forName("com.nick.Teacher");

        // 获得类的名字
        System.out.println(c1.getName());
        System.out.println(c1.getSimpleName());

        /**
         * 获得类的属性
         * getFields 获取public属性(包括继承来的public属性)
         * getDeclaredFields 只能获取在类中声明的属性
         */
        Field[] publicFiles = c1.getFields();
        Arrays.stream(publicFiles).forEach(f-> System.out.println(f));
        System.out.println("-------------------------------------");
        Field[] allFields = c1.getDeclaredFields();
        Arrays.stream(allFields).forEach(f-> System.out.println(f));
        System.out.println("-------------------------------------");
        Field name = c1.getField("name");
        System.out.println(name);

        /**
         * 获得类的方法
         * getMethods  获得本类所有方法
         * getDeclaredMethods 获得本类声明的方法
         */
        System.out.println("========================================\n\n");
        Method[] methods = c1.getMethods();
        Arrays.stream(methods).forEach(m-> System.out.println(m));
        System.out.println("-------------------------------------");
        Method[] declaredMethods = c1.getDeclaredMethods();
        Arrays.stream(declaredMethods).forEach(m-> System.out.println(m));
        System.out.println("-------------------------------------");
        Method getName = c1.getMethod("getName", null);
        Method setName = c1.getMethod("setName", String.class);
        System.out.println(getName);
        System.out.println(setName);

        /**
         * 获得类的构造器
         */
        System.out.println("========================================\n\n");
        Constructor[] constructors = c1.getConstructors();
        Arrays.stream(constructors).forEach(c-> System.out.println(c));
        System.out.println("-------------------------------------");
        Constructor[] declaredConstructors = c1.getDeclaredConstructors();
        Arrays.stream(declaredConstructors).forEach(c-> System.out.println(c));
        System.out.println("-------------------------------------");
        Constructor constructor = c1.getDeclaredConstructor(int.class, String.class);
        System.out.println(constructor);
    }
}
com.nick.Teacher
Teacher
public java.lang.String com.nick.Teacher.gender
public java.lang.String com.nick.Person.name
-------------------------------------
private int com.nick.Teacher.id
public java.lang.String com.nick.Teacher.gender
-------------------------------------
public java.lang.String com.nick.Person.name
========================================


public boolean com.nick.Teacher.equals(java.lang.Object)
public java.lang.String com.nick.Teacher.toString()
public int com.nick.Teacher.hashCode()
public int com.nick.Teacher.getId()
public java.lang.String com.nick.Teacher.getGender()
public void com.nick.Teacher.setGender(java.lang.String)
public void com.nick.Teacher.setId(int)
public java.lang.String com.nick.Person.getName()
public void com.nick.Person.setName(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
-------------------------------------
public boolean com.nick.Teacher.equals(java.lang.Object)
public java.lang.String com.nick.Teacher.toString()
public int com.nick.Teacher.hashCode()
public int com.nick.Teacher.getId()
public java.lang.String com.nick.Teacher.getGender()
public void com.nick.Teacher.setGender(java.lang.String)
public void com.nick.Teacher.setId(int)
protected boolean com.nick.Teacher.canEqual(java.lang.Object)
-------------------------------------
public java.lang.String com.nick.Person.getName()
public void com.nick.Person.setName(java.lang.String)
========================================


public com.nick.Teacher(int,java.lang.String)
public com.nick.Teacher()
-------------------------------------
public com.nick.Teacher(int,java.lang.String)
public com.nick.Teacher()
-------------------------------------
public com.nick.Teacher(int,java.lang.String)

4.2 有了Class对象,能做什么

  • 创建类对象
    • 调用Class对象的newInstance方法
    • 类必须有一个无参构造器
    • 类构造器的访问权限需要足够

难道没有无参构造器就不能创建对象了?只要在操作的时候明确调用类中的构造器,并将参数传递进去之后,才可以实例化操作。

步骤如下:

  • 通过Class类的getDeclaredConstructor(Class ...paramTypes)取的本类指定形参的构造器
  • 向构造器形参中传递一个对象进去,里面包含构造器中所需各个参数
  • 通过Constructor实例化对象

image.png

package com.nick;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @ClassName Reflection7
 * @Description 动态创建对象
 * @Author wangwk-a
 * @Date 2022/1/8 22:07
 * @Version 1.0
 */
public class Reflection7 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        Class c1 = Class.forName("com.nick.Teacher");

        // 本质上是调用了无参构造器
        Teacher teacher = (Teacher) c1.newInstance();
        System.out.println(teacher);
        
        // 使用有参构造器
        Constructor constructor = c1.getConstructor(int.class, String.class);
        Teacher teacher2 = (Teacher) constructor.newInstance(5, "男");
        System.out.println(teacher2);

        // 通过反射调用方法 invoke(对象,值)
        Method setGender = c1.getDeclaredMethod("setGender", String.class);
        setGender.invoke(teacher2, "女");
        System.out.println(teacher2);

        // 通过反射操作属性 setAccessible取消安全检测
        Field id = c1.getDeclaredField("id");
        id.setAccessible(true);
        id.set(teacher2, 1000);
        System.out.println(teacher2);
    }
}
Teacher{name='老师', id=0, gender='null'}
Teacher{name='老师', id=5, gender='男'}
Teacher{name='老师', id=5, gender='女'}
Teacher{name='老师', id=1000, gender='女'}

4.3 性能分析

package com.nick;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @ClassName Reflection8
 * @Description 性能分析
 * @Author wangwk-a
 * @Date 2022/1/9 19:23
 * @Version 1.0
 */
public class Reflection8 {

    public static final int TIMES = 1000000000;

    /**
     * 普通方式调用
     */
    public static void test01() {
        Teacher teacher = new Teacher();

        long startTime = System.currentTimeMillis();
        for (int i = 0; i < TIMES; i++) {
            teacher.getName();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("普通方式执行十亿次需要:" + (endTime - startTime) + "ms");
    }

    /**
     * 反射
     */
    public static void test02() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Class c1 = Class.forName("com.nick.Teacher");
        Teacher t = (Teacher) c1.newInstance();
        Method getName = c1.getMethod("getName", null);

        long startTime = System.currentTimeMillis();
        for (int i = 0; i < TIMES; i++) {
            getName.invoke(t);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射方式执行十亿次需要:" + (endTime - startTime) + "ms");
    }

    /**
     * 反射关闭权限检查
     */
    public static void test03() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Class c1 = Class.forName("com.nick.Teacher");
        Teacher t = (Teacher) c1.newInstance();
        Method getName = c1.getMethod("getName", null);
        getName.setAccessible(true);

        long startTime = System.currentTimeMillis();
        for (int i = 0; i < TIMES; i++) {
            getName.invoke(t);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射(关闭权限检测)方式执行十亿次需要:" + (endTime - startTime) + "ms");
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        test01();
        test02();
        test03();
    }
}
普通方式执行十亿次需要:3ms
反射方式执行十亿次需要:1737ms
反射(关闭权限检测)方式执行十亿次需要:1186ms

4.4 反射操作泛型

Java采用泛型擦除机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除

为了通过反射操作泛型,Java新增了ParameterizedTypeGenericArrayTypeTypeVariableWildcardType几种类型来代表不能被归一到Class类中的类型,但是又和原始类型齐名的类型。

  • ParameterizedType:表示一种参数化类型,比如Collection<String>
  • GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
  • TypeVariable:是各种类型变量的公共父接口
  • WildcardType:代表一种通配符类型表达式

image.png

package com.nick;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @ClassName Reflection9
 * @Description 反射操作泛型
 * @Author wangwk-a
 * @Date 2022/1/9 19:38
 * @Version 1.0
 */
public class Reflection9 {
    public void test01(Map<String, Teacher> map, List<Teacher> list) {
        System.out.println("test01");
    }

    public Map<String, Teacher> test02() {
        System.out.println("test02");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
        // 获取泛型参数类型
        Method method1 = Reflection9.class.getMethod("test01", Map.class, List.class);
        Type[] genericParameterTypes = method1.getGenericParameterTypes();
        Arrays.stream(genericParameterTypes).forEach(g-> {
            System.out.println(g);
            // 判断泛型的类型是不是一个参数化类型
            if (g instanceof ParameterizedType) {
                Type[] actualTypeArguments = ((ParameterizedType) g).getActualTypeArguments();
                Arrays.stream(actualTypeArguments).forEach(a-> System.out.println("-->  " + a));
            }
        });

        // 获取方法返回值
        // 获取泛型参数类型
        System.out.println("\n------------------------------\n");
        Method method2 = Reflection9.class.getMethod("test02", null);
        Type genericReturnType = method2.getGenericReturnType();
        System.out.println(genericReturnType);
        if (genericReturnType instanceof ParameterizedType) {
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            Arrays.stream(actualTypeArguments).forEach(a-> System.out.println("-->  " + a));
        }
    }
}
java.util.Map<java.lang.String, com.nick.Teacher>
-->  class java.lang.String
-->  class com.nick.Teacher
java.util.List<com.nick.Teacher>
-->  class com.nick.Teacher

------------------------------

java.util.Map<java.lang.String, com.nick.Teacher>
-->  class java.lang.String
-->  class com.nick.Teacher

4.5 反射操作注解

练习ORM

  • ORM(Object Relationship Mapping)对象关系映射
  • 类和表结构对应
  • 属性和字段对应
  • 对象和记录对应

要求:利用注解和反射完成类和表结构的映射关系

package com.nick;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.util.Arrays;

/**
 * @ClassName Reflection10
 * @Description 反射操作注解 ORM
 * @Author wangwk-a
 * @Date 2022/1/9 19:56
 * @Version 1.0
 */
public class Reflection10 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("com.nick.Student2");

        // 通过反射获得注解
        Annotation[] annotations = c1.getAnnotations();
        Arrays.stream(annotations).forEach(a->System.out.println(a));

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

        // 获得属性上的注解
        Field name = c1.getDeclaredField("name");
        FieldName annotation = name.getAnnotation(FieldName.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());

    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@TableName("db_student")
class Student2 {
    @FieldName(columnName = "db_id", type = "int", length = 10)
    private int id;
    @FieldName(columnName = "db_age", type = "int", length = 10)
    private int age;
    @FieldName(columnName = "db_name", type = "varchar", length = 3)
    private String name;
}

/**
 * 类名的注解
 * @author wangwk-a
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableName {
    String value();
}

/**
 * 属性的注解
 * @author wangwk-a
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldName {
    String columnName();
    String type();
    int length();
}
@com.nick.TableName(value=db_student)
db_student
db_name
varchar
3