反射的主要作用就是獲取JVM中的Class對象,獲取了對象之後可以實現很多功能,比方說IOC容器通過反射創建對象、或者是動態代理(這個實際應用講完反射原理後稍微再講一下)。
一、獲取Class對象的三種方式:
如何獲取Class對象呢?有三種情況:
- Java代碼僅經過編譯生成了字節碼文件,還未加載到內存中
- Java代碼加載到了內存中,但是還未創建對象實例
- 已經創建了對象實例
針對這三種情況分別有以下三種方式獲取Class對象:
1、Class.forName(“全類名”):只有字節碼文件,還未被加載進內存。多用於配置文件,將類名定義在配置文件中
2、類名.class:已經加載進內存但是還沒有對象。多用於參數的傳遞
3、對象名.getClass():有對象希望獲取字節碼。
同一個字節碼文件在一次程序的運行過程中只會被加載一次。不論哪一種方式獲取的class類對象都是同一個,即這三種方式只會創建一個Class對象
二、Class對象的功能(即Class類中有哪些常用的方法)
以下代碼均假設已經獲取到了Person類的Class對象:personClass
1、獲取類名
String getName()
// 獲取到的是全類名
String className = personClass.getName();
2、獲取構造函數
反射獲取Class對象後最重要的一個功能就是創建對象,創建對象自然是要通過構造函數。
方法
-
Constructor<?>[] getConstructors
:獲取所有public修飾的構造函數 -
Constructor<T> getConstructor(類<?>...parameterTypes)
獲取指定的public修飾的構造函數參數
類<?>...parameterTypes
表示要傳構造函數參數的類型,因爲同一個類中區分構造函數只能通過參數
如要獲取構造函數Person(String name, int get),則personClass.getConstructor(String.class, int.class) -
Constructor<?>[] getDeclaredConstructors()
:獲取所有的構造函數 -
Constructor<T> getDeclaredConstructor(類<?>...parameterTypes)
:獲取任一指定的構造函數
作用
1、獲取構造函數當然是爲了創建對象
T newInstance(parameters...)
Class.newInstance()
:調用空參的構造方法可以這樣簡化,但是這個方法好像JDK9之後被廢除了。
Person p = new Person;
// 空參構造函數簡化調用:personClass.newInstance();
// 假設獲取的構造方法有兩個參數:Stirng類型的名字和int類型的年齡
Constructor constructor = personClass.getConstructor(String.class, int.class);
constructor.newInstance("martin", 23);
私有構造函數可以通過getDeclaredConstructor()獲取Constructor對象,但是不能constructor.newInstance()調用,會報IllegalAccessException(非法訪問異常),可以通過執行constructor.setAccessible(true)忽略訪問權限修飾符的安全檢查
3、獲取成員變量
方法
Field[] getFields()
:獲取所有public修飾的成員變量Field getField(String name)
:獲取所有指定的public修飾的成員變量Field[] getDeclaredFields()
:獲取所有的成員變量Field getDeclaredField(String name)
:獲取任一指定的成員變量
作用
1、取值get()
、賦值set()
Field field = personClass.getField("name");
Person p = new Person;
Object value = field.get(p); // 獲取對象p的name屬性
name.set(field, "martin"); // 設置對象p的name屬性
同理可以通過field .setAccessible(true)暴力反射
4、獲取成員方法
如何:
Method[] getMethods()
:獲取所有public修飾的成員函數,包括從父類如Object等類中繼承的Method getMethod(String name, 類<?>...parameterTypes)
:
獲取所有public修飾的指定的成員函數。第一個參數爲函數方法名,後面的參數是函數的參數類型。Method[] getDeclaredMethods()
:獲取本類中所有方法,不會獲取父類中的Method[] getDeclaredMethod(String name, 類<?>...parameterTypes)
:獲取本類中任一指定的方法
同理,私有方法可以通過setAccessible(true)暴力反射
作用:
1、獲取方法名
Method.getName()
2、執行方法
Method.invoke()
Person p = new Person;
// 獲取eat()的方法對象
Method method_eat = personClass.getMethod("eat");
method_eat.invoke(p);
// 獲取eat(String foodName)的方法對象
Method method_eat = personClass.getMethod("eat", String.class);
method_eat.invoke(p, "rice");
三、案例
1、jdbc連接數據庫中通過Class.forName()加載數據庫驅動
2、代理模式對方法增強(aop的原理也是動態代理),這裏稍微看一下動態代理中的JDK代理,先上關鍵部分的代碼
public class StarProxy implements InvocationHandler {
// target接受的是接口,JDK動態代理面向接口代理
private Object target;
public void setTarget(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 加強操作
System.out.println("收代理費");
// 調用原方法
Object result = method.invoke(target, args);
//加強操作
System.out.println("互換名片");
return result;
}
// 這個方法返回的是代理對象實例,newProxyInstance()深入進去就是根據反射調用了構造函數生成實例
//PS:生成代理類的方法不一定寫在這裏,可以寫在一個工廠方法中
public Object CreateProxyObject() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
文章是看了視頻總結,視頻講的很好,附上視頻鏈接