[Java基础] 反射机制汇总
引言
初學(xué)Java反射機(jī)制的時候,只是感覺很神奇,但是不知道學(xué)了該怎么用,所以過了一段時間就忘得差不多了;最近接觸到了框架,在學(xué)習(xí)中遇到了反射,深深體會到了反射機(jī)制的神奇,回來復(fù)習(xí)了一下反射機(jī)制,寫一個小總結(jié);
概念
反射機(jī)制是什么?百度百科很好的解釋了:
Java的反射(reflection)機(jī)制是指在程序的運(yùn)行狀態(tài)中,可以構(gòu)造任意一個類的對象,可以了解任意一個對象所屬的類,可以了解任意一個類的成員變量和方法,可以調(diào)用任意一個對象的屬性和方法。
這種動態(tài)獲取程序信息以及動態(tài)調(diào)用對象的功能稱為Java語言的反射機(jī)制。反射被視為動態(tài)語言的關(guān)鍵。
在這里我也就不細(xì)說概念了,在使用中你就可以慢慢明白了;
下面我來展示一下Java反射機(jī)制的基本操作;
注:為了使代碼簡明易看,所有的異常處理直接拋出Exception(實(shí)際應(yīng)用不要這樣!!)
獲取類型
在Java中有一種類型叫做Class,這是一種類的類型,和String、Object一個意思,所以不要和類弄混了;
我們可以通過反射機(jī)制獲取一個類的類型,返回值類型就是Class
獲取類型有三種方法:
- Class靜態(tài)方法forName()
- 對象.getClass
- 類名.class
下面是代碼演示:
// 獲取不同類型(Class)的三種方式:Class靜態(tài)方法forName、對象.getClass()、類名.class // Class也是一個類類型,和String、Object一樣 // java.lang.Class:Class代表整個字節(jié)碼,代表一個類型,代表整個類 public class ReflectTest01 {public static void main(String[] args) throws Exception {// 第一種:通過Class的靜態(tài)方法:forName獲取Class類型Class c1 = Class.forName("java.lang.String"); // String類型Class c2 = Class.forName("java.lang.Object"); // Object類型System.out.println(c1 == c2); // String類型和Object類型不同, false//==============================================================// 第二種:(對象.getClass())java中任何一個對象都有這個方法:getClass(),返回值類型是Class類型Class c3 = "444".getClass(); // c5是String類型Class c4 = new Object().getClass(); // c6是Object類型System.out.println(c1 == c3); // trueSystem.out.println(c2 == c4); // true//=========================================================// 第三種:(類名.class)java中任何一種類型,包括基本數(shù)據(jù)類型,都有一個.class屬性Class c5 = String.class; // String類型Class c6 = Object.class; // Object類型System.out.println(c1 == c5); // trueSystem.out.println(c2 == c6); // true} }這三種方法中主要留意的就是第一種,forName()方法的使用規(guī)則是:
這里需要記住forName()方法可以使對應(yīng)的類加載,類加載就會執(zhí)行靜態(tài)代碼塊;
所以如果只想執(zhí)行靜態(tài)代碼塊,那么可以使用forName()方法;
為什么說這一點(diǎn)呢?因?yàn)?strong>JDBC注冊驅(qū)動中將會用到這一點(diǎn);
下面來展示一下這個特性:
forName()傳入的類:
輸出:
當(dāng)然這里只是簡單模擬了一下,但是原理都是一樣的;
結(jié)論:如果只想要一個類的靜態(tài)代碼塊執(zhí)行,其余代碼都不執(zhí)行,可以使用:Class.forName(“完整類名”)
實(shí)例化對象
當(dāng)獲取到對象類型后就可以通過該類型實(shí)例化對應(yīng)的對象,用到newInstance()方法
在框架一般都是需要通過反射機(jī)制獲取配置文件內(nèi)容并進(jìn)行操作的,通過配置文件內(nèi)容可以獲取不同信息,下面演示一下如何通過配置文件實(shí)例化對象;
注:這里使用.properties文件,簡單易懂
創(chuàng)建一個.properties文件:
這個配置文件需要放到該項(xiàng)目的目錄下,不能放到類路徑(src)或者其他目錄下;
import java.io.FileInputStream; import java.util.Properties;// 通過反射機(jī)制獲取配置文件內(nèi)容并實(shí)例化對象 // 反射機(jī)制讓程序更加靈活,符合OPC開閉原則 // 高級框架(Spring等)底層原理都是反射機(jī)制 public class ReflectTest02 {public static void main(String[] args) throws Exception{FileInputStream fis = new FileInputStream("classname.properties"); // 創(chuàng)建輸入流Properties properties = new Properties(); // 底層實(shí)現(xiàn)是Map,繼承了HashTable,key和Value都是String類型properties.load(fis); // 加載Java的配置文件classname.properties到內(nèi)存中fis.close(); // 關(guān)閉輸入流String classname = properties.getProperty("first"); // 通過key得到ValueClass<?> clazz = Class.forName(classname); // 獲取classname(Value值)的類型Object obj = clazz.newInstance(); // 創(chuàng)建對象實(shí)例System.out.println(obj);} }因?yàn)檫@里的key傳入的是first,所以輸出為:
可以看出實(shí)例化了一個Object對象
還有另外一種方法,是比較常用簡單的方法:
java.util包下提供了一個資源綁定器,便于獲取屬性配置文件中的內(nèi)容;
需要使用該類的兩個方法:
創(chuàng)建一個配置文件:
使用這種方式的時候,配置文件必須放到類路徑(src)下
import java.util.ResourceBundle;// 使用以下這種方式的時候,屬性配置文件xxx.properties必須放到類路徑下。 // 實(shí)現(xiàn)和ReflectTest02一樣的功能,但是又通過新的獲取絕對路徑的方法,而且以后常用這個方法 public class ReflectTest05 {public static void main(String[] args) throws Exception {// 資源綁定器,只能綁定xxx.properties文件。并且這個文件必須在類路徑(src文件夾)下。文件擴(kuò)展名也必須是properties// 并且在寫路徑的時候,路徑后面的擴(kuò)展名.properties不能寫。ResourceBundle bundle = ResourceBundle.getBundle("absolute_path"); // 綁定配置文件String classname = bundle.getString("second"); // 通過key得到ValueClass c = Class.forName(classname);Object obj = c.newInstance();System.out.println(obj);} }這次key傳入的是second,輸出為:
反射機(jī)制的一些操作
下面展現(xiàn)反射機(jī)制的一些操作,其實(shí)就是各種方法的調(diào)用,會查文檔就好;
這里先定義一個類,下面所有的操作都基于這個類;
反射TestClass類中所有Field(屬性)
import java.lang.reflect.Field; import java.lang.reflect.Modifier;public class ReflectTest06 {public static void main(String[] args) throws Exception {// 獲取整個類Class testClass = Class.forName("reflect.test06.TestClass01");// 獲取類名String classname = testClass.getName(); // 完整類名System.out.println(classname);System.out.println(testClass.getSimpleName()); // 簡單類名// 獲取TestClass類的屬性// getFields()只會獲取所有public修飾的屬性Field[] fields01 = testClass.getFields();Field f = fields01[0];String filename = f.getName(); // 獲取屬性名System.out.println(filename);System.out.println("=====================");// 獲取所有屬性Field[] fields02 = testClass.getDeclaredFields();for (Field i : fields02) {System.out.println(i.getName()); // 獲取屬性名// 獲取屬性的類型System.out.println(i.getType().getName()); // 完整類型System.out.println(i.getType().getSimpleName()); // 簡單類型// 獲取修飾符(用到了Modifier類的toString方法)System.out.println(Modifier.toString(i.getModifiers()));System.out.println("------------");}} }輸出:
reflect.test06.TestClass01 TestClass01 name ===================== age int int------------ name java.lang.String String public ------------ num int int private static final ------------ flag boolean boolean protected static ------------獲取對象屬性中的值
import java.lang.reflect.Field;public class ReflectTest07 {public static void main(String[] args) throws Exception {Class className = Class.forName("reflect.test06.TestClass01"); // 獲取類Object obj = className.newInstance(); // 通過類創(chuàng)建對象Field ageFiled = className.getDeclaredField("age"); // 獲取age屬性// 給屬性賦值需要有實(shí)例化的對象objageFiled.set(obj, 111); // 給obj對象的age屬性復(fù)制111System.out.println(ageFiled.get(obj)); // 獲取obj對象的age屬性的值} }輸出:
獲取方法并調(diào)用(重要)
這個相對重要,獲取方法用到了Class類的getMethod()方法或者getMethods()
返回類型Method:
同樣獲取方法后需要調(diào)用,使用Method類的一個方法:invoke()
下面是具體使用方法:
輸出:
總結(jié)
這是反射機(jī)制一些基本常用操作,但是實(shí)際應(yīng)用并不會這么簡單,所以基礎(chǔ)的一定要掌握好,這樣在以后學(xué)習(xí)spring等框架時就能看懂學(xué)習(xí)底層源碼了;
歡迎大家的點(diǎn)評!
總結(jié)
以上是生活随笔為你收集整理的[Java基础] 反射机制汇总的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构课程设计——机票售卖系统(C++
- 下一篇: [小技巧]PicGo、Gitee和Typ