Skip to content

反射机制

反射机制概述

Java反射机制是指在运行时动态获取类的信息或动态调用对象的方法、修改属性等操作。主要核心就是Class类、Constructor类、Field类、Method类等API。反射机制主要应用于框架开发、动态代理、ORM框架、JDBC驱动等方面。通过反射机制,程序员能够获得在编译期间不被知晓的类、属性、方法等信息。但是反射机制的性能较低,常常被认为是一种牺牲性能换取灵活性的实现方式。

Java反射机制核心包:java.lang.reflect.*

Java反射机制核心类:

  • java.lang.Class
  • java.lang.reflect.Field
  • java.lang.reflect.Method
  • java.lang.reflect.Constructor
  • java.lang.reflect.Modifier

获取Class

Java中获取Class对象有以下三种方式:

  1. 调用Object类的getClass()方法

可以通过对象的getClass()方法来获取Class对象,例如:

Object obj = new Object();

Class clazz = obj.getClass();

  1. 使用“类.class”语法

可以使用“类.class”语法来获取Class对象,例如:

Class clazz = Object.class;

  1. 使用Class类的forName()方法

可以使用Class类的forName()方法来获取Class对象,例如:

Class clazz = Class.forName("java.lang.Object");

反射作用的体现

在属性配置文件中配置类名:classInfo.properties

className=java.util.Date

通过IO流读取属性配置文件,获取类名,再通过反射机制实例化对象。

如果要创建其他类的实例对象,只需要修改classInfo.properties配置文件即可。

这说明反射机制可以让程序变的更加灵活。在进行系统扩展时,可以达到OCP开闭原则。

反射Field

反射Field包括两方面:

一方面:通过反射机制获取Field

Field field = clazz.getDeclaredField("fieldName"); // 通过属性名获取

Field[] fields = clazz.getDeclaredFields(); // 获取所有的属性,包括私有的

另一方面:通过Filed访问对象的属性

Object fieldValue = field.get(myObject); // 读取某个对象的属性值

field.set(myObject, newValue); // 修改某个对象的属性值

反射Method

反射Method包括两方面:

一方面:通过反射机制获取Method

Method method = clazz.getDeclaredMethod("methodName", paramTypes);

另一方面:通过Method调用方法

Class clazz = MyClass.class;

Method method = clazz.getDeclaredMethod("methodName", paramTypes);

Object[] args = {arg1, arg2, arg3};

Object result = method.invoke(myObject, args);

反射Constructor

反射Constructor包括两方面:

一方面:通过反射机制获取Constructor

Constructor constructor2 = clazz.getDeclaredConstructor(paramTypes);

另一方面:通过Constructor创建对象

Class clazz = MyClass.class;

Constructor constructor = clazz.getDeclaredConstructor(paramTypes);

Object[] args = {arg1, arg2, arg3};

Object myObject = constructor.newInstance(args);

模拟框架的部分实现

  1. 配置文件中配置如下信息:classInfo.properties

className=com.powernode.javase.reflect.UserService

methodName=login

parameterTypes=java.lang.String,java.lang.String

parameterValues=admin,123456

  1. 通过反射机制创建对象,调用配置的方法

  2. 本案例的实现类似于Spring框架中部分实现,主要是通过修改配置文件来达到创建不同的对象,调用不同的方法。

类加载及双亲委派机制

类加载的过程

  • 装载(loading)
    • 类加载器负责将类的class文件读入内存,并创建一个java.lang.Class对象
  • 连接(linking)
    • 验证(Verify)
      • 确保加载类的信息符合JVM规范。
    • 准备(Prepare)
      • 正式为静态变量在方法区中开辟存储空间并设置默认值
      • public static int k = 10; 此时:k会赋值0
      • public static final int f = 10; 此时: f会赋值10
    • 解析(Resolve)
      • 将虚拟机常量池内的符号引用替换为直接引用(地址)的过程。
  • 初始化(initialization)
    • 静态变量赋值,静态代码块执行
    低版本的JDK中类加载器的名字:
      启动类加载器:负责加载rt.jar
      扩展类加载器:ext/*.jar
      系统类加载器:classpath
    

获取Class的四种方式

  • 静态方法
  • Class clazz = Class.forName(“全限定类名”)

  • 实例方法
  • Class clazz = 引用.getClass();

  • class属性
  • Class clazz = 类型名.class;

  • 通过类加载器获取
  • ClassLoader classLoader = ClassLoader.getSystemClassLoader();

    Class clazz = classLoader.loadClass(“全限定类名”);

    Class.forName和classLoader.loadClass()的区别?
          Class.forName():类加载时会进行初始化。
          classLoader.loadClass():类加载时不会进行初始化,直到第一次使用该类。
    

类加载器

  • 虚拟机内部提供了三种类加载器(Java9+):
    • 启动类加载器(BootstrapClassLoader):加载Java最核心的类,例如String

    • 平台类加载器(PlatformClassLoader):加载Java平台扩展的类库,例如解析XML的

    • 应用类加载器(AppClassLoader):加载classpath中的

    • 同时我们还可以自定义一个类加载器(UserClassLoader)

    • 获取类加载器可以通过 getParent()方法一级一级获取

双亲委派机制

  • 某个类加载器接收到加载类的任务时,通常委托给“父 类加载”完成加载。
  • 最“父 类加载器”无法加载时,一级一级向下委托加载任务。
  • 作用:
    • 保护程序的安全。
    • 防止类加载重复。

反射泛型(了解)

  1. 反射父类的泛型
  2. 反射接口的泛型
  3. 反射属性上的泛型
  4. 反射方法参数上的泛型
  5. 反射方法返回值的泛型
  6. 反射构造方法参数上的泛型