Appearance
反射机制
反射机制概述
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对象有以下三种方式:
- 调用Object类的getClass()方法
可以通过对象的getClass()方法来获取Class对象,例如:
Object obj = new Object();
Class clazz = obj.getClass();
- 使用“类.class”语法
可以使用“类.class”语法来获取Class对象,例如:
Class clazz = Object.class;
- 使用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);
模拟框架的部分实现
- 配置文件中配置如下信息:classInfo.properties
className=com.powernode.javase.reflect.UserService
methodName=login
parameterTypes=java.lang.String,java.lang.String
parameterValues=admin,123456
通过反射机制创建对象,调用配置的方法
本案例的实现类似于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属性
- 通过类加载器获取
Class clazz = Class.forName(“全限定类名”)
Class clazz = 引用.getClass();
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()方法一级一级获取
双亲委派机制
- 某个类加载器接收到加载类的任务时,通常委托给“父 类加载”完成加载。
- 最“父 类加载器”无法加载时,一级一级向下委托加载任务。
- 作用:
- 保护程序的安全。
- 防止类加载重复。
反射泛型(了解)
- 反射父类的泛型
- 反射接口的泛型
- 反射属性上的泛型
- 反射方法参数上的泛型
- 反射方法返回值的泛型
- 反射构造方法参数上的泛型