Java基礎:反射

參考原文連接:
Java基礎篇:反射機制詳解
Java基礎之—反射(非常重要)
Java高級特性——反射

一、什麼是反射

JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。
要想解剖一個類,必須先要獲取到該類的字節碼文件對象。而解剖使用的就是Class類中的方法.所以先要獲取到每一個字節碼文件對應的Class類型的對象.

反射就是把java類中的各種成分映射成一個個的Java對象
例如:一個類有:成員變量、方法、構造方法、包等等信息,利用反射技術可以對一個類進行解剖,把個個組成部分映射成一個個對象。
其實:一個類中這些成員方法、構造方法、在加入類中都有一個類來描述

二、反射的原理

下是類的正常加載過程:反射的原理在與class對象。
熟悉一下加載的時候:Class對象的由來是將class文件讀入內存,併爲之創建一個Class對象。
在這裏插入圖片描述
其中這個Class對象很特殊。我們先了解一下這個Class類

三、查看Class類在java中的api詳解(1.7的API)

如何閱讀java中的api詳見java基礎之——String字符串處理
在這裏插入圖片描述
Class 類的實例表示正在運行的 Java 應用程序中的類和接口。也就是jvm中有N多的實例每個類都有該Class對象。(包括基本數據類型)
Class 沒有公共構造方法。Class 對象是在加載類時由 Java 虛擬機以及通過調用類加載器中的defineClass 方法自動構造的。也就是這不需要我們自己去處理創建,JVM已經幫我們創建好了。

四、反射的優缺點

優點:
使用反射,我們就可以在運行時獲得類的各種內容,進行反編譯,對於Java這種先編譯再運行的語言,能夠讓我們很方便的創建靈活的代碼,這些代碼可以在運行時裝配,無需在組件之間進行源代碼的鏈接,更加容易實現面向對象。

缺點:
(1)反射會消耗一定的系統資源,因此,如果不需要動態地創建一個對象,那麼就不需要用反射;
(2)反射調用方法時可以忽略權限檢查,因此可能會破壞封裝性而導致安全問題。

五、反射的用途

1、反編譯:.class–>.java

2、通過反射機制訪問java對象的屬性,方法,構造方法等

3、當我們在使用IDE,比如Ecplise時,當我們輸入一個對象或者類,並想調用他的屬性和方法是,一按點號,編譯器就會自動列出他的屬性或者方法,這裏就是用到反射。

4、反射最重要的用途就是開發各種通用框架。比如很多框架(Spring)都是配置化的(比如通過XML文件配置Bean),爲了保證框架的通用性,他們可能需要根據配置文件加載不同的類或者對象,調用不同的方法,這個時候就必須使用到反射了,運行時動態加載需要的加載的對象。

5、例如,在使用Strut2框架的開發過程中,我們一般會在struts.xml裏去配置Action,
比如

<action name="login" class="org.ScZyhSoft.test.action.SimpleLoginAction" method="execute">   
    <result>/shop/shop-index.jsp</result>           
    <result name="error">login.jsp</result>       
</action>

比如我們請求login.action時,那麼StrutsPrepareAndExecuteFilter就會去解析struts.xml文件,從action中查找出name爲login的Action,並根據class屬性創建SimpleLoginAction實例,並用Invoke方法來調用execute方法,這個過程離不開反射。配置文件與Action建立了一種映射關係,當View層發出請求時,請求會被StrutsPrepareAndExecuteFilter攔截,然後StrutsPrepareAndExecuteFilter會去動態地創建Action實例。

比如
加載數據庫驅動的,用到的也是反射。

Class.forName("com.mysql.jdbc.Driver"); // 動態加載mysql驅動

六、反射機制常用的類:

類名 用途
Class類 代表類的實體,在運行的Java應用程序中表示類和接口
Field類 代表類的成員變量(成員變量也稱爲類的屬性)
Method類 代表類的方法
Constructor類 代表類的構造方法

Class類
Class代表類的實體,在運行的Java應用程序中表示類和接口。在這個類中提供了很多有用的方法,這裏對他們簡單的分類介紹。

  • 獲得類相關的方法
方法 用途
asSubclass(Class<U> clazz) 把傳遞的類的對象轉換成代表其子類的對象
Cast 把對象轉換成代表類或是接口的對象
getClassLoader() 獲得類的加載器
getClasses() 返回一個數組,數組中包含該類中所有公共類和接口類的對象
getDeclaredClasses() 返回一個數組,數組中包含該類中所有類和接口類的對象
forName(String className) 根據類名返回類的對象
getName() 獲得類的完整路徑名字
newInstance() 創建類的實例
getPackage() 獲得類的包
getSimpleName() 獲得類的名字
getSuperclass() 獲得當前類繼承的父類的名字
getInterfaces() 獲得當前類實現的類或是接口
  • 獲得類中屬性相關的方法
方法 用途
getField(String name) 獲得某個公有的屬性對象
getFields() 獲得所有公有的屬性對象
getDeclaredField(String name) 獲得某個屬性對象
getDeclaredFields() 獲得所有屬性對象
  • 獲得類中註解相關的方法
方法 用途
getAnnotation(Class<A> annotationClass) 返回該類中與參數類型匹配的公有註解對象
getAnnotations() 返回該類所有的公有註解對象
getDeclaredAnnotation(Class<A> annotationClass) 返回該類中與參數類型匹配的所有註解對象
getDeclaredAnnotations() 返回該類所有的註解對象
  • 獲得類中構造器相關的方法
方法 用途
getConstructor(Class…<?> parameterTypes) 獲得該類中與參數類型匹配的公有構造方法
getConstructors() 獲得該類的所有公有構造方法
getDeclaredConstructor(Class…<?> parameterTypes) 獲得該類中與參數類型匹配的構造方法
getDeclaredConstructors() 獲得該類所有構造方法
  • 獲得類中方法相關的方法
方法 用途
getMethod(String name, Class…<?> parameterTypes) 獲得該類某個公有的方法
getMethods() 獲得該類所有公有的方法
getDeclaredMethod(String name, Class…<?> parameterTypes) 獲得該類某個方法
getDeclaredMethods() 獲得該類所有方法
  • 類中其他重要的方法
方法 用途
isAnnotation() 如果是註解類型則返回true
isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果是指定類型註解類型則返回true
isAnonymousClass() 如果是匿名類則返回true
isArray() 如果是一個數組類則返回true
isEnum() 如果是枚舉類則返回true
isInstance(Object obj) 如果obj是該類的實例則返回true
isInterface() 如果是接口類則返回true
isLocalClass() 如果是局部類則返回true
isMemberClass() 如果是內部類則返回true

Field類
Field代表類的成員變量(成員變量也稱爲類的屬性)。

方法 用途
equals(Object obj) 屬性與obj相等則返回true
get(Object obj) 獲得obj中對應的屬性值
set(Object obj, Object value) 設置obj中對應屬性值

Method類
Method代表類的方法。

方法 用途
invoke(Object obj, Object… args) 傳遞object對象及參數調用該對象對應的方法

Constructor類
Constructor代表類的構造方法。

方法 用途
newInstance(Object… initargs) 根據傳遞的參數創建類的對象

七、反射的基本使用

1、獲得Class:主要有三種方法:

(1)Object–>getClass

(2)任何數據類型(包括基本的數據類型)都有一個“靜態”的class屬性

(3)通過class類的靜態方法:forName(String className)(最常用)

package fanshe;
 
public class Fanshe {
	public static void main(String[] args) {
		//第一種方式獲取Class對象  
		Student stu1 = new Student();//這一new 產生一個Student對象,一個Class對象。
		Class stuClass = stu1.getClass();//獲取Class對象
		System.out.println(stuClass.getName());
		
		//第二種方式獲取Class對象
		Class stuClass2 = Student.class;
		System.out.println(stuClass == stuClass2);//判斷第一種方式獲取的Class對象和第二種方式獲取的是否是同一個
		
		//第三種方式獲取Class對象
		try {
			Class stuClass3 = Class.forName("fanshe.Student");//注意此字符串必須是真實路徑,就是帶包名的類路徑,包名.類名
			System.out.println(stuClass3 == stuClass2);//判斷三種方式是否獲取的是同一個Class對象
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
	}
}

注意,在運行期間,一個類,只有一個Class對象產生,所以打印結果都是true;

三種方式中,常用第三種,第一種對象都有了還要反射幹什麼,第二種需要導入類包,依賴太強,不導包就拋編譯錯誤。一般都使用第三種,一個字符串可以傳入也可以寫在配置文件中等多種方法。

2、判斷是否爲某個類的示例:

一般的,我們使用instanceof 關鍵字來判斷是否爲某個類的實例。同時我們也可以藉助反射中Class對象的isInstance()方法來判斷時候爲某個類的實例,他是一個native方法。

public native boolean isInstance(Object obj);

3、創建實例:通過反射來生成對象主要有兩種方法:
(1)使用Class對象的newInstance()方法來創建Class對象對應類的實例。

Class<?> c = String.class;
Object str = c.newInstance();

(2)先通過Class對象獲取指定的Constructor對象,再調用Constructor對象的newInstance()方法來創建對象,這種方法可以用指定的構造器構造類的實例。

//獲取String的Class對象
Class<?> str = String.class;
//通過Class對象獲取指定的Constructor構造器對象
Constructor constructor=c.getConstructor(String.class);
//根據構造器創建實例:
Object obj = constructor.newInstance(“hello reflection”);

4、通過反射獲取構造方法並使用:
(1)批量獲取的方法:
public Constructor[] getConstructors():所有"公有的"構造方法
public Constructor[] getDeclaredConstructors():獲取所有的構造方法(包括私有、受保護、默認、公有)

(2)單個獲取的方法,並調用:
public Constructor getConstructor(Class… parameterTypes):獲取單個的"公有的"構造方法:
public Constructor getDeclaredConstructor(Class… parameterTypes):獲取"某個構造方法"可以是私有的,或受保護、默認、公有;

(3) 調用構造方法:

Constructor–>newInstance(Object… initargs)
newInstance是 Constructor類的方法(管理構造函數的類)

api的解釋爲:newInstance(Object… initargs) ,使用此 Constructor 對象表示的構造方法來創建該構造方法的聲明類的新實例,並用指定的初始化參數初始化該實例。

它的返回值是T類型,所以newInstance是創建了一個構造方法的聲明類的新實例對象,併爲之調用。

例子:
Student類:共六個構造方法。

package fanshe;
public class Student {
	//---------------構造方法-------------------
	//(默認的構造方法)
	Student(String str){
		System.out.println("(默認)的構造方法 s = " + str);
	}
	//無參構造方法
	public Student(){
		System.out.println("調用了公有、無參構造方法執行了。。。");
	}
	//有一個參數的構造方法
	public Student(char name){
		System.out.println("姓名:" + name);
	}
	//有多個參數的構造方法
	public Student(String name ,int age){
		System.out.println("姓名:"+name+"年齡:"+ age);//這的執行效率有問題,以後解決。
	}
	//受保護的構造方法
	protected Student(boolean n){
		System.out.println("受保護的構造方法 n = " + n);
	}
	//私有構造方法
	private Student(int age){
		System.out.println("私有的構造方法   年齡:"+ age);
	}
}

測試類:

package fanshe;
import java.lang.reflect.Constructor;
 
/*
 * 通過Class對象可以獲取某個類中的:構造方法、成員變量、成員方法;並訪問成員;
 * 
 * 1.獲取構造方法:
 * 		1).批量的方法:
 * 			public Constructor[] getConstructors():所有"公有的"構造方法
            public Constructor[] getDeclaredConstructors():獲取所有的構造方法(包括私有、受保護、默認、公有)
 * 		2).獲取單個的方法,並調用:
 * 			public Constructor getConstructor(Class... parameterTypes):獲取單個的"公有的"構造方法:
 * 			public Constructor getDeclaredConstructor(Class... parameterTypes):獲取"某個構造方法"可以是私有的,或受保護、默認、公有; 		
 * 		3).調用構造方法:
 * 			Constructor-->newInstance(Object... initargs)
 */
public class Constructors {
 
	public static void main(String[] args) throws Exception {
		//1.加載Class對象
		Class clazz = Class.forName("fanshe.Student");
		
		//2.獲取所有公有構造方法
		System.out.println("**********************所有公有構造方法*********************************");
		Constructor[] conArray = clazz.getConstructors();
		for(Constructor c : conArray){
			System.out.println(c);
		}
		
		System.out.println("************所有的構造方法(包括:私有、受保護、默認、公有)***************");
		conArray = clazz.getDeclaredConstructors();
		for(Constructor c : conArray){
			System.out.println(c);
		}
		
		System.out.println("*****************獲取公有、無參的構造方法*******************************");
		Constructor con = clazz.getConstructor(null);
		//1>、因爲是無參的構造方法所以類型是一個null,不寫也可以:這裏需要的是一個參數的類型,切記是類型
		//2>、返回的是描述這個無參構造函數的類對象。
		System.out.println("con = " + con);
 
		//調用構造方法
		Object obj = con.newInstance();
	//	System.out.println("obj = " + obj);
	//	Student stu = (Student)obj;
		
		System.out.println("******************獲取私有構造方法,並調用*******************************");
		con = clazz.getDeclaredConstructor(char.class);
		System.out.println(con);
		//調用構造方法
		con.setAccessible(true);//暴力訪問(忽略掉訪問修飾符)
		obj = con.newInstance('男');
	}
}

控制檯輸出:

**********************所有公有構造方法*********************************
public fanshe.Student(java.lang.String,int)
public fanshe.Student(char)
public fanshe.Student()
************所有的構造方法(包括:私有、受保護、默認、公有)***************
private fanshe.Student(int)
protected fanshe.Student(boolean)
public fanshe.Student(java.lang.String,int)
public fanshe.Student(char)
public fanshe.Student()
fanshe.Student(java.lang.String)
*****************獲取公有、無參的構造方法*******************************
con = public fanshe.Student()
調用了公有、無參構造方法執行了。。。
******************獲取私有構造方法,並調用*******************************
public fanshe.Student(char)
姓名:男

5、獲取成員變量並調用:
Student類:

package fanshe.field;
 
public class Student {
	public Student(){
		
	}
	//**********字段*************//
	public String name;
	protected int age;
	char sex;
	private String phoneNum;
	
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + ", sex=" + sex
				+ ", phoneNum=" + phoneNum + "]";
	}
}

測試類:

package fanshe.field;
import java.lang.reflect.Field;
/*
 * 獲取成員變量並調用:
 * 
 * 1.批量的
 * 		1).Field[] getFields():獲取所有的"公有字段"
 * 		2).Field[] getDeclaredFields():獲取所有字段,包括:私有、受保護、默認、公有;
 * 2.獲取單個的:
 * 		1).public Field getField(String fieldName):獲取某個"公有的"字段;
 * 		2).public Field getDeclaredField(String fieldName):獲取某個字段(可以是私有的)
 * 
 * 	 設置字段的值:
 * 		Field --> public void set(Object obj,Object value):
 * 					參數說明:
 * 					1.obj:要設置的字段所在的對象;
 * 					2.value:要爲字段設置的值;
 */
public class Fields {
 
		public static void main(String[] args) throws Exception {
			//1.獲取Class對象
			Class stuClass = Class.forName("fanshe.field.Student");
			//2.獲取字段
			System.out.println("************獲取所有公有的字段********************");
			Field[] fieldArray = stuClass.getFields();
			for(Field f : fieldArray){
				System.out.println(f);
			}
			System.out.println("************獲取所有的字段(包括私有、受保護、默認的)********************");
			fieldArray = stuClass.getDeclaredFields();
			for(Field f : fieldArray){
				System.out.println(f);
			}
			System.out.println("*************獲取公有字段**並調用***********************************");
			Field f = stuClass.getField("name");
			System.out.println(f);
			//獲取一個對象
			Object obj = stuClass.getConstructor().newInstance();//產生Student對象--》Student stu = new Student();
			//爲字段設置值
			f.set(obj, "劉德華");//爲Student對象中的name屬性賦值--》stu.name = "劉德華"
			//驗證
			Student stu = (Student)obj;
			System.out.println("驗證姓名:" + stu.name);
			
			
			System.out.println("**************獲取私有字段****並調用********************************");
			f = stuClass.getDeclaredField("phoneNum");
			System.out.println(f);
			f.setAccessible(true);//暴力反射,解除私有限定
			f.set(obj, "18888889999");
			System.out.println("驗證電話:" + stu);
			
		}
	}

控制檯輸出:

************獲取所有公有的字段********************
public java.lang.String fanshe.field.Student.name
************獲取所有的字段(包括私有、受保護、默認的)********************
public java.lang.String fanshe.field.Student.name
protected int fanshe.field.Student.age
char fanshe.field.Student.sex
private java.lang.String fanshe.field.Student.phoneNum
*************獲取公有字段**並調用***********************************
public java.lang.String fanshe.field.Student.name
驗證姓名:劉德華
**************獲取私有字段****並調用********************************
private java.lang.String fanshe.field.Student.phoneNum
驗證電話:Student [name=劉德華, age=0, sex=

6、獲取成員方法並調用:

Student類:

package fanshe.method;
 
public class Student {
	//**************成員方法***************//
	public void show1(String s){
		System.out.println("調用了:公有的,String參數的show1(): s = " + s);
	}
	protected void show2(){
		System.out.println("調用了:受保護的,無參的show2()");
	}
	void show3(){
		System.out.println("調用了:默認的,無參的show3()");
	}
	private String show4(int age){
		System.out.println("調用了,私有的,並且有返回值的,int參數的show4(): age = " + age);
		return "abcd";
	}
}

測試類:

package fanshe.method;
import java.lang.reflect.Method;
 
/*
 * 獲取成員方法並調用:
 * 
 * 1.批量的:
 * 		public Method[] getMethods():獲取所有"公有方法";(包含了父類的方法也包含Object類)
 * 		public Method[] getDeclaredMethods():獲取所有的成員方法,包括私有的(不包括繼承的)
 * 2.獲取單個的:
 * 		public Method getMethod(String name,Class<?>... parameterTypes):
 * 					參數:
 * 						name : 方法名;
 * 						Class ... : 形參的Class類型對象
 * 		public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
 * 
 * 	 調用方法:
 * 		Method --> public Object invoke(Object obj,Object... args):
 * 					參數說明:
 * 					obj : 要調用方法的對象;
 * 					args:調用方式時所傳遞的實參;
):
 */
public class MethodClass {
 
	public static void main(String[] args) throws Exception {
		//1.獲取Class對象
		Class stuClass = Class.forName("fanshe.method.Student");
		//2.獲取所有公有方法
		System.out.println("***************獲取所有的”公有“方法*******************");
		stuClass.getMethods();
		Method[] methodArray = stuClass.getMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
		System.out.println("***************獲取所有的方法,包括私有的*******************");
		methodArray = stuClass.getDeclaredMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
		System.out.println("***************獲取公有的show1()方法*******************");
		Method m = stuClass.getMethod("show1", String.class);
		System.out.println(m);
		//實例化一個Student對象
		Object obj = stuClass.getConstructor().newInstance();
		m.invoke(obj, "劉德華");
		
		System.out.println("***************獲取私有的show4()方法******************");
		m = stuClass.getDeclaredMethod("show4", int.class);
		System.out.println(m);
		m.setAccessible(true);//解除私有限定
		Object result = m.invoke(obj, 20);//需要兩個參數,一個是要調用的對象(獲取有反射),一個是實參
		System.out.println("返回值:" + result);	
	}
}

控制檯輸出:

***************獲取所有的”公有“方法*******************
public void fanshe.method.Student.show1(java.lang.String)
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 void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
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 void fanshe.method.Student.show1(java.lang.String)
private java.lang.String fanshe.method.Student.show4(int)
protected void fanshe.method.Student.show2()
void fanshe.method.Student.show3()
***************獲取公有的show1()方法*******************
public void fanshe.method.Student.show1(java.lang.String)
調用了:公有的,String參數的show1(): s = 劉德華
***************獲取私有的show4()方法******************
private java.lang.String fanshe.method.Student.show4(int)
調用了,私有的,並且有返回值的,int參數的show4(): age = 20
返回值:abcd

7、反射main方法:

Student類:

package fanshe.main;
 
public class Student {
	public static void main(String[] args) {
		System.out.println("main方法執行了。。。");
	}
}

測試類:

package fanshe.main;
import java.lang.reflect.Method;
 
/**
 * 獲取Student類的main方法、不要與當前的main方法搞混了
 */
public class Main {
	
	public static void main(String[] args) {
		try {
			//1、獲取Student對象的字節碼
			Class clazz = Class.forName("fanshe.main.Student");
			
			//2、獲取main方法
			 Method methodMain = clazz.getMethod("main", String[].class);//第一個參數:方法名稱,第二個參數:方法形參的類型,
			//3、調用main方法
			// methodMain.invoke(null, new String[]{"a","b","c"});
			 //第一個參數,對象類型,因爲方法是static靜態的,所以爲null可以,第二個參數是String數組,這裏要注意在jdk1.4時是數組,jdk1.5之後是可變參數
			 //這裏拆的時候將  new String[]{"a","b","c"} 拆成3個對象。。。所以需要將它強轉。
			 methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一
			// methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

控制檯輸出:

main方法執行了。。。

8、利用反射創建數值:

數組在Java裏是比較特殊的一種類型,它可以賦值給一個Object Reference。

public static void testArray() throws ClassNotFoundException {
        Class<?> cls = Class.forName("java.lang.String");
        Object array = Array.newInstance(cls,25);
        //往數組裏添加內容
        Array.set(array,0,"hello");
        Array.set(array,1,"Java");
        Array.set(array,2,"fuck");
        Array.set(array,3,"Scala");
        Array.set(array,4,"Clojure");
        //獲取某一項的內容
        System.out.println(Array.get(array,3));
    }

9、反射方法的其他使用–通過反射運行配置文件內容:

Student類:

public class Student {
	public void show(){
		System.out.println("is show()");
	}
}

配置文件以txt文件爲例子:

className = cn.fanshe.Student
methodName = show

測試類:

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;
 
/*
 * 我們利用反射和配置文件,可以使:應用程序更新時,對源碼無需進行任何修改
 * 我們只需要將新類發送給客戶端,並修改配置文件即可
 */
public class Demo {
	public static void main(String[] args) throws Exception {
		//通過反射獲取Class對象
		Class stuClass = Class.forName(getValue("className"));//"cn.fanshe.Student"
		//2獲取show()方法
		Method m = stuClass.getMethod(getValue("methodName"));//show
		//3.調用show()方法
		m.invoke(stuClass.getConstructor().newInstance());
		
	}
	
	//此方法接收一個key,在配置文件中獲取相應的value
	public static String getValue(String key) throws IOException{
		Properties pro = new Properties();//獲取配置文件的對象
		FileReader in = new FileReader("pro.txt");//獲取輸入流
		pro.load(in);//將流加載到配置文件對象中
		in.close();
		return pro.getProperty(key);//返回根據key獲取的value值
	}
}

控制檯輸出:

is show()

需求:

當我們升級這個系統時,不要Student類,而需要新寫一個Student2的類時,這時只需要更改pro.txt的文件內容就可以了。代碼就一點不用改動。

public class Student2 {
	public void show2(){
		System.out.println("is show2()");
	}
}

配置文件更改爲:

className = cn.fanshe.Student2
methodName = show2

10、反射方法的其他使用–通過反射越過泛型檢查:

泛型用在編譯期,編譯過後泛型擦除(消失掉),所以是可以通過反射越過泛型檢查的

測試類:

import java.lang.reflect.Method;
import java.util.ArrayList;
 
/*
 * 通過反射越過泛型檢查
 * 例如:有一個String泛型的集合,怎樣能向這個集合中添加一個Integer類型的值?
 */
public class Demo {
	public static void main(String[] args) throws Exception{
		ArrayList<String> strList = new ArrayList<>();
		strList.add("aaa");
		strList.add("bbb");
		
	//	strList.add(100);
		//獲取ArrayList的Class對象,反向的調用add()方法,添加數據
		Class listClass = strList.getClass(); //得到 strList 對象的字節碼 對象
		//獲取add()方法
		Method m = listClass.getMethod("add", Object.class);
		//調用add()方法
		m.invoke(strList, 100);
		
		//遍歷集合
		for(Object obj : strList){
			System.out.println(obj);
		}
	}
}

控制檯輸出:

aaa
bbb
100
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章