1. 程式人生 > >Java程式設計中基礎反射詳細解析

Java程式設計中基礎反射詳細解析

在這裡插入圖片描述
類的載入、連線和初始化
1.1 類的載入
在這裡插入圖片描述
當程式主動使用某個類時,如果該類還沒有被載入到記憶體中,則系統會通過載入、連線、初始化這三個步驟對該類進行初始化。有時會把這一整個流程統稱為類載入或類初始化。
類載入指的是將類的class檔案讀入記憶體中,併為之建立一個 java.lang.Class 物件,也就是說程式使用任何類的時候,都會為其建立一個class物件。
1.2 類的連線
類被載入之後,系統會為之生成一個Class物件,接著會進入連線階段,連線階段負責把類的二進位制資料合併到JRE中。
類的連線又分為下面三個階段:
驗證:確保被載入類的正確性
準備:負責為類的靜態成員分配記憶體,並設定預設初始化值
解析:將類中的符號引用替換為直接引用
1.3 類的初始化
在這裡插入圖片描述


在java中對類變數指定初始值得方法有兩種:

  1. 宣告類變數時指定初始值;

  2. 使用靜態初始化塊為類變數指定初始值。類載入的時機建立類的例項的時候訪問類的靜態變數的時候呼叫類的靜態方法的時候使用反射方式來強制建立某個類或介面對應的java.lang.Class物件初始化某個類的子類的時候直接使用java.exe命令來執行某個主類
    1.4 類載入器
    在這裡插入圖片描述
    類載入器負責將.class檔案載入到記憶體中,併為之生成對應的Class物件。類載入器負責載入所有的類,系統為所有載入到記憶體中的類生成一個java.lang.Class 的例項。
    類載入器的組成:
    Bootstrap ClassLoader 根類載入器 : 也被稱為引導類載入器,負責Java核心類的載入,比如System類,在JDK中JRE的lib目錄下rt.jar檔案中的類
    Extension ClassLoader 擴充套件類載入器 : 負責JRE的擴充套件目錄中jar包的載入,在JDK中JRE的lib目錄下ext目錄
    System ClassLoader 系統類載入器 : 負責在JVM啟動時載入來自java命令的class檔案,以及classpath環境變數所指定的jar包和類路徑,主要是我們開發者自己寫的類
    2.反射
    Java反射就是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意方法和屬性;並且能改變它的屬性。Java反射就是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意方法和屬性;並且能改變它的屬性。
    反射機制允許程式在執行時取得任何一個已知名稱的class的內部資訊,包括包括其modifiers(修飾符),fields(屬性),methods(方法)等,並可於執行時改變fields內容或呼叫methods。那麼我們便可以更靈活的編寫程式碼,程式碼可以在執行時裝配,無需在元件之間進行原始碼連結,降低程式碼的耦合度;還有動態代理的實現等等。
    2.1 反射基本資訊
    java程式中許多物件在執行時會出現兩種型別:執行時型別和編譯時型別,例如Person p = new Student();這句程式碼中p在編譯時型別為Person,執行時型別為Student。程式需要在執行時發現物件和類的真實信心。而通過使用反射程式就能判斷出該物件和類屬於哪些類。
    2.1.1 Class物件
    在這裡插入圖片描述


    Java檔案被編譯後,生成了.class檔案,JVM此時就要去解讀.class檔案。當程式主動去使用某個類時,JVM會通過前面提到的三個步驟:載入、連線和初始化三個步驟對類進行初始化。被編譯後的Java檔案.class也被JVM解析為一個物件,這個物件就是java.lang.Class。這樣當程式在執行時,每個java檔案就最終變成了Class類物件的一個例項。我們通過Java的反射機制應用到這個例項,就可以去獲得甚至去新增改變這個類的屬性和動作,使得這個類成為一個動態的類。
      
    Class類的概念儘管很抽象,但是無疑,它是反射機制的起源,是Java語言中一個精巧美妙地設計。
      
    下面是翻譯後的中文文件的描述:
    Class類的例項表示正在執行的Java應用程式的類和介面。列舉是一種類,註釋(註解)是一種介面。每個陣列屬於被對映為Class物件的一個類,所有具有相同元素型別和維數的陣列都共享該Class物件。基本的Java型別(boolean、byte、char、short、int、long、float 和 double)和關鍵字 void 也表示為 Class 物件。Class沒有公用構造方法。Class物件是在載入類時由JVM以及通過呼叫類載入器中的defineClass方法自動構造的。
    2.1.2 Java反射機制的類庫支援在深入到反射機制之前,先探析一下反射機制的定義和應用。反射機制定義:Java反射機制是在執行狀態時,對於任意一個類,都能夠直到這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性。
    在Java中,Class類和java.lang.reflect類庫一起構成了對Java反射機制的支援。其中最常使用到的類是Constructor,Field,Method,而這三個類都繼承了一個介面java.lang.reflect.Member。
    2.2 反射的基本實現
    實驗類public class Cat {
    private String name = “”;
    private String master = “”;
    private int age = 0;

    public Cat() { }
    private Cat(String name, String master) { }
    public Cat(String name, String master, int age) { }

    public void eat() {
    System.out.println(“小魚乾真好吃~”);
    }

    private void play() {
    System.out.println(“快來陪我玩~”);
    }

    @Override
    public String toString() {
    return “Cat [name=” + name + “, master=” + master + “, age=” + age + “]”;
    }
    }
    2.2.1 獲取Class物件
    在java中獲取Class物件有三種方法:
    通過類名獲取:Class c1 = Student.class; 呼叫某各類的class屬性來獲取Class物件。
    通過物件獲取:Class c2 = stu.getClass(); 通過getClass方法,該方法是Object類下的一個方法。
    通過全類名獲取:Class c3 = Class.forName(“全限定類名”); 會有一個ClassNotFoundException異常public static void main(String[] args) throws Exception {
    Cat c = new Cat();

    Class cat1 = Class.forName(“algorithms.sort.Cat”);
    Class cat2 = Cat.class;
    Class cat3 = c.getClass();

    System.out.println(cat1 == cat2);
    System.out.println(cat2 == cat3);
    }//輸出結果
    true
    true
    2.2.2 獲取構造器並建立物件
    getConstructors():返回了表示此類公共構造方法的Constructor物件陣列。
    getDeclaredConstructors():這個方法返回Constructor物件的所有構造方法。獲取構造器public static void main(String[] args) throws Exception {
    Class cat = Cat.class;
    //獲取所有公共構造方法
    Constructor<?> cons[] = cat.getConstructors();
    for (Constructor<?> con : cons) {
    System.out.println(“getConstructors-----------” + con);
    }
    System.out.println("**************************************");
    //獲取所有構造方法
    Constructor<?> cons2[] = cat.getDeclaredConstructors();
    for (Constructor<?> con2 : cons2) {
    System.out.println(“getDeclaredConstructors—” + con2);
    }
    }
    //輸出結果
    getConstructors-----------public algorithms.sort.Cat(java.lang.String,java.lang.String,int)
    getConstructors-----------public algorithms.sort.Cat()


getDeclaredConstructors—public algorithms.sort.Cat(java.lang.String,java.lang.String,int)
getDeclaredConstructors—private algorithms.sort.Cat(java.lang.String,java.lang.String)
getDeclaredConstructors—public algorithms.sort.Cat()建立物件public static void main(String[] args) throws Exception {
Class cat = Cat.class;
//使用公共構造器例項化物件
Constructor<?> cons1 = cat.getConstructor();
//使用私有構造器例項化物件
Constructor<?> cons2 = cat.getDeclaredConstructor(String.class,String.class);
Cat cat1 = (Cat)cons1.newInstance();
//私有的構造方法反射後要開啟許可權才能進行相應操作
cons2.setAccessible(true);
Cat cat2 = (Cat)cons2.newInstance(“tom”,“denny”);
System.out.println(cat1);
System.out.println(cat2);
}
//輸出結果
Cat [name=, master=, age=0]
Cat [name=tom, master=denny, age=0]在建立物件的過程中,值得注意的是如果反射的構造方法是私有的,那麼要開啟訪問許可權才能進行物件的例項化;也就是使用cons2.setAccessible(true);語句的原因。2.2.3 獲取成員變數和成員方法獲取成員變數public static void main(String[] args) throws Exception {
Class cat = Cat.class;
//獲取構造器
Constructor<?> cons = cat.getConstructor(String.class,String.class,int.class);
//例項化物件
Cat cat1 = (Cat)cons.newInstance(“tom”,“denny”,5);
System.out.println(cat1);
System.out.println("****************");

Field fields = cat.getDeclaredField("name");
//開啟訪問許可權限制
fields.setAccessible(true);
fields.set(cat1, "jack");
System.out.println(cat1);

}
//輸出結果
Cat [name=tom, master=denny, age=5]


Cat [name=jack, master=denny, age=5]獲取成員方法public static void main(String[] args) throws Exception {
Class cat = Cat.class;
//獲取構造器
Constructor<?> cons = cat.getConstructor(String.class, String.class, int.class);
//例項化物件
Cat cat1 = (Cat) cons.newInstance(“tom”, “denny”, 5);
System.out.println(cat1);
System.out.println("****************");

//獲取私有和公共成員方法
Method method1 = cat.getDeclaredMethod("setName", String.class);
Method method2 = cat.getDeclaredMethod("eat");
method1.setAccessible(true);
method1.invoke(cat1, "petter");
System.out.println(cat1);
method2.invoke(cat1);

}
//輸出結果
Cat [name=tom, master=denny, age=5]


Cat [name=petter, master=denny, age=5]
小魚乾真好吃~2.2.4 反射越過泛型檢查public static void main(String[] args) throws Exception {
//泛型只在編譯期進行檢查,在執行期會被擦除
ArrayList list = new ArrayList<>();
list.add(111);
list.add(222);
//拿到位元組碼檔案,位元組碼檔案屬於執行期
Class cla = Class.forName(“java.util.ArrayList”);
Method meth = cla.getMethod(“add”, Object.class);
meth.invoke(list, “abc”);
System.out.println(list);
}
//輸出結果
[111, 222, abc]
文章來自:https://www.itjmd.com/news/show-4288.html