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); // ageClass fieldType = ageField.getType();
System.out.println(fieldType); // intint 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); // eatClass returnType = eatMethod.getReturnType();
System.out.println(returnType); // class java.lang.StringClass[] 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; }@Overridepublic 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;}// 唱歌@Overridepublic String sing() {System.out.println(name + "正在唱歌");return "謝謝";}// 跳舞@Overridepublic 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() {@Overridepublic 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編程、日志記錄、事務管理等場景。