版權聲明:本文爲博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/lib739449500/article/details/44966689
反射(Reflect)
反射之中包含了一個「反」字,所以瞭解反射我們先從「正」開始。
一般情況下,我們使用某個類時必定知道它是什麼類,是用來做什麼的。於是我們直接對這個類進行實例化,之後使用這個類對象進行操作。
反射則是一開始並不知道我要初始化的類對象是什麼,自然也無法使用 new 關鍵字來創建對象了。這時候,我們使用 JDK 提供的反射 API 進行反射調用。反射就是在運行時才知道要操作的類是什麼,並且可以在運行時獲取類的完整構造,並調用對應的方法。
Reflection(反射)是Java被視爲動態語言的關鍵,反射機制允許程序在執行期藉助於Reflection API取得任何類的內部信息,並能直接操作任意對象的內部屬性及方法。
Java反射機制主要提供了以下功能:
- 在運行時構造任意一個類的對象
- 在運行時獲取任意一個類所具有的成員變量和方法
- 在運行時調用任意一個對象的方法(屬性)
Java 是一門面向對象的語言。在面向對象的世界裏,萬事萬物皆對象,既然萬事萬物皆對象,那麼我們的類是不是對象呢?我們寫的每一個類都可以看成一個對象,是 java.lang.Class 類的對象。每一個類對應的Class放在哪裏呢?當我們寫完一個類的Java文件,編譯成class文件的時候,編譯器都會將這個類的對應的class對象放在class文件的末尾。裏面都保存了些什麼?大家可以理解保存了類的元數據信息,一個類的元數據信息包括什麼?有哪些屬性,方法,構造器,實現了哪些接口等等,那麼這些信息在Java裏都有對應的類來表示。
Class類
Class是一個類,封裝了當前對象所對應的類的信息
一個類中有屬性,方法,構造器等,比如說有一個Person類,一個Order類,一個Book類,這些都是不同的類,現在需要一個類,用來描述類,這就是Class,它應該有類名,屬性,方法,構造器等。Class是用來描述類的類。
Class類是一個對象照鏡子的結果,對象可以看到自己有哪些屬性,方法,構造器,實現了哪些接口等等
對於每個類而言,JRE 都爲其保留一個不變的 Class 類型的對象。一個 Class 對象包含了特定某個類的有關信息。
對象只能由系統建立對象,一個類(而不是一個對象)在 JVM 中只會有一個Class實例
獲取Class對象的三種方式
1.通過類名獲取 類名.class
2.通過對象獲取 對象名.getClass()
3.通過全類名獲取 Class.forName(全類名)
Class類的常用方法
類加載器、構造器、Method、Field的使用
現在有一個person類
public class Person {
String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
System.out.println("this is setName()!");
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
System.out.println("this is setAge()!");
}
//包含一個帶參的構造器和一個不帶參的構造器
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Person() {
super();
}
//私有方法
private void privateMethod(){
System.out.println("this is private method!");
}
}
類加載器測試
public class TestClassLoader {
public static void main(String[] args) throws FileNotFoundException, ClassNotFoundException {
testClassLoader();
}
/*類加載器相關*/
public static void testClassLoader() throws ClassNotFoundException,
FileNotFoundException {
//1. 獲取一個系統的類加載器(可以獲取,當前這個類TestClassLoader就是它加載的)
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
//2. 獲取系統類加載器的父類加載器(擴展類加載器,可以獲取).
classLoader = classLoader.getParent();
System.out.println(classLoader);
//3. 獲取擴展類加載器的父類加載器(引導類加載器,不可獲取).
classLoader = classLoader.getParent();
System.out.println(classLoader);
//4. 測試當前類由哪個類加載器進行加載(系統類加載器):
classLoader = Class.forName("com.lbin.Person")
.getClassLoader();
System.out.println(classLoader);
//5. 測試 JDK 提供的 Object 類由哪個類加載器負責加載(引導類加載器,不可獲取)
classLoader = Class.forName("java.lang.Object")
.getClassLoader();
System.out.println(classLoader);
}
}
運行結果
構造器測試
public class TestConstructor {
public static void main(String[] args) throws Exception {
new TestConstructor().testConstructor();
}
/*構造器相關*/
public void testConstructor() throws Exception{
String className = "com.lbin.Person";
Class<Person> clazz = (Class<Person>) Class.forName(className);
System.out.println(clazz.getName());
System.out.println("獲取全部Constructor對象-----");
Constructor<Person>[] constructors
= (Constructor<Person>[]) clazz.getConstructors();
for(Constructor<Person> constructor: constructors){
System.out.println(constructor);
}
System.out.println("獲取某一個Constructor 對象,需要參數列表----");
Constructor<Person> constructor
= clazz.getConstructor(String.class, int.class);
System.out.println(constructor);
//2. 調用構造器的 newInstance() 方法創建對象
System.out.println("調用構造器的 newInstance() 方法創建對象-----");
Person obj = constructor.newInstance("Mark", 18);
System.out.println(obj.getName());
}
}
運行結果
Method測試
public class TestMethod {
public static void main(String[] arg) throws Exception {
new TestMethod().testMethod();
}
/*方法相關*/
public void testMethod() throws Exception{
Class clazz = Class.forName("com.lbin.Person");
System.out.println("獲取clazz對應類中的所有方法,不能獲取private方法,且獲取從父類繼承來的所有方法");
Method[] methods = clazz.getMethods();
for(Method method:methods){
System.out.print(" "+method.getName()+"()");
}
System.out.println("");
System.out.println("---------------------------");
System.out.println("獲取所有方法,包括私有方法,所有聲明的方法,都可以獲取到,且只獲取當前類的方法");
methods = clazz.getDeclaredMethods();
for(Method method:methods){
System.out.print(" "+method.getName()+"()");
}
System.out.println("");
System.out.println("---------------------------");
System.out.println("獲取指定的方法,需要參數名稱和參數列表,無參則不需要寫");
// 方法public void setName(String name) { }
Method method = clazz.getDeclaredMethod("setName", String.class);
System.out.println(method);
System.out.println("---");
// 方法public void setAge(int age) { }
/* 這樣寫是獲取不到的,如果方法的參數類型是int型
如果方法用於反射,那麼要麼int類型寫成Integer: public void setAge(Integer age) { }
要麼獲取方法的參數寫成int.class*/
method = clazz.getDeclaredMethod("setAge", int.class);
System.out.println(method);
System.out.println("---------------------------");
System.out.println("執行方法,第一個參數表示執行哪個對象的方法,剩下的參數是執行方法時需要傳入的參數");
Object obje = clazz.newInstance();
method.invoke(obje,18);
System.out.println("---------------------------");
/*私有方法的執行,必須在調用invoke之前加上一句method.setAccessible(true);*/
method = clazz.getDeclaredMethod("privateMethod");
System.out.println(method);
System.out.println("執行私有方法");
method.setAccessible(true);
method.invoke(obje);
}
}
運行結果
Field測試
public class TestField {
public static void main(String[] arg) throws Exception {
new TestField().testField();
}
/*域相關*/
public void testField() throws Exception {
String className = "com.lbin.Person";
Class clazz = Class.forName(className);
System.out.println("獲取公用和私有的所有字段,但不能獲取父類字段");
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.print(" " + field.getName());
}
System.out.println();
System.out.println("---------------------------");
System.out.println("獲取指定字段");
Field field = clazz.getDeclaredField("name");
System.out.println(field.getName());
Person person = new Person("ABC", 12);
System.out.println("獲取指定字段的值");
Object val = field.get(person);
System.out.println(field.getName() + "=" + val);
System.out.println("設置指定對象指定字段的值");
field.set(person, "DEF");
System.out.println(field.getName() + "=" + person.getName());
System.out.println("字段是私有的,不管是讀值還是寫值,都必須先調用setAccessible(true)方法");
//比如Person類中,字段name字段是非私有的,age是私有的
field = clazz.getDeclaredField("age");
field.setAccessible(true);
System.out.println(field.get(person));
}
}
測試結果
動態代理
代理模式和靜態代理
代理模式就是給某一個對象提供一個代理對象,並由代理對象控制對原對象的引用。通俗的來講代理模式就是我們生活中常見的中介。
舉個例子來說明:張三想買某種生活用品,雖然他可以自己去找,但是這確實太浪費時間和精力了,或者不好意思去買。於是張三就通過中介LB來買,LB來幫張三,張三隻是負責選擇自己喜歡的的size,然後付錢就可以了。
目的:(1)通過引入代理對象的方式來間接訪問目標對象,防止直接訪問目標對象給系統帶來的不必要複雜性; (2)通過代理對象對原有的業務增強;
代理模式一般會有三個角色:
抽象角色:指代理角色和真實角色對外提供的公共方法,一般爲一個接口
真實角色:需要實現抽象角色接口,定義了真實角色所要實現的業務邏輯,以便供代理角色調用。也就是真正的業務邏輯在此。
代理角色:需要實現抽象角色接口,是真實角色的代理,通過真實角色的業務邏輯方法來實現抽象方法,並可以附加自己的操作。將統一的流程控制都放到代理角色中處理!
而訪問者不再訪問真實角色,而是去訪問代理角色。
靜態代理在使用時,需要定義接口或者父類,被代理對象與代理對象一起實現相同的接口或者是繼承相同父類。一般來說,被代理對象和代理對象是一對一的關係,當然一個代理對象對應多個被代理對象也是可以的。
靜態代理,一對一則會出現時靜態代理對象量多、代碼量大,從而導致代碼複雜,可維護性差的問題,一對多則代理對象會出現擴展能力差的問題。
動態代理
是指在使用時再創建代理類和實例
優點
只需要1個動態代理類就可以解決創建多個靜態代理的問題,避免重複、多餘代碼
更強的靈活性
缺點
效率低,相比靜態代理中 直接調用目標對象方法,動態代理則需要先通過Java反射機制 從而 間接調用目標對象方法
應用場景侷限,因爲 Java 的單繼承特性(每個代理類都繼承了 Proxy 類),即只能針對接口 創建 代理類,不能針對類創建代理類。
在java的動態代理機制中,有兩個重要的類或接口,一個是InvocationHandler接口、另一個則是 Proxy類,這個類和接口是實現我們動態代理所必須用到的。
InvocationHandler接口是給動態代理類實現的,負責處理被代理對象的操作的,而Proxy是用來創建動態代理類實例對象的,因爲只有得到了這個對象我們才能調用那些需要代理的方法。