6.java反射
反射
1.反射机制概述
-
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
-
2.获取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");
3.反射作用的体现
-
在属性配置文件中配置类名:classInfo.properties
className=java.util.Date
-
通过IO流读取属性配置文件,获取类名,再通过反射机制实例化对象。
-
如果要创建其他类的实例对象,只需要修改classInfo.properties配置文件即可。
-
这说明反射机制可以让程序变的更加灵活。在进行系统扩展时,可以达到OCP开闭原则。
4.反射Field(属性)
反射Field包括两方面:
-
一方面:通过反射机制获取Field
Field field = clazz.getDeclaredField("fieldName"); // 通过属性名获取 Field[] fields = clazz.getDeclaredFields(); // 获取所有的属性,包括私有的
-
另一方面:通过Filed访问对象的属性
Object fieldValue = field.get(myObject); // 读取某个对象的属性值 field.set(myObject, newValue); // 修改某个对象的属性值
public class ReflectionExample {public static void main(String[] args) throws Exception {// 获取类的Class对象Class<?> clazz = Person.class;// 创建对象实例Person person = new Person("张三", 20);// 获取name属性Field nameField = clazz.getDeclaredField("name");// 设置可访问(如果是私有属性)nameField.setAccessible(true);// 读取属性值String name = (String) nameField.get(person);System.out.println("原始姓名:" + name);// 修改属性值nameField.set(person, "李四");System.out.println("修改后姓名:" + person.getName());}static class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}}
}
5.反射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);
public class MethodReflectionExample {public static void main(String[] args) throws Exception {// 获取目标类的Class对象Class<?> clazz = Calculator.class;// 创建对象实例Calculator calculator = new Calculator();// 获取add方法(参数为两个int类型)Method addMethod = clazz.getDeclaredMethod("add", int.class, int.class);// 调用方法Object result = addMethod.invoke(calculator, 10, 20);System.out.println("10 + 20 = " + result);// 获取私有方法multiplyMethod multiplyMethod = clazz.getDeclaredMethod("multiply", int.class, int.class);// 设置私有方法可访问multiplyMethod.setAccessible(true);// 调用私有方法Object multiplyResult = multiplyMethod.invoke(calculator, 5, 6);System.out.println("5 × 6 = " + multiplyResult);}static class Calculator {// 公有方法public int add(int a, int b) {return a + b;}// 私有方法private int multiply(int a, int b) {return a * b;}}
}
6.反射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);
public class ConstructorReflectionExample {public static void main(String[] args) throws Exception {// 获取目标类的Class对象Class<?> clazz = Person.class;// 获取有参构造方法(参数为String和int类型)Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);// 使用构造方法创建对象Object person = constructor.newInstance("张三", 20);System.out.println(person);// 获取私有构造方法Constructor<?> privateConstructor = clazz.getDeclaredConstructor(int.class);// 设置私有构造方法可访问privateConstructor.setAccessible(true);// 使用私有构造方法创建对象Object person2 = privateConstructor.newInstance(30);System.out.println(person2);}static class Person {private String name;private int age;// 公有有参构造方法public Person(String name, int age) {this.name = name;this.age = age;}// 私有构造方法private Person(int age) {this.name = "默认名称";this.age = age;}@Overridepublic String toString() {return "Person{name='" + name + "', age=" + age + "}";}}
}
7.模拟框架的部分实现
-
配置文件中配置如下信息:classInfo.properties
className=com.powernode.javase.reflect.UserService methodName=login parameterTypes=java.lang.String,java.lang.String parameterValues=admin,123456
-
通过反射机制创建对象,调用配置的方法
-
本案例的实现类似于Spring框架中部分实现,主要是通过修改配置文件来达到创建不同的对象,调用不同的方法。
public class ReflectionDemo {public static void main(String[] args) {// 1. 读取配置文件Properties properties = new Properties();try {// 加载配置文件properties.load(new FileInputStream("classInfo.properties"));// 2. 获取配置信息String className = properties.getProperty("className");String methodName = properties.getProperty("methodName");String parameterTypesStr = properties.getProperty("parameterTypes");String parameterValuesStr = properties.getProperty("parameterValues");// 3. 解析参数类型String[] parameterTypeNames = parameterTypesStr.split(",");Class<?>[] parameterTypes = new Class[parameterTypeNames.length];for (int i = 0; i < parameterTypeNames.length; i++) {parameterTypes[i] = Class.forName(parameterTypeNames[i]);}// 4. 解析参数值String[] parameterValues = parameterValuesStr.split(",");// 5. 加载类并获取Class对象Class<?> clazz = Class.forName(className);// 6. 创建对象实例(这里使用无参构造方法)Object obj = clazz.newInstance();// 7. 获取要调用的方法Method method = clazz.getDeclaredMethod(methodName, parameterTypes);// 8. 调用方法(需要将字符串参数转换为对应类型)Object result = method.invoke(obj, (Object[]) parameterValues);// 9. 输出结果System.out.println("方法执行结果:" + result);} catch (IOException e) {System.out.println("配置文件读取失败:" + e.getMessage());} catch (Exception e) {System.out.println("反射操作失败:" + e.getMessage());e.printStackTrace();}}
}public class UserService {/*** 登录方法* @param username 用户名* @param password 密码* @return 登录结果*/public String login(String username, String password) {// 模拟登录验证逻辑if ("admin".equals(username) && "123456".equals(password)) {return "登录成功,欢迎回来," + username + "!";} else {return "登录失败,用户名或密码错误";}}
}
8.类加载及双亲委派机制
类加载的过程
-
装载(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()方法一级一级获取
public class ClassLoaderTest {public static void main(String[] args) {// 获取应用类加载器ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();System.out.println("应用类加载器:" + appClassLoader); // AppClassLoader// 获取平台类加载器(父加载器)ClassLoader platformClassLoader = appClassLoader.getParent();System.out.println("平台类加载器:" + platformClassLoader); // PlatformClassLoader// 获取启动类加载器(父加载器,返回null,因为是C实现)ClassLoader bootstrapClassLoader = platformClassLoader.getParent();System.out.println("启动类加载器:" + bootstrapClassLoader); // null}
}
双亲委派机制
-
某个类加载器接收到加载类的任务时,通常委托给“父 类加载”完成加载。
-
最“父 类加载器”无法加载时,一级一级向下委托加载任务。
-
作用:
-
保护程序的安全。
-
防止类加载重复。
-
举例说明
以加载com.example.MyClass
类为例,流程如下:
- 应用类加载器(子)收到请求,先委托给父加载器 —— 平台类加载器。
- 平台类加载器再委托给父加载器 —— 启动类加载器。
- 启动类加载器检查后发现
com.example.MyClass
不在自己的加载范围内(它只加载核心类),无法加载。 - 任务退回给平台类加载器,平台类加载器也无法加载(它加载平台扩展类),再次退回。
- 最终由应用类加载器(最初的子加载器)加载
com.example.MyClass
(因为它在 classpath 下)。
9.反射泛型
-
反射父类的泛型
-
反射接口的泛型
-
反射属性上的泛型
-
反射方法参数上的泛型
-
反射方法返回值的泛型
-
反射构造方法参数上的泛型