一.反射的概念及定義
- 在日常的第三方應(yīng)用開發(fā)過程中,經(jīng)常會遇到某個(gè)類的某個(gè)成員變量、方法或是屬性是私有的或是只對系統(tǒng) 應(yīng)用開放,這時(shí)候就可以利用Java的反射機(jī)制通過反射來獲取所需的私有成員或是方法 。
- 反射最重要的用途就是開發(fā)各種通用框架,比如在spring中,我們將所有的類Bean交給spring容器管理,無 論是XML配置Bean還是注解配置,當(dāng)我們從容器中獲取Bean來依賴注入時(shí),容器會讀取配置,而配置中給的就是類的信息,spring根據(jù)這些信息,需要創(chuàng)建那些Bean,spring就動態(tài)的創(chuàng)建這些類。
反射不同時(shí)期的類型:
Java程序中許多對象在運(yùn)行時(shí)會出現(xiàn)兩種類型:運(yùn)行時(shí)類型(RTTI)和編譯時(shí)類型。
例如:Person p = new Student();
-
編譯時(shí)類型是在編譯時(shí)期確定的,它是變量聲明時(shí)所使用的類型。在上面的示例代碼中,變量
p
的編譯時(shí)類型是Person
,因?yàn)樗峭ㄟ^Person p
的聲明來定義的。 -
運(yùn)行時(shí)類型是在程序運(yùn)行時(shí)確定的,它是實(shí)際分配給對象的類型。在上面的示例代碼中,使用
new Student()
創(chuàng)建了一個(gè)Student
對象,并將其賦值給變量p
,因此在運(yùn)行時(shí),p
的類型是Student
。
通過反射,可以獲取對象的運(yùn)行時(shí)類型。例如,可以使用p.getClass()
方法獲取p
對象的實(shí)際類型(運(yùn)行時(shí)類型),并進(jìn)行相應(yīng)的操作。
除了獲取對象的運(yùn)行時(shí)類型,反射還可以獲取類的詳細(xì)信息,包括類的名稱、父類、接口、構(gòu)造函數(shù)、字段和方法等。通過獲取類的信息,可以做一些動態(tài)的操作,如動態(tài)創(chuàng)建對象、調(diào)用方法、訪問字段等。
注意:Java文件被編譯后,生成了.class文件,JVM此時(shí)就要去解讀.class文件 ,被編譯后的Java文件.class也被JVM解析為一個(gè)對象,這個(gè)對象就是 java.lang.Class .這樣當(dāng)程序在運(yùn)行時(shí),每個(gè)java文件就最終變成了Class類對象的一個(gè)實(shí)例。我們通過Java的反射機(jī)制應(yīng)用到這個(gè)實(shí)例,就可以去獲得甚至去添加改變這個(gè)類的屬性和動作,使得這個(gè)類成為一個(gè)動態(tài)的類 .
二.反射相關(guān)的示例
在Java中,反射通過java.lang.reflect
包中的類和接口來實(shí)現(xiàn)。
反射的基本信息:
-
Class
類:Class
是反射的核心類之一,它表示一個(gè)類或接口的運(yùn)行時(shí)對象。通過Class
類可以獲取和操作類的信息,如類的名稱、父類、接口、構(gòu)造函數(shù)、字段和方法等。 -
Constructor
類:Constructor
類表示類的構(gòu)造函數(shù)。通過Constructor
類可以創(chuàng)建類的實(shí)例,調(diào)用構(gòu)造函數(shù)并實(shí)例化對象。 -
Field
類:Field
類表示類的字段(成員變量)。通過Field
類可以獲取和修改字段的值,以及訪問字段的屬性信息。 -
Method
類:Method
類表示類的方法。通過Method
類可以調(diào)用類的方法,傳遞參數(shù)并獲取返回值。 -
獲取
Class
對象:可以使用多種方式獲取Class
對象,如使用類名調(diào)用Class.forName()
方法、通過類的實(shí)例調(diào)用getClass()
方法、或者直接通過類字面常量使用SomeClass.class
。 -
實(shí)例化對象:通過
Class
對象和Constructor
類可以實(shí)例化類的對象??梢允褂?code>newInstance()方法創(chuàng)建無參構(gòu)造函數(shù)的實(shí)例,或者使用Constructor
類的newInstance()
方法傳遞參數(shù)創(chuàng)建有參構(gòu)造函數(shù)的實(shí)例。 -
調(diào)用方法:通過
Class
對象和Method
類可以調(diào)用類的方法??梢允褂?code>invoke()方法傳遞對象和參數(shù)來調(diào)用方法,并獲取返回值。 -
訪問字段:通過
Class
對象和Field
類可以訪問類的字段??梢允褂?code>get()和set()
方法獲取和修改字段的值,以及使用getField()
和getDeclaredField()
方法獲取字段對象。
?
?2.1獲取Class對象的三種方式
Class
?對象,因?yàn)?
Class
?對象提供了許多有用的方法和操作,用于在運(yùn)行時(shí)檢查和操作類的結(jié)構(gòu)、屬性和方法。
Class
?對象,這個(gè)對象相當(dāng)于是類的身份證。通過這個(gè)身份證,我們可以了解這個(gè)類的各種信息。
通過?Class
?對象,我們可以做以下事情:
-
創(chuàng)建對象實(shí)例:通過?
Class
?對象的?newInstance()
?方法可以動態(tài)地創(chuàng)建類的對象實(shí)例。 -
獲取類的信息:通過?
Class
?對象可以獲取類的名稱、修飾符、包名、父類、接口、字段和方法等信息。 -
獲取和設(shè)置字段值:通過?
Class
?對象和字段名稱,可以獲取和設(shè)置類的字段的值。 -
調(diào)用方法:通過?
Class
?對象和方法名稱,可以調(diào)用類的方法。 -
動態(tài)加載類:通過?
Class.forName()
?方法可以在運(yùn)行時(shí)動態(tài)加載類。 -
進(jìn)行注解處理:通過?
Class
?對象可以獲取類上的注解信息,并進(jìn)行相應(yīng)的處理。
Class
?對象,我們可以在運(yùn)行時(shí)對類進(jìn)行檢查和操作,而無需提前知道類的具體信息。這使得代碼更加靈活,可以在運(yùn)行時(shí)根據(jù)需要動態(tài)地處理和操作類。
package demo1;
/**
* @Author 12629
* @Description:
*/
class Student {
//私有屬性name
private String name = "Classmates";
//公有屬性age
public int age = 18;
//不帶參數(shù)的構(gòu)造方法
public Student(){
System.out.println("Student()");
}
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);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
第一種,使用 Class.forName("類的全路徑名"); 靜態(tài)方法。(前提:已明確類的全路徑名。)
public class Test {
public static void main(String[] args) {
//獲取Class對象有三種方式之一
//第一種
Class<Student> c1 = null;
try {
c1 = Class.forName("demo1.Student");
}catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
這段代碼的作用是將字符串?"demo1.Student"
?作為參數(shù)傳遞給?Class.forName()
?方法,并將返回的?Class
?對象賦值給變量?c1
。
為什么有調(diào)異常呢?
這是因?yàn)?Class.forName()
?方法嘗試根據(jù)提供的類名加載對應(yīng)的類,但如果在類路徑中找不到該類,就會拋出?ClassNotFoundException
?異常。
注意:使用?Class.forName()
?方法時(shí),需要提供類的全限定名,即包括包名和類名。

?
第二種:使用 .class 方法。(僅適合在編譯前就已經(jīng)明確要操作的 Class)
public class Test {
public static void main(String[] args) {
//第二種
Class<Student> c2 = Student.class;
}
}
段代碼的作用是通過類字面常量?Student.class
?來獲取?Student
?類的?Class
?對象,并將其賦值給變量?c2
。
使用類字面常量的方式非常簡單,只需要在類名后面加上?.class
?就可以直接訪問該類的?Class
?對象。
public class Test {
//第三種
Student student = new Student();
Class<? extends Student> c3 = student.getClass();
}
我們創(chuàng)建了一個(gè)?Student
?類的對象?student
,然后通過?student.getClass()
?方法獲取該對象的運(yùn)行時(shí)類的?Class
?對象,并將其賦值給類型為?Class<? extends Student>
?的變量?c3
。
使用對象的?getClass()
?方法可以在運(yùn)行時(shí)獲取對象所屬類的?Class
?對象。這種方式適用于當(dāng)我們有一個(gè)對象實(shí)例,想要獲取其對應(yīng)的類信息時(shí)。
?
注意:一個(gè)類在 JVM 中只會有一個(gè) Class 實(shí)例
public class Test {
/*
Class對象 只有一個(gè)
*/
public static void main(String[] args) {
//獲取Class對象有三種方式
//生成的對象只有一個(gè)
//第一種
Class<Student> c1 = null;
try {
c1 = Class.forName("demo1.Student");
}catch (ClassNotFoundException e) {
e.printStackTrace();
}
//第二種
Class<Student> c2 = Student.class;
//第三種
Student student = new Student();
Class<? extends Student> c3 = student.getClass();
System.out.println(c1 == c2);
System.out.println(c1 == c3);
}
}
運(yùn)行例圖如下:?
2.2 Class對象的使用?
上一步我們已經(jīng)創(chuàng)建好?Class
?對象了,可以使用它來進(jìn)行各種操作。
?
?2.常用獲得類中屬性相關(guān)的方法(以下方法返回值為Field相關(guān)):
3.?獲得類中構(gòu)造器相關(guān)的方法(以下方法返回值為Constructor相關(guān))
4.
4.獲得類中方法相關(guān)的方法(以下方法返回值為Method相關(guān))?
5.獲得類中注解相關(guān)的方法?(了解即可)
代碼案例如下:
package demo1;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectClassDemo {
/*reflectNewInstance()?方法使用反射創(chuàng)建一個(gè)類的實(shí)例,然后輸出該實(shí)例。
它通過?Class.forName()?方法獲取類的?Class?對象,然后使用?newInstance()?方法創(chuàng)建實(shí)例。
*/
public static void reflectNewInstance() {
Class<?> classStudent = null;
try {
// 獲取類的Class對象
classStudent = Class.forName("demo1.Student");
// 使用newInstance()方法創(chuàng)建類的實(shí)例
Student student = (Student) classStudent.newInstance();
System.out.println(student);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/*
reflectPrivateConstructor()?方法演示了如何使用反射調(diào)用私有的構(gòu)造方法。
它通過?Class.forName()?方法獲取類的?Class?對象,然后使用?getDeclaredConstructor()?方法獲取私有構(gòu)造方法,并通過?setAccessible(true)?設(shè)置訪問權(quán)限。
最后,使用?newInstance()?方法創(chuàng)建實(shí)例并輸出。
*/
public static void reflectPrivateConstructor() {
Class<?> classStudent = null;
try {
classStudent = Class.forName("demo1.Student");
// 獲取私有構(gòu)造方法
Constructor<?> constructor = classStudent.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true); // 設(shè)置私有構(gòu)造方法可訪問
// 調(diào)用私有構(gòu)造方法創(chuàng)建實(shí)例
Student student = (Student) constructor.newInstance("xiaoming", 15);
System.out.println(student);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/*
reflectPrivateField()?方法展示了如何使用反射操作私有字段。
它通過?Class.forName()?方法獲取類的?Class?對象,然后使用?getDeclaredField()?方法獲取私有字段,并通過?setAccessible(true)?設(shè)置訪問權(quán)限。
接下來,使用?set()?方法給字段設(shè)置新的值,并輸出結(jié)果。
*/
public static void reflectPrivateField() {
Class<?> classStudent = null;
try {
classStudent = Class.forName("demo1.Student");
// 獲取私有字段
Field field = classStudent.getDeclaredField("name");
field.setAccessible(true); // 設(shè)置私有字段可訪問(這個(gè)特別要注意,不然會報(bào)異常)
// 創(chuàng)建類的實(shí)例
Student student = (Student) classStudent.newInstance();
// 設(shè)置私有字段的值
field.set(student, "caocao");
System.out.println(student);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/*
reflectPrivateMethod()?方法演示了如何使用反射調(diào)用私有方法。
它通過?Class.forName()?方法獲取類的?Class?對象,然后使用?getDeclaredMethod()?方法獲取私有方法,并通過?setAccessible(true)?設(shè)置訪問權(quán)限。
最后,使用?invoke()?方法調(diào)用方法并輸出結(jié)果。
*/
public static void reflectPrivateMethod() {
Class<?> classStudent = null;
try {
classStudent = Class.forName("demo1.Student");
// 獲取私有方法
Method method = classStudent.getDeclaredMethod("function", String.class);
method.setAccessible(true); // 設(shè)置私有方法可訪問
// 創(chuàng)建類的實(shí)例
Student student = (Student) classStudent.newInstance();
// 調(diào)用私有方法
method.invoke(student, "我是一個(gè)反射的參數(shù)!");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
// 反射實(shí)例化類
reflectNewInstance();
// 反射調(diào)用私有構(gòu)造方法
reflectPrivateConstructor();
// 反射操作私有字段
reflectPrivateField();
//反射調(diào)用私有方法
reflectPrivateMethod();
}
}
?運(yùn)行例圖如下:
三.總結(jié)
反射的優(yōu)缺點(diǎn)如下:
優(yōu)點(diǎn):
-
動態(tài)性和靈活性:反射允許在運(yùn)行時(shí)動態(tài)地獲取和操作類的信息,使程序能夠根據(jù)需要適應(yīng)不同的情況和需求。它提供了靈活的實(shí)例化、字段訪問和方法調(diào)用,以及動態(tài)代理和處理注解等功能,增強(qiáng)了程序的靈活性和可擴(kuò)展性。
-
泛型操作:反射使得可以在運(yùn)行時(shí)獲取泛型類型的信息,并進(jìn)行相應(yīng)的操作。這對于編寫通用代碼和框架非常有用,可以在不知道具體類型的情況下進(jìn)行更多的操作和處理。
-
框架和庫的開發(fā):反射廣泛應(yīng)用于框架和庫的開發(fā)中。通過反射,可以在運(yùn)行時(shí)動態(tài)地加載和使用類,根據(jù)配置文件或用戶輸入進(jìn)行相應(yīng)的操作,使框架和庫具有更強(qiáng)的擴(kuò)展性和適應(yīng)性。
缺點(diǎn):
-
性能影響:反射的操作通常比直接調(diào)用方法或訪問字段的性能要低。使用反射會引入額外的開銷,包括方法調(diào)用和類型檢查等。因此,頻繁使用反射可能導(dǎo)致程序的性能下降。
-
安全性問題:反射可以繞過訪問權(quán)限的限制,可以訪問和修改私有成員,并執(zhí)行敏感操作。這可能導(dǎo)致安全性問題,特別是在處理不受信任的代碼或用戶輸入時(shí)需要格外小心。
-
編碼復(fù)雜性和可讀性降低:反射的使用可能會增加代碼的復(fù)雜性和可讀性降低。由于反射是在運(yùn)行時(shí)動態(tài)進(jìn)行的,因此一些問題只能在運(yùn)行時(shí)才能被發(fā)現(xiàn),而不是在編譯時(shí)。這可能導(dǎo)致調(diào)試和維護(hù)過程中的困難。
-
局限性:反射有一些局限性,例如無法操作編譯時(shí)不存在的類、字段或方法;無法操作原始類型的字段等。此外,由于反射是基于運(yùn)行時(shí)信息的,因此在某些情況下可能無法獲得期望的結(jié)果。
反射是Java語言中的一項(xiàng)強(qiáng)大特性,它允許程序在運(yùn)行時(shí)動態(tài)地獲取、操作和修改類、對象、字段和方法的信息。通過反射,我們可以實(shí)現(xiàn)靈活的類實(shí)例化、字段訪問和方法調(diào)用,以及處理注解和實(shí)現(xiàn)動態(tài)代理等功能。然而,反射的使用應(yīng)謹(jǐn)慎,需要平衡靈活性、性能和安全性,并注意其局限性和注意事項(xiàng)。總而言之,反射為Java開發(fā)者提供了強(qiáng)大的工具,使得程序可以在運(yùn)行時(shí)動態(tài)地適應(yīng)不同的需求和場景。文章來源:http://www.zghlxwxcb.cn/news/detail-843402.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-843402.html
到了這里,關(guān)于從初學(xué)者到專家:Java反射的完整指南的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!