邯鄲市網(wǎng)站建設(shè)新手怎么學(xué)電商運(yùn)營(yíng)
看到這句話的時(shí)候證明:此刻你我都在努力
加油陌生人
個(gè)人主頁(yè):Gu Gu Study
專欄:用Java學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)系列
喜歡的一句話: 常常會(huì)回顧努力的自己,所以要為自己的努力留下足跡
喜歡的話可以點(diǎn)個(gè)贊謝謝了。
作者:小閉
目錄
反射的定義
主要用途:
反射的基本信息
反射相關(guān)的類(lèi)(重要)
Class類(lèi)
獲取Class對(duì)象的三種方式
反射對(duì)象的使用
反射私有構(gòu)造方法:
反射私有屬性:
反射私有方法:
反射的優(yōu)缺點(diǎn)
反射的定義
Java的反射(reflection)機(jī)制是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類(lèi),都能夠知道這個(gè)類(lèi)的所有屬性和方法;對(duì)于任 意一個(gè)對(duì)象,都能夠調(diào)用它的任意方法和屬性,既然能拿到那么,我們就可以修改部分類(lèi)型信息;這種動(dòng)態(tài)獲取信 息以及動(dòng)態(tài)調(diào)用對(duì)象方法的功能稱為java語(yǔ)言的反射(reflection)機(jī)制。
主要用途:
- 在日常的第三方應(yīng)用開(kāi)發(fā)過(guò)程中,經(jīng)常會(huì)遇到某個(gè)類(lèi)的某個(gè)成員變量、方法或是屬性是私有的或是只對(duì)系統(tǒng)
應(yīng)用開(kāi)放,這時(shí)候就可以利用Java的反射機(jī)制通過(guò)反射來(lái)獲取所需的私有成員或是方法 。 - 反射最重要的用途就是開(kāi)發(fā)各種通用框架,比如在spring中,我們將所有的類(lèi)Bean交給spring容器管理,無(wú)
論是XML配置Bean還是注解配置,當(dāng)我們從容器中獲取Bean來(lái)依賴注入時(shí),容器會(huì)讀取配置,而配置中給的
就是類(lèi)的信息,spring根據(jù)這些信息,需要?jiǎng)?chuàng)建那些Bean,spring就動(dòng)態(tài)的創(chuàng)建這些類(lèi)。
反射的基本信息
Java程序中許多對(duì)象在運(yùn)行時(shí)會(huì)出現(xiàn)兩種類(lèi)型:運(yùn)行時(shí)類(lèi)型(RTTI)和編譯時(shí)類(lèi)型,例如Person p = new Student();這句代碼中p在編譯時(shí)類(lèi)型為Person,運(yùn)行時(shí)類(lèi)型為Student。程序需要在運(yùn)行時(shí)發(fā)現(xiàn)對(duì)象和類(lèi)的真實(shí) 信息。而通過(guò)使用反射程序就能判斷出該對(duì)象和類(lèi)屬于哪些類(lèi)。
反射相關(guān)的類(lèi)(重要)
類(lèi)名 用途
Class類(lèi) 代表類(lèi)的實(shí)體,在運(yùn)行的Java應(yīng)用程序中表示類(lèi)和接口
Field類(lèi) 代表類(lèi)的成員變量/類(lèi)的屬性
Method類(lèi) 代表類(lèi)的方法
Constructor類(lèi) 代表類(lèi)的構(gòu)造方法
Class類(lèi)
Class類(lèi)中主要獲取類(lèi)相關(guān)的方法:
getClassLoader() 獲得類(lèi)的加載器
getDeclaredClasses() 返回一個(gè)數(shù)組,數(shù)組中包含該類(lèi)中所有類(lèi)和接口類(lèi)的對(duì)象(包括私有的)
forName(String className) 根據(jù)類(lèi)名返回類(lèi)的對(duì)象
newInstance() 創(chuàng)建類(lèi)的實(shí)例
getName() 獲得類(lèi)的完整路徑名字
Class類(lèi)中主要獲取類(lèi)中屬性相關(guān)的方法:(以下方法返回值為Field對(duì)象)
getField(String name) 獲得某個(gè)公有的屬性對(duì)象
getFields() 獲得所有公有的屬性對(duì)象
getDeclaredField(String name) 獲得某個(gè)屬性對(duì)象
getDeclaredFields() 獲得所有屬性對(duì)象
Class類(lèi)中主要獲取類(lèi)中方法相關(guān)的方法:(以下方法返回值為Method對(duì)象)
getMethod(String name, Class... parameterTypes) 獲得該類(lèi)某個(gè)公有的方法
getMethods() 獲得該類(lèi)所有公有的方法
getDeclaredMethod(String name, Class... parameterTypes) 獲得該類(lèi)某個(gè)方法
getDeclaredMethods() 獲得該類(lèi)所有方法
Class類(lèi)中主要獲取類(lèi)中構(gòu)造器相關(guān)的方法:(以下方法返回值為Constructor對(duì)象)
getConstructor(Class... parameterTypes) 獲得該類(lèi)中與參數(shù)類(lèi)型匹配的公有構(gòu)造方法
getConstructors() 獲得該類(lèi)的所有公有構(gòu)造方法
getDeclaredConstructor(Class... parameterTypes) 獲得該類(lèi)中與參數(shù)類(lèi)型匹配的構(gòu)造方法 getDeclaredConstructors() 獲得該類(lèi)所有構(gòu)造方法
獲取Class對(duì)象的三種方式
- 第一種,使用 Class.forName("類(lèi)的全路徑名"); 靜態(tài)方法。 前提:已明確類(lèi)的全路徑名。
- 第二種,使用 .class 方法。 說(shuō)明:僅適合在編譯前就已經(jīng)明確要操作的 Class
- 第三種,使用類(lèi)對(duì)象的 getClass() 方法
如下代碼,我們有一個(gè)學(xué)生類(lèi)(因?yàn)橹皇桥e例里面是空的,沒(méi)有定義成員和方法),然后下面我們就使用上面的三種方式獲取我們的Class對(duì)象。而且我們也使用equals進(jìn)行比較,發(fā)現(xiàn)三種方式獲取的class的對(duì)象都是一樣的,也就側(cè)面說(shuō)明了:一個(gè)類(lèi)在 JVM 中只會(huì)有一個(gè) Class 實(shí)例
//建立了一個(gè)類(lèi)
class Student{//成員//方法
}class TestDemo {public static void main(String[] args) {
/*
1.通過(guò)getClass獲取Class對(duì)象
*/Student s1 = new Student();Class c1 = s1.getClass();
/*
2.直接通過(guò) 類(lèi)名.class 的方式得到,該方法最為安全可靠,程序性能更高
這說(shuō)明任何一個(gè)類(lèi)都有一個(gè)隱含的靜態(tài)成員變量 class
*/Class c2 = Student.class;
/*
3、通過(guò) Class 對(duì)象的 forName() 靜態(tài)方法來(lái)獲取,用的最多,
但可能拋出 ClassNotFoundException 異常
*/Class c3 = null;try {
//注意這里是類(lèi)的全路徑,如果有包需要加包的路徑c3 = Class.forName("Student");} catch (ClassNotFoundException e) {e.printStackTrace();}
//一個(gè)類(lèi)在 JVM 中只會(huì)有一個(gè) Class 實(shí)例,即我們對(duì)上面獲取的
//c1,c2,c3進(jìn)行 equals 比較,發(fā)現(xiàn)都是trueSystem.out.println(c1);System.out.println(c1.equals(c2));System.out.println(c1.equals(c3));System.out.println(c2.equals(c3));}
}
反射對(duì)象的使用
上面我們介紹了許多Class類(lèi)的方法,其中獲取到的 Class類(lèi) ,Field類(lèi),Metho類(lèi),Constructor類(lèi)對(duì)象,又有什么用呢,只講概念肯定是比較抽象的。
下面我們就用更具體的代碼運(yùn)行給大家看看:
首先我們創(chuàng)建一個(gè)帶有各種屬性的成員與公開(kāi),私有的方法(包括構(gòu)造方法),以便后面使用反射獲取這些方法。
public class Student{//私有屬性nameprivate String name="小閉" ;//公有屬性agepublic int age=19 ;//不帶參數(shù)的構(gòu)造方法public Student(){System.out.println("調(diào)用了無(wú)參的構(gòu)造方法");}private Student(String name,int age) {this.name = name;this.age = age;System.out.println("Student(String,name)");}private void eat(){System.out.println("i am eat");}public void sleep(){System.out.println("i am pig");}private void function(String str) {System.out.println(str);}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}
}
然后我們開(kāi)始使用反射獲取一系列的方法并使用:
獲取一個(gè)對(duì)象:
這時(shí)這里就相當(dāng)于調(diào)用的是無(wú)參的構(gòu)造方法,創(chuàng)建了一個(gè)對(duì)象
public static void reflectNewInstance() {try {Class<?> classStudent = Class.forName("Student");Object objectStudent = classStudent.newInstance();Student student = (Student) objectStudent;System.out.println("獲得學(xué)生對(duì)象:"+student);} catch (Exception ex) {ex.printStackTrace();}
}
反射私有構(gòu)造方法:
public static void reflectPrivateConstructor() {try {Class<?> classStudent = Class.forName("Student");
//注意傳入對(duì)應(yīng)的參數(shù)Constructor<?> declaredConstructorStudent = classStudent.getDeclaredConstructor(String.class,int.class);//設(shè)置為true后可修改訪問(wèn)權(quán)限declaredConstructorStudent.setAccessible(true);Object objectStudent = declaredConstructorStudent.newInstance("小閉",19);Student student = (Student) objectStudent;System.out.println("獲得私有構(gòu)造哈數(shù)且修改姓名和年齡:"+student);} catch (Exception ex) {ex.printStackTrace();}}
在上面代碼中我們首先獲取clsaa對(duì)象,然后通過(guò)class對(duì)象獲取構(gòu)造方法,這里需注意的是,getDeclaredConstructor獲取的可以是所有的方法包括私有,如果獲取了私有的方法,則就需要
調(diào)用獲取的 Constructor<?>對(duì)象的setAccessible方法
//設(shè)置為true后可修改訪問(wèn)權(quán)限
declaredConstructorStudent.setAccessible(true);
再然后我們就可以開(kāi)始創(chuàng)建實(shí)例了,也就是相當(dāng)與調(diào)用構(gòu)造方法了這里:
Object objectStudent = declaredConstructorStudent.newInstance("小閉",19);
這里返回值用Object接收,后面在進(jìn)行強(qiáng)轉(zhuǎn)為Student即可。
反射私有屬性:
public static void reflectPrivateField() {try {Class<?> classStudent = Class.forName("Student");Field field = classStudent.getDeclaredField("name");field.setAccessible(true);
//可以修改該屬性的值Object objectStudent = classStudent.newInstance();Student student = (Student) objectStudent;field.set(student,"小明");String name = (String) field.get(student);System.out.println("反射私有屬性修改了name:"+ name);System.out.println(student);} catch (Exception ex) {ex.printStackTrace();}}
步驟:
首先獲取class對(duì)象
然后調(diào)用getDeclaredField("name")方法,里面?zhèn)魅胂氆@取的屬性名稱(String)。一樣的因?yàn)槭撬接械膶傩运晕覀冞€是要調(diào)用 field.setAccessible(true);這個(gè)方法,
在然后創(chuàng)建出一個(gè)Student對(duì)象,也就是向上方一樣,調(diào)用newInstance(),并強(qiáng)轉(zhuǎn)成Student
Object objectStudent = classStudent.newInstance(); //相當(dāng)于調(diào)用無(wú)參構(gòu)造方法,類(lèi)似于new
Student student = (Student) objectStudent;
最后就是調(diào)用 field.set(student,"小明");就可以將student對(duì)象的name設(shè)置為“小明”了。
反射私有方法:
public static void reflectPrivateMethod() {try {Class<?> classStudent = Class.forName("Student");Method methodStudent = classStudent.getDeclaredMethod("function",String.class);System.out.println("私有方法的方法名為:"+methodStudent.getName());
//私有的一般都要加methodStudent.setAccessible(true);Object objectStudent = classStudent.newInstance();Student student = (Student) objectStudent;methodStudent.invoke(student,"我是給私有的function函數(shù)傳的參數(shù)");} catch (Exception ex) {ex.printStackTrace();}}
步驟:
與上面類(lèi)似
首先我們還是獲取class對(duì)象
然后獲取對(duì)應(yīng)的方法(根據(jù)傳入的參數(shù)選取對(duì)應(yīng)的方法。)
Method methodStudent = classStudent.getDeclaredMethod("function",String.class);
并且 methodStudent.setAccessible(true);
然后創(chuàng)建對(duì)應(yīng)的Student對(duì)象。
再然后開(kāi)始 調(diào)用方法 :methodStudent.invoke(student,"我是給私有的function函數(shù)傳的參數(shù)");
反射的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 對(duì)于任意一個(gè)類(lèi),都能夠知道這個(gè)類(lèi)的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意一個(gè)方法
- 增加程序的靈活性和擴(kuò)展性,降低耦合性,提高自適應(yīng)能力
- 反射已經(jīng)運(yùn)用在了很多流行框架如:Struts、Hibernate、Spring 等等。
缺點(diǎn):
. 使用反射會(huì)有效率問(wèn)題。會(huì)導(dǎo)致程序效率降低。具體參考這里:大家都說(shuō) Java 反射效率低,你知道原因在哪里么_慕課手記
2. 反射技術(shù)繞過(guò)了源代碼的技術(shù),因而會(huì)帶來(lái)維護(hù)問(wèn)題。反射代碼比相應(yīng)的直接代碼更復(fù)雜 。