Java開發(fā)崗位面試被問到反射怎么辦
到底什么是反射呢???
反射的核心就是JVM在運行時才動態(tài)加載類或調(diào)用方法,訪問屬性,它不需要事先(寫代碼的時候或編譯期)知道運行對象是誰。
每一個類都會產(chǎn)生一個對應(yīng)的Class對象,也就是保存在.class文件。
所有類都是在對其第一次使用時,動態(tài)加載到JVM的,當(dāng)程序創(chuàng)建一個對類的靜態(tài)成員的引用時,就會加載這個類,Class對象僅在需要的時候才會加載,static初始化是在類加載時進行的。
public class TestMain { public static void main(String[] args) { System.out.println(Test.name); // 對Test類的靜態(tài)成員name引用。 } } class Test { public static String name = "Test Name"; static { System.out.println("Test靜態(tài)塊"); } public Test() { System.out.println("Test構(gòu)造了"); } }
輸出:
Test靜態(tài)塊 Test Name
2. 類的生命周期
一個類編譯完成后,下一步就是開始使用類,怎么使用?
類編譯完成后,開始使用類,在程序執(zhí)行中JVM通過裝載,鏈接,初始化這3個步驟完成。
1.裝載:由類加載器完成,找到對應(yīng)的字節(jié)碼,創(chuàng)建一個Class對象。
類加載器首先會檢查這個類的Class對象是否已經(jīng)被加載過,如果沒有加載,默認的類加載器就會根據(jù)類名查找對應(yīng)的.class文件。
加載器將.class文件的二進制文件裝入JVM的方法區(qū),并且在堆區(qū)創(chuàng)建描述這個類的java.lang.Class對象,用來封裝數(shù)據(jù),但是同一個類只會被類裝載器裝載一次。
2.鏈接:就是把二進制數(shù)據(jù)組裝為可以運行的狀態(tài)
校驗:一般用來確認此二進制文件是否適合當(dāng)前的JVM(版本)
準(zhǔn)備:為靜態(tài)成員分配內(nèi)存空間,并設(shè)置默認值。
解析:轉(zhuǎn)換常量池中的代碼作為直接引用的過程,直到所有的符號都可以被運行程序使用(建立完整的對應(yīng)關(guān)系)驗證類中的字節(jié)碼,為靜態(tài)域分配空間。
3.初始化:如果該類有父類,則對其初始化,執(zhí)行靜態(tài)初始化器和靜態(tài)初始化塊。
3. Java反射框架主要提供以下功能:
在運行時構(gòu)造任意一個類的對象
在運行時調(diào)用任意一個對象的方法
在運行時判斷任意一個對象所屬的類
在運行時判斷任意一個類所具有的成員變量和方法(通過反射甚至可以調(diào)用private方法)
反射的基本用法
1. 獲得Class對象
<1> 使用Class類的forName靜態(tài)方法:
public static Class<?> forName(String className) 在JDBC開發(fā)中常用此方法加載數(shù)據(jù)庫驅(qū)動: Class.forName(driver); //加入Java開發(fā)交流君樣:756584822一起吹水聊天
<2> 直接獲取某一個對象的class:(編譯時已知類型名稱或已知對象)
Class<?> klass = int.class; Class<?> classInt = Integer.TYPE;
<3> 調(diào)用某個對象的getClass()方法:
StringBuilder str = new StringBuilder("123"); Class<?> klass = str.getClass();
注意: 使用.class來創(chuàng)建Class對象的引用時,不會自動初始化該Class對象,使用forName(…)會自動初始化該Class對象。
2. 判斷是否為某個類的實類
一般:用instanceof關(guān)鍵字判斷
反射:反射中Class對象的isInstance()方法
public native boolean isInstance(Object obj);
3.創(chuàng)建實例
通過反射來生成對象主要有兩種方式:
<1> 使用Class對象的newInstance()方法來創(chuàng)建Class對象對應(yīng)類的實例。
Class<?> c = String.class; Object str = c.newInstance();
<2> 先通過Class對象獲取指定的Constructor對象,再調(diào)用Constructor對象的newInstance()方法來創(chuàng)建實例。
// 獲取String所對應(yīng)的Class對象 Class<?> c = String.class; // 獲取String類帶一個String參數(shù)的構(gòu)造器 Constructor constructor = c.getConstructor(String.class); // 根據(jù)構(gòu)造器創(chuàng)建實例 Object obj = constructor.newInstance("23333"); System.out.println(obj);
4. 獲取構(gòu)造器信息
主要是通過Class類的getConstructor方法得到Constructor類的一個實例,而Constructor類有一個newInstance方法可以創(chuàng)建一個對象實例
5. 獲取方法
<1> getDeclaredMethods()
– 返回類或接口聲明的所有方法,包括公共、保護、默認(包)訪問和私有方法,但不包括繼承的方法
public Method[] getDeclaredMethods() throws SecurityException
<2> getMethods()
– 返回某個類的所有公共(public)方法,包括繼承的公有方法
public Method[] getMethods() throws SecurityException
<3> getDeclaredMethod()
– 返回一個特定的方法,其中第一個參數(shù)為方法名稱,后面的參數(shù)為方法的參數(shù)類型對應(yīng)的Class對象
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
<4> getMethod()
– 返回一個特定的方法,其中第一個參數(shù)為方法名稱,后面的參數(shù)為方法的參數(shù)類型對應(yīng)的Class對象
public Method getMethod(String name, Class<?>... parameterTypes)
6. 獲取類的成員變量(字段)信息
getDeclaredFields()
– 訪問所有已聲明的成員變量,但不能訪問繼承的成員變量。
getFileds()
– 訪問所有已聲明的公有(public)成員變量,包括繼承的公有成員變量。
getDeclaredField()
– 特定訪問所有成員變量(不包括繼承的),參數(shù)為成員變量的名字。
getFiled()
– 特定訪問公有成員變量(包括繼承的),參數(shù)為成員變量的名字。
7. 利用反射創(chuàng)建數(shù)組
Class<?> cls = Class.forName("java.lang.String"); Object array = Array.newInstance(cls, 25); //往數(shù)組里添加內(nèi)容 Array.set(array, 0, "hello"); Array.set(array, 1, "Java"); Array.set(array, 2, "fuck"); Array.set(array, 3, "Scala"); Array.set(array, 4, "Clojure"); //獲取某一項的內(nèi)容 System.out.println(Array.get(array,3)); //加入Java開發(fā)交流君樣:756584822一起吹水聊天
其中的Array類為java.lang.reflect.Array
類,我們通過Array.newInstance()
創(chuàng)建數(shù)組對象,它的原型是:
public static Object newInstance(Class<?> componentType, int length) throws NegativeArraySizeException { return newArray(componentType, length); }
newArray()方法是一個Native方法:
private static native Object newArray(Class<?> componentType, int length) throws NegativeArraySizeException;
反射的注意事項
由于反射會額外消耗一定的系統(tǒng)資源,因此如果不需要動態(tài)地創(chuàng)建一個對象,那么就不需要用反射。反射調(diào)用方法時可以忽略權(quán)限檢查,因此可能會破壞封裝性而導(dǎo)致安全問題。
反射的主要用途
最重要的用途就是開發(fā)各種通用框架
很多框架(比如Spring)都是配置化的(比如通過XML文件配置JavaBean,Action之類的),為了保證框架的通用性,它們可能需要根據(jù)配置文件加載不同的對象或類,調(diào)用不同的方法,這個時候就必須用到反射——運行時動態(tài)加載需要加載的對象。
總結(jié)
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Java錯誤org.apache.ibatis.binding.BindingException: Inval
本文主要介紹了Java錯誤org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.sjks.mapper.Use,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06SpringMVC 方法四種類型返回值總結(jié)(你用過幾種)
這篇文章主要介紹了SpringMVC 方法四種類型返回值總結(jié)(你用過幾種),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-05-05圖解Eclipse j2ee開發(fā)環(huán)境的搭建過程
這篇文章以圖文結(jié)合的方式介紹了Eclipse j2ee開發(fā)環(huán)境的搭建過程,內(nèi)容很詳細,每一個步驟都有對應(yīng)的操作截圖,需要的朋友可以參考下2015-08-08springboot與vue實現(xiàn)簡單的CURD過程詳析
這篇文章主要介紹了springboot與vue實現(xiàn)簡單的CURD過程詳析,圍繞springboot與vue的相關(guān)資料展開實現(xiàn)CURD過程的過程介紹,需要的小伙伴可以參考一下2022-01-01spring-boot報錯javax.servlet.http不存在的問題解決
當(dāng)springboot項目從2.7.x的升級到3.0.x的時候,會遇到j(luò)avax.servlet.http不存在,本文就來介紹一下這個問題的解決,具有一定的參考價值,感興趣的可以了解一下2024-06-06