1.概念
Java反射是指:在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意方法和屬性;并且能改變它的屬性。
這也是Java作為靜態(tài)語言,卻能被視為動(dòng)態(tài)/準(zhǔn)動(dòng)態(tài)語言(動(dòng)態(tài)語言是指程序運(yùn)行時(shí),允許改變程序結(jié)構(gòu)或變量類型。Perl,Python,Ruby是動(dòng)態(tài)語言;C++,Java,C#是靜態(tài)語言)的一個(gè)關(guān)鍵性質(zhì)。
2.作用
? 反射機(jī)制允許程序在運(yùn)行時(shí)取得任何一個(gè)已知名稱的class的內(nèi)部信息,包括:modifiers(修飾符),fields(屬性),methods(方法)等,并可于運(yùn)行時(shí)改變fields內(nèi)容或調(diào)用methods。
? 運(yùn)用反射,我們便可以更靈活的編寫代碼,代碼可以在運(yùn)行時(shí)裝配,無需在組件之間進(jìn)行源代碼鏈接,降低代碼的耦合度。此外,還有動(dòng)態(tài)代理的實(shí)現(xiàn),等等。
? 但是需要注意的是:反射使用不當(dāng)會(huì)造成很高的資源消耗。
3.反射的實(shí)現(xiàn)
3.1關(guān)鍵概念
Java 提供反射機(jī)制,依賴于 Class 類和 java.lang.reflect 類庫。
反射API用來生成JVM中的類、接口或則對(duì)象的信息。
-
Class類:反射的核心類,可以獲取類的屬性,方法等信息。
Class 類是 Java 中用來表示運(yùn)行時(shí)類型信息的對(duì)應(yīng)類。在 Java 中,每個(gè)類都有一個(gè) Class 對(duì)象,每當(dāng)我們編寫并且編譯一個(gè)新創(chuàng)建的類,就會(huì)將相關(guān)信息寫到 .class 文件里。當(dāng)我們 new 一個(gè)新對(duì)象或者引用靜態(tài)成員變量時(shí),JVM 中的類加載器子系統(tǒng)會(huì)將對(duì)應(yīng) Class 對(duì)象加載到 JVM 中,然后 JVM 再根據(jù)這個(gè)類型信息相關(guān)的 Class 對(duì)象創(chuàng)建我們需要實(shí)例對(duì)象或者提供靜態(tài)變量的引用值。我們可以將 Class 類,稱為類類型,一個(gè) Class 對(duì)象,稱為類類型對(duì)象。
Field類:Java.lang.reflec包中的類,表示類的成員變量,可以用來獲取和設(shè)置類之中的屬性值。
Method類: Java.lang.reflec包中的類,表示類的方法,它可以用來獲取類中的方法信息或者執(zhí)行方法。
Cons:Java.lang.reflec包中的類,表示類的構(gòu)造方法。
總結(jié)一下:反射就是把java類中的各種成分映射成一個(gè)個(gè)的Java對(duì)象
3.2加載過程
加載的時(shí)候:將.class文件讀入內(nèi)存,并為之創(chuàng)建一個(gè)Class對(duì)象

4.反射的使用
? 步驟如下:
- 獲取想要操作的類的Class對(duì)象
- 調(diào)用Class類中的方法
- 使用反射API來操作這些信息
4.1獲取Class對(duì)象
- Object.getClass( ): 返回一個(gè)對(duì)象的運(yùn)行時(shí)類
- 通過類名.class獲?。喝魏螖?shù)據(jù)類型(包括基本數(shù)據(jù)類型)都有一個(gè)“靜態(tài)”的class屬性
- 通過Class類的靜態(tài)方法:Class.forName(String className) (常用)
注意:在運(yùn)行期間,一個(gè)類,只有一個(gè)Class對(duì)象產(chǎn)生。
說明:上述三種方式,常用第三種。
? 第一種:已經(jīng)有了對(duì)象,直接通過對(duì)象去調(diào)用即可,再用反射意義不大。
? 第二種:需要導(dǎo)入類的包,依賴太強(qiáng),不導(dǎo)包就拋編譯錯(cuò)誤。
? 第三種:參數(shù)字符串,可以直接傳入,也可以寫在配置文件中。
代碼實(shí)現(xiàn):
@Test
public void test1() throws ClassNotFoundException {
// 方法1:通過Object.getClass( ),返回一個(gè)對(duì)象的運(yùn)行時(shí)類
Cat cat = new Cat();
Class<? extends Cat> clazz1 = cat.getClass();
System.out.println("方法1:" + clazz1);
// 方法2:通過類名.class獲取
Class clazz2 = Cat.class;
System.out.println("方法2:" + clazz2);
Assert.assertEquals(clazz2, clazz1);
// 方法3:通過Class.forName()獲取
Class clazz3 = Class.forName("com.qyl.spring.resttemplate.domain.Cat");
System.out.println("方法3:" + clazz3);
Assert.assertEquals(clazz3, clazz1);
}
4.2獲取構(gòu)造方法
Cat類:
@Data
//@NoArgsConstructor
//@AllArgsConstructor
//@Builder
public class Cat {
public String name;
private Integer age;
private String sound;
// 默認(rèn)的構(gòu)造方法
Cat(String str) {
System.out.println("默認(rèn)的構(gòu)造方法,s = " + str);
}
// 無參構(gòu)造方法
public Cat() {
System.out.println("調(diào)用了共有的無參構(gòu)造方法執(zhí)行");
}
// 有一個(gè)參數(shù)的構(gòu)造方法
public Cat(char name) {
System.out.println("姓名:" + name);
}
// 有多個(gè)參數(shù)的構(gòu)造方法
public Cat(String name, int age) {
System.out.println("姓名:"+name+"年齡:"+ age);
}
protected Cat(boolean n) {
System.out.println("受保護(hù)的構(gòu)造方法 n = " + n);
}
//私有構(gòu)造方法
private Cat(int age) {
System.out.println("私有的構(gòu)造方法,年齡:"+ age);
this.age = age;
}
}
測(cè)試:
@Test
public void test2() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = Class.forName("com.qyl.spring.resttemplate.domain.Cat");
//獲取所有共有的構(gòu)造方法
System.out.println("-----------所有共有構(gòu)造方法-----------");
Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("\n-----------所有的構(gòu)造方法(包括:私有、受保護(hù)、默認(rèn)、公有)-----------");
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for(Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
System.out.println("\n-----------獲取公有、無參的構(gòu)造方法-----------");
Constructor constructor = clazz.getConstructor();
System.out.println("constructor = " + constructor);
Object obj = constructor.newInstance();
Cat cat = (Cat)obj;
System.out.println(cat);
System.out.println("\n-----------獲取私有構(gòu)造方法,并調(diào)用-----------");
Constructor con = clazz.getDeclaredConstructor(int.class);
System.out.println(con);
//調(diào)用構(gòu)造方法
con.setAccessible(true);//暴力訪問(忽略掉訪問修飾符)
Object o = con.newInstance(3);
System.out.println(o);
}
執(zhí)行結(jié)果:
-----------所有共有構(gòu)造方法-----------
public com.qyl.spring.resttemplate.domain.Cat(java.lang.String,int)
public com.qyl.spring.resttemplate.domain.Cat()
public com.qyl.spring.resttemplate.domain.Cat(char)
-----------所有的構(gòu)造方法(包括:私有、受保護(hù)、默認(rèn)、公有)-----------
private com.qyl.spring.resttemplate.domain.Cat(int)
protected com.qyl.spring.resttemplate.domain.Cat(boolean)
public com.qyl.spring.resttemplate.domain.Cat(java.lang.String,int)
com.qyl.spring.resttemplate.domain.Cat(java.lang.String)
public com.qyl.spring.resttemplate.domain.Cat()
public com.qyl.spring.resttemplate.domain.Cat(char)
-----------獲取公有、無參的構(gòu)造方法-----------
constructor = public com.qyl.spring.resttemplate.domain.Cat()
調(diào)用了共有的無參構(gòu)造方法執(zhí)行
Cat(name=null, age=null, sound=null)
-----------獲取私有構(gòu)造方法,并調(diào)用-----------
private com.qyl.spring.resttemplate.domain.Cat(int)
私有的構(gòu)造方法,年齡:3
Cat(name=null, age=3, sound=null)
4.3獲取屬性
@Test
public void test3() throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 獲取Class對(duì)象
Class clazz = Class.forName("com.qyl.spring.resttemplate.domain.Cat");
// 獲取字段
System.out.println("-----------獲取所有公有的字段-----------");
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("\n-----------獲取所有的字段(包括私有、受保護(hù)、默認(rèn)的)-----------");
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields) {
System.out.println(f);
}
System.out.println("\n-----------獲取公有字段并調(diào)用-----------");
Field f = clazz.getField("name");
System.out.println(f);
//獲取一個(gè)對(duì)象
Object obj = clazz.getConstructor().newInstance();
f.set(obj, "昭君");
Cat cat = (Cat)obj;
System.out.println("驗(yàn)證姓名:" + cat.getName());
System.out.println("\n-----------獲取私有字段并調(diào)用-----------");
f = clazz.getDeclaredField("age");
System.out.println(f);
f.setAccessible(true); // 暴力反射,解除私有限定
f.set(cat, 5);
System.out.println("驗(yàn)證電話:" + cat);
}
運(yùn)行結(jié)果:
-----------獲取所有公有的字段-----------
public java.lang.String com.qyl.spring.resttemplate.domain.Cat.name
-----------獲取所有的字段(包括私有、受保護(hù)、默認(rèn)的)-----------
public java.lang.String com.qyl.spring.resttemplate.domain.Cat.name
private java.lang.Integer com.qyl.spring.resttemplate.domain.Cat.age
private java.lang.String com.qyl.spring.resttemplate.domain.Cat.sound
-----------獲取公有字段并調(diào)用-----------
public java.lang.String com.qyl.spring.resttemplate.domain.Cat.name
調(diào)用了共有的無參構(gòu)造方法執(zhí)行
驗(yàn)證姓名:昭君
-----------獲取私有字段并調(diào)用-----------
private java.lang.Integer com.qyl.spring.resttemplate.domain.Cat.age
驗(yàn)證電話:Cat(name=昭君, age=5, sound=null)
4.4獲取成員屬性
代碼如下:
@Test
public void test4() throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
//1.獲取Class對(duì)象
Class clazz = Class.forName("com.qyl.spring.resttemplate.domain.Cat");
//2.獲取所有公有方法
System.out.println("-----------獲取所有的公有方法-----------");
Method[] methodArray = clazz.getMethods();
for(Method m : methodArray) {
System.out.println(m);
}
System.out.println("\n-----------獲取所有的方法,包括私有的-----------");
methodArray = clazz.getDeclaredMethods();
for(Method m : methodArray) {
System.out.println(m);
}
System.out.println("\n-----------獲取公有的show1()方法-----------");
Method m = clazz.getMethod("show1", String.class);
System.out.println(m);
//實(shí)例化一個(gè)Student對(duì)象
Object obj = clazz.getConstructor().newInstance();
m.invoke(obj, "貂蟬");
System.out.println("\n-----------獲取私有的show4()方法-----------");
m = clazz.getDeclaredMethod("show4", int.class);
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object result = m.invoke(obj, 20);//需要兩個(gè)參數(shù),一個(gè)是要調(diào)用的對(duì)象(獲取有反射),一個(gè)是實(shí)參
System.out.println("返回值:" + result);
}
運(yùn)行結(jié)果:
-----------獲取所有的公有方法-----------
public boolean com.qyl.spring.resttemplate.domain.Cat.equals(java.lang.Object)
public java.lang.String com.qyl.spring.resttemplate.domain.Cat.toString()
public int com.qyl.spring.resttemplate.domain.Cat.hashCode()
public java.lang.String com.qyl.spring.resttemplate.domain.Cat.getName()
public void com.qyl.spring.resttemplate.domain.Cat.setName(java.lang.String)
public void com.qyl.spring.resttemplate.domain.Cat.show1(java.lang.String)
public java.lang.Integer com.qyl.spring.resttemplate.domain.Cat.getAge()
public void com.qyl.spring.resttemplate.domain.Cat.setAge(java.lang.Integer)
public java.lang.String com.qyl.spring.resttemplate.domain.Cat.getSound()
public void com.qyl.spring.resttemplate.domain.Cat.setSound(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
-----------獲取所有的方法,包括私有的-----------
public boolean com.qyl.spring.resttemplate.domain.Cat.equals(java.lang.Object)
public java.lang.String com.qyl.spring.resttemplate.domain.Cat.toString()
public int com.qyl.spring.resttemplate.domain.Cat.hashCode()
public java.lang.String com.qyl.spring.resttemplate.domain.Cat.getName()
public void com.qyl.spring.resttemplate.domain.Cat.setName(java.lang.String)
public void com.qyl.spring.resttemplate.domain.Cat.show1(java.lang.String)
private java.lang.String com.qyl.spring.resttemplate.domain.Cat.show4(int)
protected void com.qyl.spring.resttemplate.domain.Cat.show2()
void com.qyl.spring.resttemplate.domain.Cat.show3()
public java.lang.Integer com.qyl.spring.resttemplate.domain.Cat.getAge()
public void com.qyl.spring.resttemplate.domain.Cat.setAge(java.lang.Integer)
protected boolean com.qyl.spring.resttemplate.domain.Cat.canEqual(java.lang.Object)
public java.lang.String com.qyl.spring.resttemplate.domain.Cat.getSound()
public void com.qyl.spring.resttemplate.domain.Cat.setSound(java.lang.String)
-----------獲取公有的show1()方法-----------
public void com.qyl.spring.resttemplate.domain.Cat.show1(java.lang.String)
調(diào)用了共有的無參構(gòu)造方法執(zhí)行
調(diào)用了:公有的,String參數(shù)的show1(): s = 貂蟬
-----------獲取私有的show4()方法-----------
private java.lang.String com.qyl.spring.resttemplate.domain.Cat.show4(int)
調(diào)用了,私有的,并且有返回值的,int參數(shù)的show4(): age = 20
返回值:age = 20
4.5讀取配置文件信息
@Test
public void test5() throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//通過反射獲取Class對(duì)象
Class clazz = Class.forName(getValue("className")); // "com.qyl.spring.resttemplate.domain.Book"
//2獲取show()方法
Method m = clazz.getMethod(getValue("methodName")); // show
//3.調(diào)用show()方法
m.invoke(clazz.getConstructor().newInstance());
}
//此方法接收一個(gè)key,在配置文件中獲取相應(yīng)的value
private String getValue(String key) throws IOException {
Properties pro = new Properties();//獲取配置文件的對(duì)象
FileReader in = new FileReader("src/test.properties");//獲取輸入流
pro.load(in);//將流加載到配置文件對(duì)象中
in.close();
return pro.getProperty(key);//返回根據(jù)key獲取的value值
}
Book.java
@Data
public class Book {
public void read() {
System.out.println("this is read()");
}
}
配置文件:
className = com.qyl.spring.resttemplate.domain.Book
methodName = read
運(yùn)行結(jié)果:
this is read()
好處:
當(dāng)我們升級(jí)這個(gè)系統(tǒng)時(shí),不再需要Book類時(shí),而要新增一個(gè)Book_New的類時(shí),這時(shí)只需要更改test.properties的文件內(nèi)容就可以了。
4.6通過反射越過泛型檢查
代碼:
@Test
public void test6() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ArrayList<String> strList = new ArrayList<>();
strList.add("qyl");
strList.add("nn");
//獲取ArrayList的Class對(duì)象,反向的調(diào)用add()方法,添加數(shù)據(jù)
Class listClazz = strList.getClass(); //得到 strList 對(duì)象的字節(jié)碼 對(duì)象
//獲取add()方法
Method m = listClazz.getMethod("add", Object.class);
//調(diào)用add()方法
m.invoke(strList, 100);
//遍歷集合
for(Object obj : strList){
System.out.println(obj.getClass() + ":" + obj);
}
}
運(yùn)行結(jié)果:
class java.lang.String:qyl
class java.lang.String:nn
class java.lang.Integer:100