java反射機制的概念是什么及怎么用
今天小編給大家分享一下java反射機制的概念是什么及怎么用的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
一、Java反射機制概述
1. Java Reflection
(1)Reflection(反射)是被視為動態語言的關鍵,反射機制允許程序在執行期 借助于ReflectionAPI取得任何類的內部信息,并能直接操作任意對象的內 部屬性及方法。
(2)加載完類之后,在堆內存的方法區中就產生了一個Class類型的對象(一個類只有一個Class對象),這個對象就包含了完整的類的結構信息。我們可以通過這個對象看到類的結構。這個對象就像一面鏡子,透過這個鏡子看到類的結構,所以,我們形象的稱之為:反射。
2. 動態語言 vs 靜態語言
(1)動態語言
是一類在運行時可以改變其結構的語言:例如新的函數、對象、甚至代碼可以 被引進,已有的函數可以被刪除或是其他結構上的變化。通俗點說就是在運行時代碼可以根據某些條件改變自身結構。
主要動態語言:Object-C、C#、JavaScript、PHP、Python、Erlang。
(2)靜態語言
與動態語言相對應的,運行時結構不可變的語言就是靜態語言。如Java、C、C++。Java不是動態語言,但Java可以稱之為“準動態語言”。即Java有一定的動態性,我們可以利用反射機制、字節碼操作獲得類似動態語言的特性。 Java的動態性讓編程的時候更加靈活!
(3)Java反射機制研究及應用
Java反射機制提供的功能
在運行時判斷任意一個對象所屬的類
在運行時構造任意一個類的對象
在運行時判斷任意一個類所具有的成員變量和方法
在運行時獲取泛型信息 在運行時調用任意一個對象的成員變量和方法
在運行時處理注解 生成動態代理
反射相關的主要API
java.lang.Class:代表一個類
java.lang.reflect.Method:代表類的方法
java.lang.reflect.Field:代表類的成員變量
java.lang.reflect.Constructor:代表類的構造器 ? … …
二、 Class類的理解
1. 類的加載過程
1.1 初步了解
程序經過javac.exe
命令以后,會生成一個或多個字節碼文件(.class
結尾)。
接著我們使用java.exe
命令對某個字節碼文件進行解釋運行。相當于將某個字節碼文件加載到內存中。此過程就稱為類的加載。加載到內存中的類,我們就稱為運行時類,此運行時類,就作為Class
的一個實例。
換句話說,Class
的實例就對應著一個運行時類。
加載到內存中的運行時類,會緩存一定的時間。在此時間之內,我們可以通過不同的方式來獲取此運行時類。
1.2 類的加載過程圖解
當程序主動使用某個類時,如果該類還未被加載到內存中,則系統會通過如下三個步驟來對該類進行初始化。
類的加載:將class
文件字節碼內容加載到內存中,并將這些靜態數據轉換成方法區的運行時數據結構,然后生成一個代表這個類的java.lang.Class
對象,作為方法區中類數據的訪問入口(即引用地址)。所有需要訪問和使用類數據只能通過這個Class對象。這個加載的過程需要類加載器參與。
類的鏈接:將Java類的二進制代碼合并到JVM的運行狀態之中的過程。
● 驗證:確保加載的類信息符合JVM規范,例如:以cafe開頭,沒有安全方面的問題
● 準備:正式為類變量(static
)分配內存并設置類變量默認初始值的階段,這些內存 都將在方法區中進行分配。
● 解析:虛擬機常量池內的符號引用(常量名)替換為直接引用(地址)的過程。
類的初始化:
● 執行類構造器【clinit
】()方法的過程。類構造器【clinit
】()方法是由編譯期自動收集類中 所有類變量的賦值動作和靜態代碼塊中的語句合并產生的。(類構造器是構造類信 息的,不是構造該類對象的構造器)。
● 當初始化一個類的時候,如果發現其父類還沒有進行初始化,則需要先觸發其父類 的初始化。
● 虛擬機會保證一個類的()方法在多線程環境中被正確加鎖和同步。
public?class?ClassLoadingTest?{ public?static?void?main(String[]?args)?{ System.out.println(A.m); }?} class?A?{ static?{?m?=?300; } static?int?m?=?100; } //第二步:鏈接結束后m=0 //第三步:初始化后,m的值由<clinit>()方法執行決定 //?這個A的類構造器<clinit>()方法由類變量的賦值和靜態代碼塊中的語句按照順序合并產生,類似于 //?<clinit>(){ //?m?=?300; //?m?=?100; //?}
1.3 了解:什么時候會發生類初始化?
類的主動引用(一定會發生類的初始化)
當虛擬機啟動,先初始化
main
方法所在的類new
一個類的對象調用類的靜態成員(除了final常量)和靜態方法
使用
java.lang.reflect
包的方法對類進行反射調用當初始化一個類,如果其父類沒有被初始化,則先會初始化它的父類
類的被動引用(不會發生類的初始化)
當訪問一個靜態域時,只有真正聲明這個域的類才會被初始化
當通過子類引用父類的靜態變量,不會導致子類初始化
通過數組定義類引用,不會觸發此類的初始化
引用常量不會觸發此類的初始化(常量在鏈接階段就存入調用類的常量池中了)
1.4 類加載器的作用
類加載的作用:將class文件字節碼內容加載到內存中,并將這些靜態數據轉換成方法區的運行時數據結構,然后在堆中生成一個代表這個類的java.lang.Class對象,作為 方法區中類數據的訪問入口。
類緩存:標準的JavaSE類加載器可以按要求查找類,但一旦某個類被加載到類加載器 中,它將維持加載(緩存)一段時間。不過JVM垃圾回收機制可以回收這些Class對象。
1.5 JVM中不同類型的類的加載器
1.6 代碼演示
不同類型的類的加載器:
?@Test ????public?void?test1(){ ????????//對于自定義類,使用系統類加載器進行加載 ????????ClassLoader?classLoader?=?ClassLoaderTest.class.getClassLoader(); ????????System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2:系統類加載器 ????????//調用系統類加載器的getParent():獲取擴展類加載器 ????????ClassLoader?classLoader1?=?classLoader.getParent(); ????????System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@279f2327:擴展類加載器 ????????//調用擴展類加載器的getParent():無法獲取引導類加載器 ????????//引導類加載器主要負責加載java的核心類庫,無法加載自定義類的。 ????????ClassLoader?classLoader2?=?classLoader1.getParent(); ????????System.out.println(classLoader2);//null ????????ClassLoader?classLoader3?=?String.class.getClassLoader(); ????????System.out.println(classLoader3);//null ????}
使用系統類加載器讀取Properties
配置文件。
?/* ????Properties:用來讀取配置文件。 ?????*/ ????@Test ????public?void?test2()?throws?Exception?{ ????????Properties?pros?=??new?Properties(); ????????//此時的文件默認在當前的module下。 ????????//讀取配置文件的方式一://????????FileInputStream?fis?=?new?FileInputStream("jdbc.properties");//????????FileInputStream?fis?=?new?FileInputStream("src\\jdbc1.properties");//????????pros.load(fis); ????????//讀取配置文件的方式二:使用ClassLoader ????????//配置文件默認識別為:當前module的src下 ????????ClassLoader?classLoader?=?ClassLoaderTest.class.getClassLoader(); ????????InputStream?is?=?classLoader.getResourceAsStream("jdbc1.properties"); ????????pros.load(is); ????????String?user?=?pros.getProperty("user"); ????????String?password?=?pros.getProperty("password"); ????????System.out.println("user?=?"?+?user?+?",password?=?"?+?password); ????}}
2. 何為Class類?
Class
類在Object
類中定義了以下的方法,此方法將被所有子類繼承:
public?final?Class?getClass()
以上的方法返回值的類型是一個Class
類,此類是Java
反射的源頭,實際上所謂反射從程序的運行結果來看也很好理解,即:可以通過對象反射求出類的名稱。
對象照鏡子后可以得到的信息:某個類的屬性、方法和構造器、某個類到底實現了哪些接口。對于每個類而言,JRE
都為其保留一個不變的Class
類型的對象。
一個 Class
對象包含了特定某個結構(class
/interface
/enum
/annotation
/primitivetype
/void
/[]
)的有關信息。
Class
本身也是一個類
Class
對象只能由系統建立對象
一個加載的類在 JVM
中只會有一個Class
實例
一個Class對象對應的是一個加載到JVM
中的一個.class
文件
每個類的實例都會記得自己是由哪個Class
實例所生成
通過Class
可以完整地得到一個類中的所有被加載的結構
Class
類是Reflection
的根源,針對任何你想動態加載、運行的類,唯有先獲得相應的
3. Class類的常用方法方法
方法名 | 功能說明 |
---|---|
static Class forName(String name) |
返回指定類名 name 的 Class 對象 |
Object newInstance() |
調用缺省構造函數,返回該Class 對象的一個實例 |
getName() |
返回此Class對象所表示的實體(類、接口、數組類、基本類型或void )名稱 |
Class getSuperClass() |
返回當前Class 對象的父類的Class 對象 |
Class [] getInterfaces() |
獲取當前Class 對象的接口 |
ClassLoader getClassLoader() |
返回該類的類加載器 |
Class getSuperclass() |
返回表示此Class 所表示的實體的超類的Class |
Constructor[] getConstructors() |
返回一個包含某些Constructor 對象的數組 |
Field[] getDeclaredFields() |
返回Field 對象的一個數組 |
Method getMethod(String name,Class … paramTypes) |
返回一個Method 對象,此對象的形參類型為paramType
|
3. 哪些類型可以有Class對象?
(1)class: 外部類,成員(成員內部類,靜態內部類),局部內部類,匿名內部類
(2)interface:接口
(3)[]:數組
(4)enum:枚舉
(5)annotation:注解@interface
(6)primitive type:基本數據類型
(7)void
三、獲取Class類實例的四種方法
1. 調用運行時類的屬性:.class
前提:若已知具體的類,通過類的class屬性獲取,該方法最為安全可靠, 程序性能最高
示例: Class clazz1 = String.class;
2. 通過運行時類的對象,調用getClass()
前提:已知某個類的實例,調用該實例的getClass()
方法獲取Class
對象
示例:Class clazz = “www.atguigu.com”.getClass();
3.調用Class的靜態方法:forName(String classPath)
前提:已知一個類的全類名,且該類在類路徑下,可通過Class
類的靜態方法forName()
獲取,可能拋出ClassNotFoundException
示例: Class clazz = Class.forName(“java.lang.String”);
4. 使用類的加載器:ClassLoader
示例:ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass(“類的全類名”);
5. 代碼演示
@Testpublic?void?test1()?throws?ClassNotFoundException?{ ????????????//方式一:調用運行時類的屬性:.class ????????????Class?clazz1?=?Person.class; ????????????System.out.println(clazz1);//class?com.jiaying.java1.Person ????????????//方式二:通過運行時類的對象,調用getClass() ????????????Person?p1?=?new?Person(); ????????????Class?clazz2?=?p1.getClass(); ????????????System.out.println(clazz2);//class?com.jiaying.java1.Person ????????????//方式三:調用Class的靜態方法:forName(String?classPath) ????????????Class?clazz3?=?Class.forName("com.jiaying.java1.Person"); ????????????Class?clazz5?=?Class.forName("java.lang.String"); ????????????System.out.println(clazz3);//class?com.jiaying.java1.Person ????????????System.out.println(clazz5);//class?java.lang.String ????????????System.out.println(clazz1?==?clazz2);//true ????????????System.out.println(clazz1?==?clazz3);//true ????????????//方式四:使用類的加載器:ClassLoader??(了解) ????????????ClassLoader?classLoader?=?ReflectionTest.class.getClassLoader(); ????????????Class?clazz4?=?classLoader.loadClass("com.jiaying.java1.Person"); ????????????System.out.println(clazz4);//class?com.jiaying.java1.Person ????????????System.out.println(clazz1?==?clazz4);//true}
四、 創建運行時類的對象
1. 引入
有了Class對象,能做什么?
創建類的對象:調用Class
對象的newInstance()
方法
要求:
類必須有一個無參數的構造器。
類的構造器的訪問權限需要足夠。
難道沒有無參的構造器就不能創建對象了嗎?
不是!只要在操作的時候明確的調用類中的構造器,并將參數傳遞進去之后,才可以實例化操作。
步驟如下:
通過
Class
類的getDeclaredConstructor(Class … parameterTypes)
取得本類的指定形參類型的構造器向構造器的形參中傳遞一個對象數組進去,里面包含了構造器中所需的各個參數。
通過
Constructor
實例化對象。
2. 語法步驟
(1)根據全類名獲取對應的Class
對象
String?name?=?“atguigu.java.Person";Class?clazz?=?null;clazz?=?Class.forName(name);
(2)調用指定參數結構的構造器,生成Constructor
的實例
Constructor?con?=?clazz.getConstructor(String.class,Integer.class);
(3)通過Constructor
的實例創建對應類的對象,并初始化類屬性
Person?p2?=?(Person)?con.newInstance("Peter",20);System.out.println(p2);
3. 代碼演示
?@Test ????public?void?test1()?throws?IllegalAccessException,?InstantiationException?{ ????????Class<Person>?clazz?=?Person.class; ????????/* ????????newInstance():調用此方法,創建對應的運行時類的對象。內部調用了運行時類的空參的構造器。 ????????要想此方法正常的創建運行時類的對象,要求: ????????1.運行時類必須提供空參的構造器 ????????2.空參的構造器的訪問權限得夠。通常,設置為public。 ????????在javabean中要求提供一個public的空參構造器。原因: ????????1.便于通過反射,創建運行時類的對象 ????????2.便于子類繼承此運行時類時,默認調用super()時,保證父類有此構造器 ?????????*/ ????????Person?obj?=?clazz.newInstance(); ????????System.out.println(obj); ????}
4. 體會反射的動態性
//體會反射的動態性 ????@Test ????public?void?test2(){ ????????for(int?i?=?0;i?<?100;i++){ ????????????int?num?=?new?Random().nextInt(3);//0,1,2 ????????????String?classPath?=?""; ????????????switch(num){ ????????????????case?0: ????????????????????classPath?=?"java.util.Date"; ????????????????????break; ????????????????case?1: ????????????????????classPath?=?"java.lang.Object"; ????????????????????break; ????????????????case?2: ????????????????????classPath?=?"com.atguigu.java.Person"; ????????????????????break; ????????????} ????????????try?{ ????????????????Object?obj?=?getInstance(classPath); ????????????????System.out.println(obj); ????????????}?catch?(Exception?e)?{ ????????????????e.printStackTrace(); ????????????} ????????} ????} ????/* ????創建一個指定類的對象。 ????classPath:指定類的全類名 ?????*/ ????public?Object?getInstance(String?classPath)?throws?Exception?{ ???????Class?clazz?=??Class.forName(classPath); ???????return?clazz.newInstance(); ????}}
五、獲取運行時類的完整結構
提供具有豐富內容的Person
類
//接口public?interface?MyInterface?{ ????void?info();}//注解@Target({TYPE,?FIELD,?METHOD,?PARAMETER,?CONSTRUCTOR,?LOCAL_VARIABLE})@Retention(RetentionPolicy.RUNTIME)public?@interface?MyAnnotation?{ ????String?value()?default?"hello";}//父類public?class?Creature<T>?implements?Serializable?{ ????private?char?gender; ????public?double?weight; ????private?void?breath(){ ????????System.out.println("生物呼吸"); ????} ????public?void?eat(){ ????????System.out.println("生物吃東西"); ????}}//Person類@MyAnnotation(value="hi")public?class?Person?extends?Creature<String>?implements?Comparable<String>,MyInterface{ ????private?String?name; ????int?age; ????public?int?id; ????public?Person(){} ????@MyAnnotation(value="abc") ????private?Person(String?name){ ????????this.name?=?name; ????} ?????Person(String?name,int?age){ ????????this.name?=?name; ????????this.age?=?age; ????} ????@MyAnnotation ????private?String?show(String?nation){ ????????System.out.println("我的國籍是:"?+?nation); ????????return?nation; ????} ????public?String?display(String?interests,int?age)?throws?NullPointerException,ClassCastException{ ????????return?interests?+?age; ????} ????@Override ????public?void?info()?{ ????????System.out.println("我是一個人"); ????} ????@Override ????public?int?compareTo(String?o)?{ ????????return?0; ????} ????private?static?void?showDesc(){ ????????System.out.println("我是一個可愛的人"); ????} ????@Override ????public?String?toString()?{ ????????return?"Person{"?+ ????????????????"name='"?+?name?+?'\''?+ ????????????????",?age="?+?age?+ ????????????????",?id="?+?id?+ ????????????????'}'; ????}}
1. 獲取當前運行時類的屬性結構
方法 | 作用 |
---|---|
public Field[] getFields() |
返回此Class 對象所表示的類或接口的public 的Field
|
public Field[] getDeclaredFields() |
返回此Class 對象所表示的類或接口的全部Field
|
Field方法中:
方法 | 作用 |
---|---|
public int getModifiers() |
以整數形式返回此Field 的修飾符 |
public Class<?> getType() |
得到Field 的屬性類型 |
public String getName() |
返回Field 的名稱 |
????@Test ????public?void?test1(){ ????????Class?clazz?=?Person.class; ????????//獲取屬性結構 ????????//getFields():獲取當前運行時類及其父類中聲明為public訪問權限的屬性 ????????Field[]?fields?=?clazz.getFields(); ????????for(Field?f?:?fields){ ????????????System.out.println(f); ????????} ????????System.out.println(); ????????//getDeclaredFields():獲取當前運行時類中聲明的所有屬性。(不包含父類中聲明的屬性) ????????Field[]?declaredFields?=?clazz.getDeclaredFields(); ????????for(Field?f?:?declaredFields){ ????????????System.out.println(f); ????????} ????} ????//權限修飾符??數據類型?變量名 ????@Test ????public?void?test2(){ ????????Class?clazz?=?Person.class; ????????Field[]?declaredFields?=?clazz.getDeclaredFields(); ????????for(Field?f?:?declaredFields){ ????????????//1.權限修飾符 ????????????int?modifier?=?f.getModifiers(); ????????????System.out.print(Modifier.toString(modifier)?+?"\t"); ????????????//2.數據類型 ????????????Class?type?=?f.getType(); ????????????System.out.print(type.getName()?+?"\t"); ????????????//3.變量名 ????????????String?fName?=?f.getName(); ????????????System.out.print(fName); ????????????System.out.println(); ????????} ????}}
2. 獲取當前運行時類的方法結構
方法 | 作用 |
---|---|
public Method[] getMethods() |
返回此Class 對象所表示的類或接口的public 的方法 |
public Method[] getDeclaredMethods() |
返回此Class 對象所表示的類或接口的全部方法 |
Method類中:
方法 | 作用 |
---|---|
public Class<?> getReturnType() |
取得全部的返回值 |
public Class<?>[] getParameterTypes() |
取得全部的參數 |
public int getModifiers() |
取得修飾符 |
public Class<?>[] getExceptionTypes() |
取得異常信息 |
????@Test ????public?void?test1(){ ????????Class?clazz?=?Person.class; ????????//getMethods():獲取當前運行時類及其所有父類中聲明為public權限的方法 ????????Method[]?methods?=?clazz.getMethods(); ????????for(Method?m?:?methods){ ????????????System.out.println(m); ????????} ????????System.out.println(); ????????//getDeclaredMethods():獲取當前運行時類中聲明的所有方法。(不包含父類中聲明的方法) ????????Method[]?declaredMethods?=?clazz.getDeclaredMethods(); ????????for(Method?m?:?declaredMethods){ ????????????System.out.println(m); ????????} ????} ??/* ????@Xxxx ????權限修飾符??返回值類型??方法名(參數類型1?形參名1,...)?throws?XxxException{} ?????*/ ????@Test ????public?void?test2(){ ????????Class?clazz?=?Person.class; ????????Method[]?declaredMethods?=?clazz.getDeclaredMethods(); ????????for(Method?m?:?declaredMethods){ ????????????//1.獲取方法聲明的注解 ????????????Annotation[]?annos?=?m.getAnnotations(); ????????????for(Annotation?a?:?annos){ ????????????????System.out.println(a); ????????????} ????????????//2.權限修飾符 ????????????System.out.print(Modifier.toString(m.getModifiers())?+?"\t"); ????????????//3.返回值類型 ????????????System.out.print(m.getReturnType().getName()?+?"\t"); ????????????//4.方法名 ????????????System.out.print(m.getName()); ????????????System.out.print("("); ????????????//5.形參列表 ????????????Class[]?parameterTypes?=?m.getParameterTypes(); ????????????if(!(parameterTypes?==?null?&&?parameterTypes.length?==?0)){ ????????????????for(int?i?=?0;i?<?parameterTypes.length;i++){ ????????????????????if(i?==?parameterTypes.length?-?1){ ????????????????????????System.out.print(parameterTypes[i].getName()?+?"?args_"?+?i); ????????????????????????break; ????????????????????} ????????????????????System.out.print(parameterTypes[i].getName()?+?"?args_"?+?i?+?","); ????????????????} ????????????} ????????????System.out.print(")"); ????????????//6.拋出的異常 ????????????Class[]?exceptionTypes?=?m.getExceptionTypes(); ????????????if(exceptionTypes.length?>?0){ ????????????????System.out.print("throws?"); ????????????????for(int?i?=?0;i?<?exceptionTypes.length;i++){ ????????????????????if(i?==?exceptionTypes.length?-?1){ ????????????????????????System.out.print(exceptionTypes[i].getName()); ????????????????????????break; ????????????????????} ????????????????????System.out.print(exceptionTypes[i].getName()?+?","); ????????????????} ????????????} ????????????System.out.println(); ????????} ????}}
3. 獲取當前運行時類的構造器結構
方法 | 作用 |
---|---|
public Constructor<T>[] getConstructors() |
返回此 Class 對象所表示的類的所有public 構造方法。 |
public Constructor<T>[] getDeclaredConstructors() |
返回此 Class 對象表示的類聲明的所有構造方法。 |
Constructor類中:
方法 | 作用 |
---|---|
public int getModifiers() |
取得修飾符 |
public String getName() |
取得方法名稱 |
public Class<?>[] getParameterTypes() |
取得參數的類型 |
/* ????獲取構造器結構 ?????*/ ????@Test ????public?void?test1(){ ????????Class?clazz?=?Person.class; ????????//getConstructors():獲取當前運行時類中聲明為public的構造器 ????????Constructor[]?constructors?=?clazz.getConstructors(); ????????for(Constructor?c?:?constructors){ ????????????System.out.println(c); ????????} ????????System.out.println(); ????????//getDeclaredConstructors():獲取當前運行時類中聲明的所有的構造器 ????????Constructor[]?declaredConstructors?=?clazz.getDeclaredConstructors(); ????????for(Constructor?c?:?declaredConstructors){ ????????????System.out.println(c); ????????} ????} ?/* ????獲取運行時類的父類 ?????*/ ????@Test ????public?void?test2(){ ????????Class?clazz?=?Person.class; ????????Class?superclass?=?clazz.getSuperclass(); ????????System.out.println(superclass); ????} ????/* ????獲取運行時類的帶泛型的父類 ?????*/ ????@Test ????public?void?test3(){ ????????Class?clazz?=?Person.class; ????????Type?genericSuperclass?=?clazz.getGenericSuperclass(); ????????System.out.println(genericSuperclass); ????} ????/* ????獲取運行時類的帶泛型的父類的泛型 ????代碼:邏輯性代碼??vs?功能性代碼 ?????*/ ????@Test ????public?void?test4(){ ????????Class?clazz?=?Person.class; ????????Type?genericSuperclass?=?clazz.getGenericSuperclass(); ????????ParameterizedType?paramType?=?(ParameterizedType)?genericSuperclass; ????????//獲取泛型類型 ????????Type[]?actualTypeArguments?=?paramType.getActualTypeArguments();//????????System.out.println(actualTypeArguments[0].getTypeName()); ????????System.out.println(((Class)actualTypeArguments[0]).getName()); ????}/* ????獲取運行時類實現的接口 ?????*/ ????@Test ????public?void?test5(){ ????????Class?clazz?=?Person.class; ????????Class[]?interfaces?=?clazz.getInterfaces(); ????????for(Class?c?:?interfaces){ ????????????System.out.println(c); ????????} ????????System.out.println(); ????????//獲取運行時類的父類實現的接口 ????????Class[]?interfaces1?=?clazz.getSuperclass().getInterfaces(); ????????for(Class?c?:?interfaces1){ ????????????System.out.println(c); ????????} ????} ????/* ????????獲取運行時類所在的包 ?????*/ ????@Test ????public?void?test6(){ ????????Class?clazz?=?Person.class; ????????Package?pack?=?clazz.getPackage(); ????????System.out.println(pack); ????} ????/* ????????獲取運行時類聲明的注解 ?????*/ ????@Test ????public?void?test7(){ ????????Class?clazz?=?Person.class; ????????Annotation[]?annotations?=?clazz.getAnnotations(); ????????for(Annotation?annos?:?annotations){ ????????????System.out.println(annos); ????????} ????}}
六、調用運行時類的指定結構
關于setAccessible方法的使用
Method
和Field
、Constructor
對象都有setAccessible()
方法。
setAccessible
啟動和禁用訪問安全檢查的開關。
參數值為true
則指示反射的對象在使用時應該取消Java語言訪問檢查。
提高反射的效率。如果代碼中必須用反射,而該句代碼需要頻繁的被 調用,那么請設置為true
,使得原本無法訪問的私有成員也可以訪問,參數值為false
則指示反射的對象應該實施Java語言訪問檢查。
1. 調用運行時類中指定的屬性
在反射機制中,可以直接通過Field
類操作類中的屬性,通過Field
類提供的set()
和 get()
方法就可以完成設置和取得屬性內容的操作。
方法 | 作用 |
---|---|
public Field getField(String name) |
返回此Class 對象表示的類或接口的指定的public 的Field
|
public Field getDeclaredField(String name) |
返回此Class 對象表示的類或接口的指定的Field
|
在Field中:
方法 | 作用 |
---|---|
public Object get(Object obj) |
取得指定對象obj 上此Field 的屬性內容 |
public void set(Object obj,Object value) |
設置指定對象obj 上此Field 的屬性內容 |
代碼演示:
public?class?ReflectionTest?{ ????@Test ????public?void?testField()?throws?Exception?{ ????????Class?clazz?=?Person.class; ????????//創建運行時類的對象 ????????Person?p?=?(Person)?clazz.newInstance(); ????????//獲取指定的屬性:要求運行時類中屬性聲明為public ????????//通常不采用此方法 ????????Field?id?=?clazz.getField("id"); ????????/* ????????設置當前屬性的值 ????????set():參數1:指明設置哪個對象的屬性???參數2:將此屬性值設置為多少 ?????????*/ ????????id.set(p,1001); ????????/* ????????獲取當前屬性的值 ????????get():參數1:獲取哪個對象的當前屬性值 ?????????*/ ????????int?pId?=?(int)?id.get(p); ????????System.out.println(pId); ????} ????/* ????如何操作運行時類中的指定的屬性?--?需要掌握 ?????*/ ????@Test ????public?void?testField1()?throws?Exception?{ ????????Class?clazz?=?Person.class; ????????//創建運行時類的對象 ????????Person?p?=?(Person)?clazz.newInstance(); ????????//1.?getDeclaredField(String?fieldName):獲取運行時類中指定變量名的屬性 ????????Field?name?=?clazz.getDeclaredField("name"); ????????//2.保證當前屬性是可訪問的 ????????name.setAccessible(true); ????????//3.獲取、設置指定對象的此屬性值 ????????name.set(p,"Tom"); ????????System.out.println(name.get(p)); ????}
2. 調用運行時類中的指定的方法
通過反射,調用類中的方法,通過Method
類完成。步驟:
通過
Class
類的getMethod(String name,Class…parameterTypes)
方法取得 一個Method
對象,并設置此方法操作時所需要的參數類型。之后使用
Object invoke(Object obj, Object[] args)
進行調用,并向方法中 傳遞要設置的obj
對象的參數信息。
Object invoke(Object obj, Object … args)
說明:Object
對應原方法的返回值,若原方法無返回值,此時返回null
若原方法若為靜態方法,此時形參Object obj
可為null
若原方法形參列表為空,則Object[] args
為null
若原方法聲明為private
,則需要在調用此invoke()
方法前,顯式調用 方法對象的setAccessible(true)
方法,將可訪問private
的方法。
代碼演示:
?/* ????如何操作運行時類中的指定的方法?--?需要掌握 ?????*/ ????@Test ????public?void?testMethod()?throws?Exception?{ ????????Class?clazz?=?Person.class; ????????//創建運行時類的對象 ????????Person?p?=?(Person)?clazz.newInstance(); ????????/* ????????1.獲取指定的某個方法 ????????getDeclaredMethod():參數1?:指明獲取的方法的名稱??參數2:指明獲取的方法的形參列表 ?????????*/ ????????Method?show?=?clazz.getDeclaredMethod("show",?String.class); ????????//2.保證當前方法是可訪問的 ????????show.setAccessible(true); ????????/* ????????3.?調用方法的invoke():參數1:方法的調用者??參數2:給方法形參賦值的實參 ????????invoke()的返回值即為對應類中調用的方法的返回值。 ?????????*/ ????????Object?returnValue?=?show.invoke(p,"CHN");?//String?nation?=?p.show("CHN"); ????????System.out.println(returnValue); ????????System.out.println("*************如何調用靜態方法*****************"); ????????//?private?static?void?showDesc() ????????Method?showDesc?=?clazz.getDeclaredMethod("showDesc"); ????????showDesc.setAccessible(true); ????????//如果調用的運行時類中的方法沒有返回值,則此invoke()返回null//????????Object?returnVal?=?showDesc.invoke(null); ????????Object?returnVal?=?showDesc.invoke(Person.class); ????????System.out.println(returnVal);//null ????}
3. 調用運行時類中的指定的構造器
代碼演示:
??/* ????如何調用運行時類中的指定的構造器 ?????*/ ????@Test ????public?void?testConstructor()?throws?Exception?{ ????????Class?clazz?=?Person.class; ????????//private?Person(String?name) ????????/* ????????1.獲取指定的構造器 ????????getDeclaredConstructor():參數:指明構造器的參數列表 ?????????*/ ????????Constructor?constructor?=?clazz.getDeclaredConstructor(String.class); ????????//2.保證此構造器是可訪問的 ????????constructor.setAccessible(true); ????????//3.調用此構造器創建運行時類的對象 ????????Person?per?=?(Person)?constructor.newInstance("Tom"); ????????System.out.println(per); ????}}
以上就是“java反射機制的概念是什么及怎么用”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注蝸牛博客行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:niceseo99@gmail.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。
評論