Java 反射与动态代理学习笔记
反射概述
反射允许对成员变量、成员方法和构造方法进行编程访问,提供了在运行时分析类和对象的能力。
获取Class对象的三种方式
方式 |
代码示例 |
说明 |
Class.forName() |
Class.forName("全类名") |
通过类的全限定名获取Class对象 |
对象.getClass() |
对象.getClass() |
通过对象实例获取Class对象 |
类名.class |
类名.class |
通过类字面常量获取Class对象 |
// 1. Class.forName("全类名")
Class clazz1 = Class.forName("com.zzz.Student");
System.out.println(clazz1);
// 2. 对象.getClass()
ReflectionDemo reflectionDemo = new ReflectionDemo();
Class clazz2 = reflectionDemo.getClass();
// 3. 类名.class
Class clazz3 = ReflectionDemo.class;
// 三种方式获取的Class对象是相同的
System.out.println(clazz1 == clazz2); // true
System.out.println(clazz1 == clazz3); // true
反射操作构造方法
Class类中获取构造方法的方法
方法 |
说明 |
Constructor<?>[] getConstructors() |
返回所有公共构造方法对象的数组 |
Constructor<?>[] getDeclaredConstructors() |
返回所有构造方法对象的数组 |
Constructor<T> getConstructor(Class<?>... parameterTypes) |
返回单个公共构造方法对象 |
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) |
返回单个构造方法对象 |
Constructor类中创建对象的方法
方法 |
说明 |
T newInstance(Object... initargs) |
根据指定的构造方法创建对象 |
setAccessible(boolean flag) |
设置为true,表示取消访问检查 |
// 获取所有公共构造方法
Constructor[] constructors = clazz1.getConstructors();
for (Constructor c : constructors) {
System.out.println(c);
}
// 输出:
// public com.zzz.Student()
// public com.zzz.Student(java.lang.String,int)
// 获取所有构造方法(包括私有和受保护的)
Constructor[] allConstructors = clazz1.getDeclaredConstructors();
for (Constructor c : allConstructors) {
System.out.println(c);
}
// 输出:
// public com.zzz.Student()
// private com.zzz.Student(int)
// protected com.zzz.Student(java.lang.String)
// public com.zzz.Student(java.lang.String,int)
// 获取特定构造方法
Constructor constructor = clazz1.getConstructor(String.class, int.class);
System.out.println(constructor); // public com.zzz.Student(java.lang.String,int)
// 获取私有构造方法
Constructor privateConstructor = clazz1.getDeclaredConstructor(int.class);
System.out.println(privateConstructor);
// 使用构造方法创建对象
constructor.setAccessible(true); // 临时取消权限校验
Student stu = (Student) constructor.newInstance("张三", 18);
System.out.println(stu);
反射操作成员变量
Class类中获取成员变量的方法
方法 |
说明 |
Field[] getFields() |
返回所有公共成员变量对象的数组 |
Field[] getDeclaredFields() |
返回所有成员变量对象的数组 |
Field getField(String name) |
返回单个公共成员变量对象 |
Field getDeclaredField(String name) |
返回单个成员变量对象 |
Field类中操作成员变量的方法
方法 |
说明 |
void set(Object obj, Object value) |
给指定对象的字段赋值 |
Object get(Object obj) |
获取指定对象的字段值 |
String getName() |
获取字段名称 |
Class<?> getType() |
获取字段类型 |
int getModifiers() |
获取字段修饰符 |
// 获取所有公共成员变量
Field[] publicFields = clazz1.getFields();
for (Field f : publicFields) {
System.out.println(f);
}
// 输出: public java.lang.String com.zzz.Student.gender
// 获取所有成员变量(包括私有)
Field[] allFields = clazz1.getDeclaredFields();
for (Field f : allFields) {
System.out.println(f);
}
// 输出:
// private java.lang.String com.zzz.Student.name
// private int com.zzz.Student.age
// public java.lang.String com.zzz.Student.gender
// 获取特定成员变量
Field ageField = clazz1.getDeclaredField("age");
System.out.println(ageField); // private int com.zzz.Student.age
// 获取成员变量信息
String fieldName = ageField.getName();
System.out.println(fieldName); // age
Class fieldType = ageField.getType();
System.out.println(fieldType); // int
int modifiers = ageField.getModifiers();
System.out.println(modifiers); // 2 (表示private)
// 获取和设置字段值
Student student = new Student("张三", 18, "男");
ageField.setAccessible(true); // 访问私有字段需要取消访问检查
int ageValue = (int) ageField.get(student);
System.out.println(ageValue); // 18
// 修改字段值
ageField.set(student, 19);
System.out.println(student); // Student{name='张三', age=19, gender='男'}
反射操作成员方法
Class类中获取成员方法的方法
方法 |
说明 |
Method[] getMethods() |
返回所有公共成员方法对象的数组(包括父类方法) |
Method[] getDeclaredMethods() |
返回所有成员方法对象的数组(仅本类方法) |
Method getMethod(String name, Class<?>... parameterTypes) |
返回单个公共成员方法对象 |
Method getDeclaredMethod(String name, Class<?>... parameterTypes) |
返回单个成员方法对象 |
Method类中调用方法的方法
方法 |
说明 |
Object invoke(Object obj, Object... args) |
调用方法 |
String getName() |
获取方法名称 |
Class<?> getReturnType() |
获取方法返回值类型 |
Class<?>[] getParameterTypes() |
获取方法参数类型数组 |
Class<?>[] getExceptionTypes() |
获取方法异常类型数组 |
int getModifiers() |
获取方法修饰符 |
// 获取所有公共方法(包括父类方法)
Method[] publicMethods = clazz1.getMethods();
for (Method m : publicMethods) {
System.out.println(m);
}
// 获取所有方法(仅本类方法)
Method[] allMethods = clazz1.getDeclaredMethods();
for (Method m : allMethods) {
System.out.println(m);
}
// 获取特定方法
Method sleepMethod = clazz1.getMethod("sleep");
System.out.println(sleepMethod);
// 获取带参数的方法
Method eatMethod = clazz1.getDeclaredMethod("eat", String.class);
System.out.println(eatMethod);
// 获取方法信息
int methodModifiers = eatMethod.getModifiers();
System.out.println(methodModifiers);
String methodName = eatMethod.getName();
System.out.println(methodName); // eat
Class returnType = eatMethod.getReturnType();
System.out.println(returnType); // class java.lang.String
Class[] parameterTypes = eatMethod.getParameterTypes();
for (Class c : parameterTypes) {
System.out.println(c); // class java.lang.String
}
Class[] exceptionTypes = eatMethod.getExceptionTypes();
for (Class c : exceptionTypes) {
System.out.println(c); // class java.io.IOException, class java.lang.ClassNotFoundException
}
// 调用方法
Student student = new Student("张三", 18, "男");
eatMethod.setAccessible(true); // 访问私有方法需要取消访问检查
Object result = eatMethod.invoke(student, "西风");
System.out.println(result); // 吃奥里给
import java.io.IOException;
public class Student {
private String name;
private int age;
public String gender;
public Student() {}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
protected Student(String name) {
this.name = name;
}
private Student(int age) {
this.age = age;
}
public Student(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
// Getter和Setter方法
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getGender() { return gender; }
public void setGender(String gender) { this.gender = gender; }
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + ", gender='" + gender + "'}";
}
public void sleep() {
System.out.println("睡觉");
}
private String eat(String something) throws IOException, ClassNotFoundException {
System.out.println("吃" + something);
return "吃奥里给";
}
}
反射与配置文件结合
利用反射与配置文件结合的方式,可以动态创建对象并调用方法,提高代码的灵活性。
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException,
NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 1. 读取配置文件
Properties properties = new Properties();
FileInputStream fis = new FileInputStream("day27-Reflection\\src\\prop.properties");
properties.load(fis);
fis.close();
System.out.println(properties);
// 2. 获取全类名和方法名
String methodName = properties.getProperty("method");
String className = properties.getProperty("className");
System.out.println(className);
System.out.println(methodName);
// 3. 利用反射创建对象
Class clazz = Class.forName(className);
Constructor constructor = clazz.getConstructor();
Object obj = constructor.newInstance();
System.out.println(obj);
// 4. 获取并调用方法
Method method = clazz.getDeclaredMethod(methodName);
method.setAccessible(true);
method.invoke(obj);
}
}
配置文件示例(prop.properties):
className=com.zzz.demo.Student
method=study




动态代理
动态代理可以在运行时创建代理对象,对方法调用进行拦截和处理。
代理接口
public interface Star {
// 唱歌
String sing();
// 跳舞
void dance();
}
被代理类
public class BigStar implements Star {
private String name;
public BigStar() {}
public BigStar(String name) {
this.name = name;
}
// 唱歌
@Override
public String sing() {
System.out.println(name + "正在唱歌");
return "谢谢";
}
// 跳舞
@Override
public void dance() {
System.out.println(name + "正在跳舞");
}
// Getter和Setter省略
}
代理工具类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyUtil {
/**
* JDK创建代理对象
* Java.lang.reflect.Proxy类中提供了为对象产生代理对象的方法
*
* public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
* 参数一:用于指定用哪个类加载器,去加载生成的代理类
* 参数二:指定接口,这些接口用于指定生成的代理有哪些方法
* 参数三:用来指定生成的代理对象要干什么事
*/
public static Star createProxy(BigStar bigStar) {
Star star = (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),
new Class[]{Star.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* proxy 第一个参数:代理的对象
* method 第二个参数:代理对象要调用的方法 sing
* args 第三个参数:代理对象要调用方法时,传递的参数
*/
if (method.getName().equals("sing")) {
System.out.println("代理开始工作,准备话筒,收钱");
} else if (method.getName().equals("dance")) {
System.out.println("代理开始工作,准备场地,收钱");
}
// 调用大明星中的唱歌或者跳舞方法
Object result = method.invoke(bigStar, args);
if (method.getName().equals("sing")) {
System.out.println("代理工作结束(唱歌)");
} else if (method.getName().equals("dance")) {
System.out.println("代理工作结束(跳舞)");
}
return result;
}
});
return star;
}
}
测试代理
public class Test {
public static void main(String[] args) {
BigStar star = new BigStar("张三");
Star proxy = ProxyUtil.createProxy(star);
proxy.dance();
// 输出:
// 代理开始工作,准备场地,收钱
// 张三正在跳舞
// 代理工作结束(跳舞)
String singResult = proxy.sing();
System.out.println(singResult);
// 输出:
// 代理开始工作,准备话筒,收钱
// 张三正在唱歌
// 代理工作结束(唱歌)
// 谢谢
}
}
总结
反射是Java中强大的特性,它允许程序在运行时检查类、接口、字段和方法的信息,并能够动态创建对象、调用方法和访问字段。结合配置文件使用反射可以提高代码的灵活性和可扩展性。
动态代理则基于反射机制,允许在运行时创建代理对象,对方法调用进行拦截和处理,常用于AOP编程、日志记录、事务管理等场景。