反射的發展歷史
1996年01月23日,jdk 1.0版本發佈,代號爲Oak(橡樹)。
這個代號爲Oak(橡樹)的版本,在發佈後的第二年,1997年02月19日,發佈jdk 1.1版本,這次版本發佈中引入了反射機制。
關於反射機制,由於年代久遠,能搜索到對於反射機制的記載少之又少,能找到最爲久遠的是一篇題爲《Using Java Reflection》的文章,發表於 1998年1月,文中提到:反射是一個可以獲取java類、屬性的一個工具,因爲它是動態加載的
。
而在另外一篇文章《A Button is a Bean》裏解釋道,反射是爲了能把一個類的屬性可視化的展示給用戶,如下圖所示:
通俗的解釋就是:無論是公有還是私有的方法、屬性、構造方法,全都可以用反射進行獲取、進行賦值、調用。聽到這個解釋,是不是感覺反射很強。
正因爲反射的強大,在java世界裏運用的地方有很多,比如:Java類加載和初始化、Java中RTTI、Spring的IOC,。
如此廣泛的運用,只能說反射除了強,用起來肯定很爽。我想起我的同事,IT界的刁民,總是熱衷於反射。
他在講解他是如何運用反射時,嘴角總是壓抑不住的微笑,這種迷戀反射的樣子,像極了愛情。
正所謂:反射一開,誰都不愛。(傲嬌)
下面就看看反射究竟是如何在程序中使用的。
反射的概述和使用
反射的概述
JAVA反射機制是在運行狀態中,
對於任意一個類,都能夠知道這個類的所有屬性和方法;
對於任意一個對象,都能夠調用它的任意一個方法和屬性;
這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。
我們知道class文件是在編譯的時候生成的,Class對象是將class文件讀入內存,併爲之創建一個Class對象。
Class 沒有公共構造方法。Class 對象是在加載類時由 Java 虛擬機以及通過調用類加載器中的defineClass 方法自動構造的。也就是這不需要我們自己去處理創建,JVM已經幫我們創建好了。
Class類裏面,包含了一個類應有的所有描述,包括:
字段:Field.java
方法:Method.java
構造方法:Constructor.java
等等...
知道了Class類裏面包含了哪些內容之後,再看一下new一個對象的究竟會發生那些過程:
反射的使用
這裏使用一個Animal類來作爲示範,可以看到這個類裏的成員變量、方法、構造方法的訪問修飾符既有public、也有private的。下面就將使用反射獲取不同修飾符修飾的成員變量、方法、構造方法。
package com.shuai.ioc.ref;
public class Animal {
/**
* 動物名字
*/
public String name;
/**
* 動物年齡
*/
protected int age;
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
/**
* 默認的構造方法
*
* @param name
*/
Animal(String name) {
System.out.println("執行了" + "默認的構造方法 " + name);
}
/**
* 無參構造方法
*/
public Animal() {
System.out.println("執行了" + "無參構造方法 ");
}
/**
* 有一個參數的構造方法
*
* @param name
*/
public Animal(char name) {
System.out.println("執行了" + "有一個參數的構造方法 name:" + name);
}
/**
* 有多個參數的構造方法
*
* @param name
* @param age
*/
public Animal(String name, int age) {
System.out.println("執行了" + "有多個參數的構造方法 name:" + name + "age:" + age);
}
/**
* protected的構造方法
*
* @param n
*/
protected Animal(boolean n) {
System.out.println("執行了" + "受保護的構造方法 n:" + n);
}
/**
* 私有構造方法
*
* @param age
*/
private Animal(int age) {
System.out.println("執行了" + "私有構造方法 age:" + age);
this.name = "私有構造方法調用成功";
this.age = age;
}
/**
* 公有方法
*
* @param s
*/
public void public1(String s) {
System.out.println("調用了" + "公有的方法" + ": public1 , s:" + s);
}
/**
* protected的方法
*/
protected void protected2() {
System.out.println("調用了" + "protected的方法" + ": protected2 ");
}
/**
* 友好的方法
*/
void friendly1() {
System.out.println("調用了" + "友好的方法" + ": friendly1 ");
}
/**
* 私有方法
*
* @param age
* @return
*/
private String private1(int age) {
System.out.println("調用了" + "私有方法" + ": private1 ,age:" + age);
return age + "";
}
}
用反射獲取類的構造方法
在Class類中,提供一系列獲取被反射類構造方法的方法。
- 批量獲取構造方法的方法
-
public Constructor[] getConstructors()
:所有"公有的"構造方法 -
public Constructor[] getDeclaredConstructors()
:獲取所有的構造方法(包括私有、受保護、默認、公有)
-
- 獲取單個的方法,並調用
-
public Constructor getConstructor(Class... parameterTypes)
:獲取單個的"公有的"構造方法 -
public Constructor getDeclaredConstructor(Class... parameterTypes)
:獲取"某個構造方法"可以是私有的,或受保護、默認、公有;
-
- 調用構造方法
- newInstance(Object... initargs)
package com.shuai.ioc.ref;
import com.shuai.ioc.Book;
import java.lang.reflect.Constructor;
public class ConstructorsTest {
public static void main(String[] args) throws Exception {
//1.加載Class對象
Class clazz = Class.forName("com.shuai.ioc.ref.Animal");
//2.獲取所有公有構造方法
System.out.println("所有公有構造方法");
Constructor[] conArray = clazz.getConstructors();
for (Constructor c : conArray) {
System.out.println(c);
}
// 所有的構造方法,公有、私有都行
System.out.println("");
System.out.println("所有的構造方法,包括:私有、受保護、默認、公有");
conArray = clazz.getDeclaredConstructors();
for (Constructor c : conArray) {
System.out.println(c);
}
// 獲取公有、無參的構造方法
System.out.println("");
System.out.println("獲取公有、無參的構造方法");
Constructor con = clazz.getConstructor(null);
System.out.println("con = " + con);
//調用構造方法
Object obj = con.newInstance();
// 獲取私有構造方法
System.out.println("");
System.out.println("獲取私有構造方法,並調用");
con = clazz.getDeclaredConstructor(int.class);
System.out.println(con);
//暴力訪問,忽略掉訪問修飾符
con.setAccessible(true);
//調用構造方法
Animal animal = (Animal) con.newInstance(1);
System.out.println(animal.toString());
}
}
用反射獲取類的方法
在Class類中,提供一系列獲取被反射類構造方法的方法。
- 批量的
-
public Method[] getMethods()
:獲取所有"公有方法";(包含了父類的方法也包含Object類) -
public Method[] getDeclaredMethods()
:獲取所有的成員方法,包括私有的(不包括繼承的)
-
- 獲取單個的
-
public Method getMethod(String name,Class<?>... parameterTypes)
,name
: 方法名;Class ...
:形參的Class類型對象 -
public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
,obj
:要調用方法的對象;args
:調用方式時所傳遞的實參;
-
- 調用方法
-
public Object invoke(Object obj,Object... args)
,obj
:要調用方法的對象;args
:調用方式時所傳遞的實參;
-
package com.shuai.ioc.ref;
import java.lang.reflect.Method;
public class MethodClassTest {
public static void main(String[] args) throws Exception {
//1.獲取Class對象
Class stuClass = Class.forName("com.shuai.ioc.ref.Animal");
//2.獲取所有公有方法
System.out.println("獲取所有 公有 方法");
stuClass.getMethods();
Method[] methodArray = stuClass.getMethods();
for (Method m : methodArray) {
System.out.println(m);
}
System.out.println();
System.out.println("獲取所有的方法,包括私有的");
methodArray = stuClass.getDeclaredMethods();
for (Method m : methodArray) {
System.out.println(m);
}
System.out.println();
System.out.println("獲取公有的public1()方法");
Method m = stuClass.getMethod("public1", String.class);
System.out.println(m);
//實例化一個Student對象
Object obj = stuClass.getConstructor().newInstance();
m.invoke(obj, "this is name value");
System.out.println();
System.out.println("獲取私有的private1()方法");
m = stuClass.getDeclaredMethod("private1", int.class);
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object result = m.invoke(obj, 20);//需要兩個參數,一個是要調用的對象(獲取有反射),一個是實參
System.out.println("返回值:" + result);
}
}
用反射獲取類的字段
在Class類中,提供一系列獲取被反射類構造方法的方法。
- 批量的
-
Field[] getFields()
:獲取所有的"公有字段" -
Field[] getDeclaredFields()
:獲取所有字段,包括:私有、受保護、默認、公有;
-
- 獲取單個的
-
public Field getField(String fieldName)
:獲取某個"公有的"字段; -
public Field getDeclaredField(String fieldName)
:獲取某個字段(可以是私有的)
-
- 設置字段的值
-
public void set(Object obj,Object value)
:obj
:要設置的字段所在的對象;value
:要爲字段設置的值;
-
package com.shuai.ioc.ref;
import java.lang.reflect.Field;
public class FieldsTest {
public static void main(String[] args) throws Exception {
//1.獲取Class對象
Class animalClass = Class.forName("com.shuai.ioc.ref.Animal");
//2.獲取字段
System.out.println("獲取所有公有的字段");
Field[] fieldArray = animalClass.getFields();
for (Field f : fieldArray) {
System.out.println(f);
}
System.out.println();
System.out.println("獲取所有的字段(包括私有、受保護、默認的)");
fieldArray = animalClass.getDeclaredFields();
for (Field f : fieldArray) {
System.out.println(f);
}
System.out.println();
System.out.println("獲取公有字段並調用");
Field f = animalClass.getField("name");
System.out.println(f);
//獲取一個對象
Object obj = animalClass.getConstructor().newInstance();//產生Student對象--》Student stu = new Student();
//爲字段設置值
f.set(obj, "dog");//爲Student對象中的name屬性賦值--》stu.name = "劉德華"
//驗證
Animal stu = (Animal) obj;
System.out.println("驗證name:" + stu.name);
System.out.println();
System.out.println("獲取私有字段並調用");
f = animalClass.getDeclaredField("name");
System.out.println(f);
f.setAccessible(true);//暴力反射,解除私有限定
f.set(obj, "this is name value");
System.out.println("驗證name:" + stu);
}
}
反射越過泛型檢查
編寫代碼時,如果我們設置容器list爲String類型,在調用add方法插入數據時入參傳了其他類型,編譯時會無法成功,但是通過反射卻可以執行,實例代碼:
package com.shuai.ioc.ref;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/*
* 通過反射越過泛型檢查
*
*/
public class IgnoreType {
public static void main(String[] args) throws Exception {
List<String> list = new ArrayList<>();
list.add("one");
//反射獲取list對象
Class listClass = list.getClass();
// 調用list對象的add方法
Method m = listClass.getMethod("add", Object.class);
m.invoke(list, 100);
//輸出驗證
for (Object obj : list) {
System.out.println(obj);
}
}
}