今天小編給大家分享一下java反射機(jī)制的概念是什么及怎么用的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識(shí),所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
為鳩江等地區(qū)用戶提供了全套網(wǎng)頁(yè)設(shè)計(jì)制作服務(wù),及鳩江網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作、鳩江網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!
(1)Reflection(反射)是被視為動(dòng)態(tài)語(yǔ)言的關(guān)鍵,反射機(jī)制允許程序在執(zhí)行期 借助于ReflectionAPI取得任何類的內(nèi)部信息,并能直接操作任意對(duì)象的內(nèi) 部屬性及方法。
(2)加載完類之后,在堆內(nèi)存的方法區(qū)中就產(chǎn)生了一個(gè)Class類型的對(duì)象(一個(gè)類只有一個(gè)Class對(duì)象),這個(gè)對(duì)象就包含了完整的類的結(jié)構(gòu)信息。我們可以通過這個(gè)對(duì)象看到類的結(jié)構(gòu)。這個(gè)對(duì)象就像一面鏡子,透過這個(gè)鏡子看到類的結(jié)構(gòu),所以,我們形象的稱之為:反射。
(1)動(dòng)態(tài)語(yǔ)言
是一類在運(yùn)行時(shí)可以改變其結(jié)構(gòu)的語(yǔ)言:例如新的函數(shù)、對(duì)象、甚至代碼可以 被引進(jìn),已有的函數(shù)可以被刪除或是其他結(jié)構(gòu)上的變化。通俗點(diǎn)說就是在運(yùn)行時(shí)代碼可以根據(jù)某些條件改變自身結(jié)構(gòu)。
主要?jiǎng)討B(tài)語(yǔ)言:Object-C、C#、JavaScript、PHP、Python、Erlang。
(2)靜態(tài)語(yǔ)言
與動(dòng)態(tài)語(yǔ)言相對(duì)應(yīng)的,運(yùn)行時(shí)結(jié)構(gòu)不可變的語(yǔ)言就是靜態(tài)語(yǔ)言。如Java、C、C++。Java不是動(dòng)態(tài)語(yǔ)言,但Java可以稱之為“準(zhǔn)動(dòng)態(tài)語(yǔ)言”。即Java有一定的動(dòng)態(tài)性,我們可以利用反射機(jī)制、字節(jié)碼操作獲得類似動(dòng)態(tài)語(yǔ)言的特性。 Java的動(dòng)態(tài)性讓編程的時(shí)候更加靈活!
(3)Java反射機(jī)制研究及應(yīng)用
Java反射機(jī)制提供的功能
在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類
在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象
在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法
在運(yùn)行時(shí)獲取泛型信息 在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的成員變量和方法
在運(yùn)行時(shí)處理注解 生成動(dòng)態(tài)代理
反射相關(guān)的主要API
java.lang.Class:代表一個(gè)類
java.lang.reflect.Method:代表類的方法
java.lang.reflect.Field:代表類的成員變量
java.lang.reflect.Constructor:代表類的構(gòu)造器 ? … …
程序經(jīng)過
javac.exe
命令以后,會(huì)生成一個(gè)或多個(gè)字節(jié)碼文件(.class
結(jié)尾)。
接著我們使用java.exe
命令對(duì)某個(gè)字節(jié)碼文件進(jìn)行解釋運(yùn)行。相當(dāng)于將某個(gè)字節(jié)碼文件加載到內(nèi)存中。此過程就稱為類的加載。加載到內(nèi)存中的類,我們就稱為運(yùn)行時(shí)類,此運(yùn)行時(shí)類,就作為Class
的一個(gè)實(shí)例。
換句話說,
Class
的實(shí)例就對(duì)應(yīng)著一個(gè)運(yùn)行時(shí)類。
加載到內(nèi)存中的運(yùn)行時(shí)類,會(huì)緩存一定的時(shí)間。在此時(shí)間之內(nèi),我們可以通過不同的方式來獲取此運(yùn)行時(shí)類。
當(dāng)程序主動(dòng)使用某個(gè)類時(shí),如果該類還未被加載到內(nèi)存中,則系統(tǒng)會(huì)通過如下三個(gè)步驟來對(duì)該類進(jìn)行初始化。
類的加載:將
class
文件字節(jié)碼內(nèi)容加載到內(nèi)存中,并將這些靜態(tài)數(shù)據(jù)轉(zhuǎn)換成方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu),然后生成一個(gè)代表這個(gè)類的java.lang.Class
對(duì)象,作為方法區(qū)中類數(shù)據(jù)的訪問入口(即引用地址)。所有需要訪問和使用類數(shù)據(jù)只能通過這個(gè)Class對(duì)象。這個(gè)加載的過程需要類加載器參與。
類的鏈接:將Java類的二進(jìn)制代碼合并到JVM的運(yùn)行狀態(tài)之中的過程。
● 驗(yàn)證:確保加載的類信息符合JVM規(guī)范,例如:以cafe開頭,沒有安全方面的問題
● 準(zhǔn)備:正式為類變量(static
)分配內(nèi)存并設(shè)置類變量默認(rèn)初始值的階段,這些內(nèi)存 都將在方法區(qū)中進(jìn)行分配。
● 解析:虛擬機(jī)常量池內(nèi)的符號(hào)引用(常量名)替換為直接引用(地址)的過程。
類的初始化:
● 執(zhí)行類構(gòu)造器【clinit
】()方法的過程。類構(gòu)造器【clinit
】()方法是由編譯期自動(dòng)收集類中 所有類變量的賦值動(dòng)作和靜態(tài)代碼塊中的語(yǔ)句合并產(chǎn)生的。(類構(gòu)造器是構(gòu)造類信 息的,不是構(gòu)造該類對(duì)象的構(gòu)造器)。
● 當(dāng)初始化一個(gè)類的時(shí)候,如果發(fā)現(xiàn)其父類還沒有進(jìn)行初始化,則需要先觸發(fā)其父類 的初始化。
● 虛擬機(jī)會(huì)保證一個(gè)類的()方法在多線程環(huán)境中被正確加鎖和同步。
public class ClassLoadingTest { public static void main(String[] args) { System.out.println(A.m); } } class A { static { m = 300; } static int m = 100; } //第二步:鏈接結(jié)束后m=0 //第三步:初始化后,m的值由()方法執(zhí)行決定 // 這個(gè)A的類構(gòu)造器 ()方法由類變量的賦值和靜態(tài)代碼塊中的語(yǔ)句按照順序合并產(chǎn)生,類似于 // (){ // m = 300; // m = 100; // }
類的主動(dòng)引用(一定會(huì)發(fā)生類的初始化)
當(dāng)虛擬機(jī)啟動(dòng),先初始化
main
方法所在的類
new
一個(gè)類的對(duì)象調(diào)用類的靜態(tài)成員(除了final常量)和靜態(tài)方法
使用
java.lang.reflect
包的方法對(duì)類進(jìn)行反射調(diào)用當(dāng)初始化一個(gè)類,如果其父類沒有被初始化,則先會(huì)初始化它的父類
類的被動(dòng)引用(不會(huì)發(fā)生類的初始化)
當(dāng)訪問一個(gè)靜態(tài)域時(shí),只有真正聲明這個(gè)域的類才會(huì)被初始化
當(dāng)通過子類引用父類的靜態(tài)變量,不會(huì)導(dǎo)致子類初始化
通過數(shù)組定義類引用,不會(huì)觸發(fā)此類的初始化
引用常量不會(huì)觸發(fā)此類的初始化(常量在鏈接階段就存入調(diào)用類的常量池中了)
類加載的作用:將class文件字節(jié)碼內(nèi)容加載到內(nèi)存中,并將這些靜態(tài)數(shù)據(jù)轉(zhuǎn)換成方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu),然后在堆中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為 方法區(qū)中類數(shù)據(jù)的訪問入口。
類緩存:標(biāo)準(zhǔn)的JavaSE類加載器可以按要求查找類,但一旦某個(gè)類被加載到類加載器 中,它將維持加載(緩存)一段時(shí)間。不過JVM垃圾回收機(jī)制可以回收這些Class對(duì)象。
不同類型的類的加載器:
@Test public void test1(){ //對(duì)于自定義類,使用系統(tǒng)類加載器進(jìn)行加載 ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2:系統(tǒng)類加載器 //調(diào)用系統(tǒng)類加載器的getParent():獲取擴(kuò)展類加載器 ClassLoader classLoader1 = classLoader.getParent(); System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@279f2327:擴(kuò)展類加載器 //調(diào)用擴(kuò)展類加載器的getParent():無法獲取引導(dǎo)類加載器 //引導(dǎo)類加載器主要負(fù)責(zé)加載java的核心類庫(kù),無法加載自定義類的。 ClassLoader classLoader2 = classLoader1.getParent(); System.out.println(classLoader2);//null ClassLoader classLoader3 = String.class.getClassLoader(); System.out.println(classLoader3);//null }
使用系統(tǒng)類加載器讀取
Properties
配置文件。
/* Properties:用來讀取配置文件。 */ @Test public void test2() throws Exception { Properties pros = new Properties(); //此時(shí)的文件默認(rèn)在當(dāng)前的module下。 //讀取配置文件的方式一:// FileInputStream fis = new FileInputStream("jdbc.properties");// FileInputStream fis = new FileInputStream("src\\jdbc1.properties");// pros.load(fis); //讀取配置文件的方式二:使用ClassLoader //配置文件默認(rèn)識(shí)別為:當(dāng)前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); }}
Class
類在Object
類中定義了以下的方法,此方法將被所有子類繼承:
public final Class getClass()
以上的方法返回值的類型是一個(gè)
Class
類,此類是Java
反射的源頭,實(shí)際上所謂反射從程序的運(yùn)行結(jié)果來看也很好理解,即:可以通過對(duì)象反射求出類的名稱。
對(duì)象照鏡子后可以得到的信息:某個(gè)類的屬性、方法和構(gòu)造器、某個(gè)類到底實(shí)現(xiàn)了哪些接口。對(duì)于每個(gè)類而言,
JRE
都為其保留一個(gè)不變的Class
類型的對(duì)象。
一個(gè)Class
對(duì)象包含了特定某個(gè)結(jié)構(gòu)(class
/interface
/enum
/annotation
/primitivetype
/void
/[]
)的有關(guān)信息。
Class
本身也是一個(gè)類
Class
對(duì)象只能由系統(tǒng)建立對(duì)象
一個(gè)加載的類在
JVM
中只會(huì)有一個(gè)Class
實(shí)例
一個(gè)Class對(duì)象對(duì)應(yīng)的是一個(gè)加載到
JVM
中的一個(gè).class
文件
每個(gè)類的實(shí)例都會(huì)記得自己是由哪個(gè)
Class
實(shí)例所生成
通過
Class
可以完整地得到一個(gè)類中的所有被加載的結(jié)構(gòu)
Class
類是Reflection
的根源,針對(duì)任何你想動(dòng)態(tài)加載、運(yùn)行的類,唯有先獲得相應(yīng)的
方法名 | 功能說明 |
---|---|
static Class forName(String name) | 返回指定類名 name 的 Class 對(duì)象 |
Object newInstance() | 調(diào)用缺省構(gòu)造函數(shù),返回該Class 對(duì)象的一個(gè)實(shí)例 |
getName() | 返回此Class對(duì)象所表示的實(shí)體(類、接口、數(shù)組類、基本類型或void )名稱 |
Class getSuperClass() | 返回當(dāng)前Class 對(duì)象的父類的Class 對(duì)象 |
Class [] getInterfaces() | 獲取當(dāng)前Class 對(duì)象的接口 |
ClassLoader getClassLoader() | 返回該類的類加載器 |
Class getSuperclass() | 返回表示此Class 所表示的實(shí)體的超類的Class |
Constructor[] getConstructors() | 返回一個(gè)包含某些Constructor 對(duì)象的數(shù)組 |
Field[] getDeclaredFields() | 返回Field 對(duì)象的一個(gè)數(shù)組 |
Method getMethod(String name,Class … paramTypes) | 返回一個(gè)Method 對(duì)象,此對(duì)象的形參類型為paramType |
(1)class: 外部類,成員(成員內(nèi)部類,靜態(tài)內(nèi)部類),局部?jī)?nèi)部類,匿名內(nèi)部類
(2)interface:接口
(3)[]:數(shù)組
(4)enum:枚舉
(5)annotation:注解@interface
(6)primitive type:基本數(shù)據(jù)類型
(7)void
前提:若已知具體的類,通過類的class屬性獲取,該方法最為安全可靠, 程序性能最高
示例:Class clazz1 = String.class;
前提:已知某個(gè)類的實(shí)例,調(diào)用該實(shí)例的
getClass()
方法獲取Class
對(duì)象
示例:Class clazz = “www.atguigu.com”.getClass();
前提:已知一個(gè)類的全類名,且該類在類路徑下,可通過
Class
類的靜態(tài)方法forName()
獲取,可能拋出ClassNotFoundException
示例:Class clazz = Class.forName(“java.lang.String”);
示例:
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass(“類的全類名”);
@Testpublic void test1() throws ClassNotFoundException { //方式一:調(diào)用運(yùn)行時(shí)類的屬性:.class Class clazz1 = Person.class; System.out.println(clazz1);//class com.jiaying.java1.Person //方式二:通過運(yùn)行時(shí)類的對(duì)象,調(diào)用getClass() Person p1 = new Person(); Class clazz2 = p1.getClass(); System.out.println(clazz2);//class com.jiaying.java1.Person //方式三:調(diào)用Class的靜態(tài)方法: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}
有了Class對(duì)象,能做什么?
創(chuàng)建類的對(duì)象:調(diào)用
Class
對(duì)象的newInstance()
方法
要求:
類必須有一個(gè)無參數(shù)的構(gòu)造器。
類的構(gòu)造器的訪問權(quán)限需要足夠。
難道沒有無參的構(gòu)造器就不能創(chuàng)建對(duì)象了嗎?
不是!只要在操作的時(shí)候明確的調(diào)用類中的構(gòu)造器,并將參數(shù)傳遞進(jìn)去之后,才可以實(shí)例化操作。
步驟如下:
通過
Class
類的getDeclaredConstructor(Class … parameterTypes)
取得本類的指定形參類型的構(gòu)造器向構(gòu)造器的形參中傳遞一個(gè)對(duì)象數(shù)組進(jìn)去,里面包含了構(gòu)造器中所需的各個(gè)參數(shù)。
通過
Constructor
實(shí)例化對(duì)象。
(1)根據(jù)全類名獲取對(duì)應(yīng)的
Class
對(duì)象
String name = “atguigu.java.Person";Class clazz = null;clazz = Class.forName(name);
(2)調(diào)用指定參數(shù)結(jié)構(gòu)的構(gòu)造器,生成
Constructor
的實(shí)例
Constructor con = clazz.getConstructor(String.class,Integer.class);
(3)通過
Constructor
的實(shí)例創(chuàng)建對(duì)應(yīng)類的對(duì)象,并初始化類屬性
Person p2 = (Person) con.newInstance("Peter",20);System.out.println(p2);
@Test public void test1() throws IllegalAccessException, InstantiationException { Classclazz = Person.class; /* newInstance():調(diào)用此方法,創(chuàng)建對(duì)應(yīng)的運(yùn)行時(shí)類的對(duì)象。內(nèi)部調(diào)用了運(yùn)行時(shí)類的空參的構(gòu)造器。 要想此方法正常的創(chuàng)建運(yùn)行時(shí)類的對(duì)象,要求: 1.運(yùn)行時(shí)類必須提供空參的構(gòu)造器 2.空參的構(gòu)造器的訪問權(quán)限得夠。通常,設(shè)置為public。 在javabean中要求提供一個(gè)public的空參構(gòu)造器。原因: 1.便于通過反射,創(chuàng)建運(yùn)行時(shí)類的對(duì)象 2.便于子類繼承此運(yùn)行時(shí)類時(shí),默認(rèn)調(diào)用super()時(shí),保證父類有此構(gòu)造器 */ Person obj = clazz.newInstance(); System.out.println(obj); }
//體會(huì)反射的動(dòng)態(tài)性 @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(); } } } /* 創(chuàng)建一個(gè)指定類的對(duì)象。 classPath:指定類的全類名 */ public Object getInstance(String classPath) throws Exception { Class clazz = Class.forName(classPath); return clazz.newInstance(); }}
提供具有豐富內(nèi)容的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 Creatureimplements 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 implements Comparable ,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("我的國(guó)籍是:" + nation); return nation; } public String display(String interests,int age) throws NullPointerException,ClassCastException{ return interests + age; } @Override public void info() { System.out.println("我是一個(gè)人"); } @Override public int compareTo(String o) { return 0; } private static void showDesc(){ System.out.println("我是一個(gè)可愛的人"); } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", id=" + id + '}'; }}
方法 | 作用 |
---|---|
public Field[] getFields() | 返回此Class 對(duì)象所表示的類或接口的public 的Field |
public Field[] getDeclaredFields() | 返回此Class 對(duì)象所表示的類或接口的全部Field |
Field方法中:
方法 | 作用 |
---|---|
public int getModifiers() | 以整數(shù)形式返回此Field 的修飾符 |
public Class> getType() | 得到Field 的屬性類型 |
public String getName() | 返回Field 的名稱 |
@Test public void test1(){ Class clazz = Person.class; //獲取屬性結(jié)構(gòu) //getFields():獲取當(dāng)前運(yùn)行時(shí)類及其父類中聲明為public訪問權(quán)限的屬性 Field[] fields = clazz.getFields(); for(Field f : fields){ System.out.println(f); } System.out.println(); //getDeclaredFields():獲取當(dāng)前運(yùn)行時(shí)類中聲明的所有屬性。(不包含父類中聲明的屬性) Field[] declaredFields = clazz.getDeclaredFields(); for(Field f : declaredFields){ System.out.println(f); } } //權(quán)限修飾符 數(shù)據(jù)類型 變量名 @Test public void test2(){ Class clazz = Person.class; Field[] declaredFields = clazz.getDeclaredFields(); for(Field f : declaredFields){ //1.權(quán)限修飾符 int modifier = f.getModifiers(); System.out.print(Modifier.toString(modifier) + "\t"); //2.數(shù)據(jù)類型 Class type = f.getType(); System.out.print(type.getName() + "\t"); //3.變量名 String fName = f.getName(); System.out.print(fName); System.out.println(); } }}
方法 | 作用 |
---|---|
public Method[] getMethods() | 返回此Class 對(duì)象所表示的類或接口的public 的方法 |
public Method[] getDeclaredMethods() | 返回此Class 對(duì)象所表示的類或接口的全部方法 |
Method類中:
方法 | 作用 |
---|---|
public Class> getReturnType() | 取得全部的返回值 |
public Class>[] getParameterTypes() | 取得全部的參數(shù) |
public int getModifiers() | 取得修飾符 |
public Class>[] getExceptionTypes() | 取得異常信息 |
@Test public void test1(){ Class clazz = Person.class; //getMethods():獲取當(dāng)前運(yùn)行時(shí)類及其所有父類中聲明為public權(quán)限的方法 Method[] methods = clazz.getMethods(); for(Method m : methods){ System.out.println(m); } System.out.println(); //getDeclaredMethods():獲取當(dāng)前運(yùn)行時(shí)類中聲明的所有方法。(不包含父類中聲明的方法) Method[] declaredMethods = clazz.getDeclaredMethods(); for(Method m : declaredMethods){ System.out.println(m); } } /* @Xxxx 權(quán)限修飾符 返回值類型 方法名(參數(shù)類型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.權(quán)限修飾符 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(); } }}
方法 | 作用 |
---|---|
public Constructor | 返回此 Class 對(duì)象所表示的類的所有public 構(gòu)造方法。 |
public Constructor | 返回此 Class 對(duì)象表示的類聲明的所有構(gòu)造方法。 |
Constructor類中:
方法 | 作用 |
---|---|
public int getModifiers() | 取得修飾符 |
public String getName() | 取得方法名稱 |
public Class>[] getParameterTypes() | 取得參數(shù)的類型 |
/* 獲取構(gòu)造器結(jié)構(gòu) */ @Test public void test1(){ Class clazz = Person.class; //getConstructors():獲取當(dāng)前運(yùn)行時(shí)類中聲明為public的構(gòu)造器 Constructor[] constructors = clazz.getConstructors(); for(Constructor c : constructors){ System.out.println(c); } System.out.println(); //getDeclaredConstructors():獲取當(dāng)前運(yùn)行時(shí)類中聲明的所有的構(gòu)造器 Constructor[] declaredConstructors = clazz.getDeclaredConstructors(); for(Constructor c : declaredConstructors){ System.out.println(c); } } /* 獲取運(yùn)行時(shí)類的父類 */ @Test public void test2(){ Class clazz = Person.class; Class superclass = clazz.getSuperclass(); System.out.println(superclass); } /* 獲取運(yùn)行時(shí)類的帶泛型的父類 */ @Test public void test3(){ Class clazz = Person.class; Type genericSuperclass = clazz.getGenericSuperclass(); System.out.println(genericSuperclass); } /* 獲取運(yùn)行時(shí)類的帶泛型的父類的泛型 代碼:邏輯性代碼 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()); }/* 獲取運(yùn)行時(shí)類實(shí)現(xiàn)的接口 */ @Test public void test5(){ Class clazz = Person.class; Class[] interfaces = clazz.getInterfaces(); for(Class c : interfaces){ System.out.println(c); } System.out.println(); //獲取運(yùn)行時(shí)類的父類實(shí)現(xiàn)的接口 Class[] interfaces1 = clazz.getSuperclass().getInterfaces(); for(Class c : interfaces1){ System.out.println(c); } } /* 獲取運(yùn)行時(shí)類所在的包 */ @Test public void test6(){ Class clazz = Person.class; Package pack = clazz.getPackage(); System.out.println(pack); } /* 獲取運(yùn)行時(shí)類聲明的注解 */ @Test public void test7(){ Class clazz = Person.class; Annotation[] annotations = clazz.getAnnotations(); for(Annotation annos : annotations){ System.out.println(annos); } }}
關(guān)于setAccessible方法的使用
Method
和Field
、Constructor
對(duì)象都有setAccessible()
方法。
setAccessible
啟動(dòng)和禁用訪問安全檢查的開關(guān)。
參數(shù)值為
true
則指示反射的對(duì)象在使用時(shí)應(yīng)該取消Java語(yǔ)言訪問檢查。
提高反射的效率。如果代碼中必須用反射,而該句代碼需要頻繁的被 調(diào)用,那么請(qǐng)?jiān)O(shè)置為
true
,使得原本無法訪問的私有成員也可以訪問,參數(shù)值為false
則指示反射的對(duì)象應(yīng)該實(shí)施Java語(yǔ)言訪問檢查。
在反射機(jī)制中,可以直接通過
Field
類操作類中的屬性,通過Field
類提供的set()
和get()
方法就可以完成設(shè)置和取得屬性內(nèi)容的操作。
方法 | 作用 |
---|---|
public Field getField(String name) | 返回此Class 對(duì)象表示的類或接口的指定的public 的Field |
public Field getDeclaredField(String name) | 返回此Class 對(duì)象表示的類或接口的指定的Field |
在Field中:
方法 | 作用 |
---|---|
public Object get(Object obj) | 取得指定對(duì)象obj 上此Field 的屬性內(nèi)容 |
public void set(Object obj,Object value) | 設(shè)置指定對(duì)象obj 上此Field 的屬性內(nèi)容 |
代碼演示:
public class ReflectionTest { @Test public void testField() throws Exception { Class clazz = Person.class; //創(chuàng)建運(yùn)行時(shí)類的對(duì)象 Person p = (Person) clazz.newInstance(); //獲取指定的屬性:要求運(yùn)行時(shí)類中屬性聲明為public //通常不采用此方法 Field id = clazz.getField("id"); /* 設(shè)置當(dāng)前屬性的值 set():參數(shù)1:指明設(shè)置哪個(gè)對(duì)象的屬性 參數(shù)2:將此屬性值設(shè)置為多少 */ id.set(p,1001); /* 獲取當(dāng)前屬性的值 get():參數(shù)1:獲取哪個(gè)對(duì)象的當(dāng)前屬性值 */ int pId = (int) id.get(p); System.out.println(pId); } /* 如何操作運(yùn)行時(shí)類中的指定的屬性 -- 需要掌握 */ @Test public void testField1() throws Exception { Class clazz = Person.class; //創(chuàng)建運(yùn)行時(shí)類的對(duì)象 Person p = (Person) clazz.newInstance(); //1. getDeclaredField(String fieldName):獲取運(yùn)行時(shí)類中指定變量名的屬性 Field name = clazz.getDeclaredField("name"); //2.保證當(dāng)前屬性是可訪問的 name.setAccessible(true); //3.獲取、設(shè)置指定對(duì)象的此屬性值 name.set(p,"Tom"); System.out.println(name.get(p)); }
通過反射,調(diào)用類中的方法,通過
Method
類完成。步驟:
通過
Class
類的getMethod(String name,Class…parameterTypes)
方法取得 一個(gè)Method
對(duì)象,并設(shè)置此方法操作時(shí)所需要的參數(shù)類型。之后使用
Object invoke(Object obj, Object[] args)
進(jìn)行調(diào)用,并向方法中 傳遞要設(shè)置的obj
對(duì)象的參數(shù)信息。
Object invoke(Object obj, Object … args)
說明:Object
對(duì)應(yīng)原方法的返回值,若原方法無返回值,此時(shí)返回null
若原方法若為靜態(tài)方法,此時(shí)形參
Object obj
可為null
若原方法形參列表為空,則
Object[] args
為null
若原方法聲明為private
,則需要在調(diào)用此invoke()
方法前,顯式調(diào)用 方法對(duì)象的setAccessible(true)
方法,將可訪問private
的方法。
代碼演示:
/* 如何操作運(yùn)行時(shí)類中的指定的方法 -- 需要掌握 */ @Test public void testMethod() throws Exception { Class clazz = Person.class; //創(chuàng)建運(yùn)行時(shí)類的對(duì)象 Person p = (Person) clazz.newInstance(); /* 1.獲取指定的某個(gè)方法 getDeclaredMethod():參數(shù)1 :指明獲取的方法的名稱 參數(shù)2:指明獲取的方法的形參列表 */ Method show = clazz.getDeclaredMethod("show", String.class); //2.保證當(dāng)前方法是可訪問的 show.setAccessible(true); /* 3. 調(diào)用方法的invoke():參數(shù)1:方法的調(diào)用者 參數(shù)2:給方法形參賦值的實(shí)參 invoke()的返回值即為對(duì)應(yīng)類中調(diào)用的方法的返回值。 */ Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN"); System.out.println(returnValue); System.out.println("*************如何調(diào)用靜態(tài)方法*****************"); // private static void showDesc() Method showDesc = clazz.getDeclaredMethod("showDesc"); showDesc.setAccessible(true); //如果調(diào)用的運(yùn)行時(shí)類中的方法沒有返回值,則此invoke()返回null// Object returnVal = showDesc.invoke(null); Object returnVal = showDesc.invoke(Person.class); System.out.println(returnVal);//null }
代碼演示:
/* 如何調(diào)用運(yùn)行時(shí)類中的指定的構(gòu)造器 */ @Test public void testConstructor() throws Exception { Class clazz = Person.class; //private Person(String name) /* 1.獲取指定的構(gòu)造器 getDeclaredConstructor():參數(shù):指明構(gòu)造器的參數(shù)列表 */ Constructor constructor = clazz.getDeclaredConstructor(String.class); //2.保證此構(gòu)造器是可訪問的 constructor.setAccessible(true); //3.調(diào)用此構(gòu)造器創(chuàng)建運(yùn)行時(shí)類的對(duì)象 Person per = (Person) constructor.newInstance("Tom"); System.out.println(per); }}
以上就是“java反射機(jī)制的概念是什么及怎么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會(huì)為大家更新不同的知識(shí),如果還想學(xué)習(xí)更多的知識(shí),請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。