標籤:java 反射
反射是什麼
Reflection(反射)就是Java程式在運行時可以動態得到程式內部所有類的內部資訊,並能動態調用任意對象的內部屬性和方法。
為什麼需要反射
我們為什麼要用反射,這主要是反射的動態性決定的,由於反射可以實現動態建立對象,這就很大程度發揮了java的靈活性,降低了程式調用的耦合性,使系統可以更加的靈活,可以更好的應對變化。
反射應用
運行反射我們可以做到:
在運行時判斷任意一個對象所屬的類
在運行時構造任意一個類的對象
在運行時判斷任意一個類所具有的成員變數和方法
在運行時調用任意一個對象的成員變數和方法
產生動態代理
跟反射相關的常用類主要有:
java.lang.Class:代表一個類
java.lang.reflect.Method:代表類的方法
java.lang.reflect.Field:代表類的成員變數
java.lang.reflect.Constructor:代表類的構造方法
Class類
Class類是反射機制的源頭,實際上所謂的反射從程式的運行結果來看就是,通過對象反射擷取類的名稱。
反射其實是類正常方式使用的反過程就像物理學中的反射一樣:
通過反射我們可以得到的資訊:某個類的屬性、方法、構造器、類實現了哪些介面、父類是什麼。對於每個類而言,JRE都為其保留了一個不變的Class類型的對象,一個Class對象包含了特定某個類的有關資訊。
Class本身也是一個類,每個類在JVM中只有有一個Class執行個體,每個Class對象對應的是一個載入到JVM中的一個.class檔案,每個類的執行個體都會記得自己是由哪個Class執行個體所產生的。
通過反射得到Class類對象,四種方式
我們現在有一個建立好的類Person,類路徑是package com.tgb.reflect.common;
1.已知具體Person類,通過Person類的class屬性擷取,該方法最為安全可靠,程式效能最高。
@Testpublic void test1(){Classclass1 = Person.class;System.out.println(class1);}
2.已知Person類的執行個體,調用該執行個體的getClass()方法擷取Class對象
@Testpublic void test2() {Personp = new Person();Classclass1 =p.getClass();System.out.println(class1);}
3.已知Person類的全類名,且該類在類路徑下,可通過Class類的靜態方法forName()擷取。
@Testpublic void test3()throws ClassNotFoundException{StringclassName = "com.tgb.reflect.common.Person";Classclass1 = Class.forName(className);System.out.println(class1);}
4.通過類載入器來擷取Class對象。
@Testpublic void test4()throws ClassNotFoundException{StringclassName= "com.tgb.reflect.common.Person";ClassLoaderclassLoader =this.getClass().getClassLoader();Classclass1=classLoader.loadClass(className);System.out.println(class1);}
以上四種方式得到Class對象的代碼經運行得到的結果是一樣的,運行結果是"classcom.tgb.reflect.common.Person";
類載入器
當我們程式主動使用某個類時,如果該類還未被載入到記憶體中,則系統會通過如下三個步驟來對該類進行初始化。
ClassLoader(類載入器)是用來把類(.class)裝在進入記憶體的,JVM在運行時會產生3個類載入器組成的初始化載入器階層 ,如所示:
我們通過代碼來看下:
@Testpublic void test1()throws ClassNotFoundException{ //1.擷取一個系統類別載入器ClassLoaderloader1 =ClassLoader.getSystemClassLoader();System.out.println("系統類別載入器為:"+loader1); //2.擷取系統類別載入器的父類載入器,即擴充類載入器ClassLoaderloader2 = loader1.getParent();System.out.println("擴充類載入器:"+loader2); //3.擷取擴充類載入器的父類載入器,即引導類載入器ClassLoaderloader3 = loader2.getParent();System.out.println("引導類載入器:"+loader3); //4.測試當前類由哪個類載入器進行載入Classclass1 = Person.class;ClassLoaderloader4= class1.getClassLoader();System.out.println("當前類的類載入器:"+loader4); //5.測試JDK提供的String類由哪個類載入器載入StringclassName= "java.lang.String";Classclass2 = Class.forName(className);ClassLoaderloader5= class2.getClassLoader();System.out.println("JDK提供的String類載入器:"+loader5); }
運行結果為:
系統類別載入器為:[email protected]
擴充類載入器:[email protected]
引導類載入器:null
當前類的類載入器:[email protected]
JDK提供的String類載入器:null
由代碼運行結果我們可以看到,我們自己定義的類是由系統類別載入器載入的、系統類別載入器上一級是擴充類載入器、擴充類載入器上一級系統不允許我們拿到,但是擴張類載入器上一級確實是引導類載入器,這類載入器主要是用來載入jvm內建的類庫的。
關於類載入器有時候我們可以用來讀取系統中的設定檔,下面我們就個簡單的小例子,我們將一個Test.properties檔案放到我們的類目錄中,我們用類載入器來讀取其中的內容。
我們設定檔的內容很簡單:
user=root
password=123456
代碼如下:
@Testpublic void test2()throws IOException{//獲得類載入器ClassLoaderclassLoader = this.getClass().getClassLoader(); //運用類載入器讀取property檔案到輸入資料流中StringfilePath = "com\\tgb\\reflect\\test1\\Test.properties";InputStreaminputStream = ClassLoader.getSystemResourceAsStream(filePath); //擷取檔案內容Propertiesproperties =new Properties();properties.load(inputStream);Stringuser=properties.getProperty("user");System.out.println("user:"+user);Stringpassword = properties.getProperty("password");System.out.println("password:"+password); }
運行結果是:
user:root
password:123456
後記
這篇文章介紹了反射的基本知識,還有反射的源頭Class類,並講述了得到Class類對象的四種方式,同時還介紹了類載入器的一些知識,關於反射這篇文章就先介紹這麼多,下篇文章我們繼續。
跟我學Java反射——一步曲