【Java】反射基礎——示範詳解

目錄

反射概述

Class對象的由來

類加載器

JVM自帶的類加載器的組成

類什麼時候被加載(進入內存)

類的生命週期

引用關係

類的卸載

反射

Class對象的基本使用

獲取Class對象的3中方式

反射獲取構造方法並創建對象

反射獲取成員字段

反射獲取成員方法

反射練習

一、通過反射越過泛型檢查

二、通過配置文件創建指定對象、並調用指定方法


反射概述

JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有成員(公有、私有、默認、保護);對於任意一個對象,都能夠調用它的任意一個方法和屬性;

而對類的解剖類就是.class字節碼文件的Class對象。

Class對象解剖包括:成員變量、方法、構造方法、包等等信息,利用反射技術可以對一個類進行解剖。

Class對象的由來

類加載器

當程序要使用某個類時,如果該類還未被加載到內存中,則系統會通過加載,連接,初始化三個步驟來實現對這個類進行初始化。

加載:

  • 將class文件讀入內存,併爲之建立一個Class對象。
  • 一個二進制字節碼文件對應的Class對象只能有一個,除非使用的是自定義加載器。

連接:

  • 驗證. 是否有正確的內部結構,並且和其他類協調一致。
  • 準備. 負責爲類的靜態成員分配內存,並設置默認的初始化值。
  • 解析. 將類的二進制數據中的符號引用替換爲直接引用。

初始化

JVM自帶的類加載器的組成

  1. Bootstrap ClassLoader 根類加載器
    也被稱爲引導類加載器,負責Java核心類的加載(jre的lib目錄下rt.jar)。
  2. Extension ClassLoader 擴展類加載器
    負責JRE的擴展目錄中jar包的加載(ext文件夾下)。
  3. System ClassLoader 系統類加載器
    負責在JVM啓動時在加載來自java命令的class文件,以及第三方jar文件。

類什麼時候被加載(進入內存)

  • 直接使用java.exe命令來運行某個主類。
  • 初始化某個類的子類,其父類先被加載。
  • 創建類的實例(new對象)。
  • 使用類的靜態變量,或者爲靜態變量賦值。
  • 調用類的靜態方法。
  • 使用反射方式來強制創建某個類或接口對應的java.lang.Class對象。

最先進入內存的類是Object類。

類的生命週期

當類被加載、連接和初始化後,它的生命週期就開始了。

當類的Class對象不再被引用,Class對象就會結束生命週期,類在方法區內的數據也會被卸載,從而結束類的生命週期。

由此可見,一個類何時結束生命週期,取決於代表它的Class對象何時結束生命週期

引用關係

加載器和Class對象:

  • 在類加載器的內部實現中,用一個Java集合來存放所加載類的引用。
  • 另一方面,一個Class對象總是會引用它的類加載器。調用Class對象的getClassLoader()方法,就能獲得它的類加載器。
  • 由此可見,Class實例和加載它的加載器之間爲雙向關聯關係。(互相引用)

類、類的Class對象、類的實例對象:

  • 一個類的實例總是引用代表這個類的Class對象。
  • 在Object類中定義了getClass()方法,這個方法返回代表對象所屬類的Class對象的引用。
  • 此外,所有的Java類都有一個靜態屬性class,它引用代表這個類的Class對象。

類的卸載

  • 由Java虛擬機自帶的類加載器所加載的類,在虛擬機的生命週期中,始終不會被卸載。
  • Java虛擬機本身會始終引用這些類加載器,而這些類加載器則會始終引用它們所加載的類的Class對象,因此這些Class對象始終是可觸及的
  • 由用戶自定義的類加載器加載的類是可以被卸載的。

        loader1變量和obj變量間接應用代表Sample類的Class對象,而objClass變量則直接引用它。

   如果程序運行過程中,將上圖左側三個引用變量都置爲null,此時Sample對象結束生命週期,MyClassLoader對象結束生命週期,代表Sample類的Class對象也結束生命週期,Sample類在方法區內的二進制數據被卸載

  當再次有需要時,會檢查Sample類的Class對象是否存在,如果存在會直接使用,不再重新加載;如果不存在Sample類會被重新加載,在Java虛擬機的堆區會生成一個新的代表Sample類的Class實例 (可以通過哈希碼查看是否是同一個實例)。

 

反射

Class對象的基本使用

獲取Class對象的3中方式

  1. 使用繼承自Object類的 ——> getClass();方法

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

  3. 通過Class類的靜態方法:forName(String  className)(常用)。

package com.bin.demo;

public class Main {

	public static void main(String[] args) throws ClassNotFoundException {
		//第一種方式獲取Class對象
		Instance obj1 = new Instance();
		Class<?> c1 = obj1.getClass();
		System.out.println(c1.getName());
		//第二種方式獲取Class對象
		Class<?> c2 = Instance.class;
		System.out.println(c2 == c1);
		//第三種方式獲取Class對象
		//絕對路徑 | 相對路徑 + 全限定類名
		Class<?> c3 = Class.forName("com.bin.demo.Instance");
		System.out.println(c3 == c2);
	}

}

反射獲取構造方法並創建對象

相關方法

// 獲得所有 公共構造方法
public Constructor<?>[] getConstructors()
// 獲取指定公共構造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
// 獲取所有構造方法包括(公共、私有、保護、默認)
public Constructor<?>[] getDeclaredConstructors()
// 獲取指定的構造方法,可以是(公共、私有、保護、默認)
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
//調用構造對象Constructor的方法,可以傳參數,也可以不傳
public T newInstance(Object... obj)

Instance.java類

package com.bin.demo;

public class Instance {
	
	//默認構造方法
	Instance(String str){
		System.out.println("默認構造 value = " + str);
	}
	
	//無參構造方法
	public Instance(){
		System.out.println("公有無參構造");
	}
	
	//有一個參數的構造方法
	public Instance(Integer age){
		System.out.println("年齡:" + age);
	}
	
	//有多個參數的構造方法
	public Instance(String name ,int age){
		System.out.println("姓名:" + name + "年齡:" + age);
	}
	
	//受保護的構造方法
	protected Instance(boolean n){
		System.out.println("受保護構造 n = " + n);
	}
	
	//私有構造方法
	private Instance(int age){
		System.out.println("私有構造  年齡:"+ age);
	}

}
package com.bin.demo;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Main {

	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		
		Class<?> c = Class.forName("com.bin.demo.Instance");
		
		System.out.println("************所有的構造方法(包括:私有、受保護、默認、公有)***************");
		Constructor[] conArray = c.getDeclaredConstructors();
		for (Constructor cc : conArray) {
			System.out.println(cc);
		}
		
		System.out.println("**********************所有公有構造方法*********************************");
		conArray = c.getConstructors();
		for (Constructor<?> cc : conArray) {
			System.out.println(cc);
		}

		System.out.println("*****************獲取公有指定構造方法*******************************");
		Constructor con = c.getConstructor(null);
		//穿null或不寫就默認查找無參公有構造,這裏需要的是可變參數的類型
		System.out.println("con = " + con);

		System.out.println("******************獲取指定構造方法:並調用*******************************");
		con = c.getDeclaredConstructor(Integer.class);
		System.out.println(con);
		con.setAccessible(true); //解除私有權限,對構造函數來說可以不更改此權限
		Object obj = con.newInstance(100);

	}

}

輸出

************所有的構造方法(包括:私有、受保護、默認、公有)***************
private com.bin.demo.Instance(int)
protected com.bin.demo.Instance(boolean)
public com.bin.demo.Instance(java.lang.String,int)
public com.bin.demo.Instance(java.lang.Integer)
public com.bin.demo.Instance()
com.bin.demo.Instance(java.lang.String)

**********************所有公有構造方法*********************************
public com.bin.demo.Instance(java.lang.String,int)
public com.bin.demo.Instance(java.lang.Integer)
public com.bin.demo.Instance()

*****************獲取公有指定構造方法*******************************
con = public com.bin.demo.Instance()

******************獲取指定構造方法:並調用*******************************
public com.bin.demo.Instance(java.lang.Integer)
年齡:100

反射獲取成員字段

相關方法

//獲取所有公共的變量
public Field[] getFields()
//獲取指定的公共變量,傳遞參數是字符串形式的變量名
public Field getField(String name)
//獲取指定變量,可以是(公共、私有、保護、默認)
public Field getDeclaredField(String name)
//獲取所有變量,包括(公共、私有、保護、默認),不包括繼承的字段
public Field[] getDeclaredFields()
// Field對象的方法,第一個參數:要改變與此Field變量對象的對象。第二個參數:改變的值。
public void set(Object obj, Object value)

Instance.java類

package com.bin.demo;

public class Instance {
	
	public String name;
	protected int age;
	boolean isEnd;
	private String phoneNum;
	
	public String getPhoneNum() {
		return phoneNum;
	}

}
package com.bin.demo;

import java.lang.reflect.Field;

public class Main {

	public static void main(String[] args) throws Exception {
		Class<?> c = Class.forName("com.bin.demo.Instance");
		
		System.out.println("************獲取所有的字段(公有、私有、受保護、默認的)********************");
		Field[] fieldArray = c.getDeclaredFields();
		for(Field f : fieldArray){
			System.out.println(f);
		}

		System.out.println("************獲取所有公有的字段********************");
		fieldArray = c.getFields();
		for(Field f : fieldArray){
			System.out.println(f);
		}
		
		System.out.println("*************獲取指定公有字段**並設置值和調用***********************************");
		Field f = c.getField("name");
		System.out.println(f);
		//創建一個對象
		Object obj = c.getConstructor().newInstance();
		//爲字段設置值
		f.set(obj, "超人");
		//驗證
		Instance instance = (Instance) obj;
		System.out.println("驗證姓名:" + instance.name);
		
		System.out.println("**************獲取指定字段****並設置值和調用********************************");
		f = c.getDeclaredField("phoneNum");
		System.out.println(f);
		f.setAccessible(true); //解除私有權限,改變私有變量需要解除權限
		f.set(obj, "110");
		System.out.println("驗證電話:" + instance.getPhoneNum());
	}

}

輸出

************獲取所有的字段(公有、私有、受保護、默認的)********************
public java.lang.String com.bin.demo.Instance.name
protected int com.bin.demo.Instance.age
boolean com.bin.demo.Instance.isEnd
private java.lang.String com.bin.demo.Instance.phoneNum

************獲取所有公有的字段********************
public java.lang.String com.bin.demo.Instance.name

*************獲取指定公有字段**並設置值和調用***********************************
public java.lang.String com.bin.demo.Instance.name
驗證姓名:超人

**************獲取指定字段****並設置值和調用********************************
private java.lang.String com.bin.demo.Instance.phoneNum
驗證電話:110

反射獲取成員方法

相關方法

//獲取所有公共方法
public Method[] getMethods()
//獲取指定公共的方法
public Method getMethod(String name, Class<?>... parameterTypes)
//獲取所有方法,包括(公共、私有、默認、保護),但不包括繼承的方法
public Method[] getDeclaredMethods()
//獲取指定的方法,包括(公共、私有、保護、默認)
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
//調用此方法,第一個參數:要調用的對象。第二個參數:傳遞的值,是個可變參數。
public Object invoke(Object obj, Object... args)

Instance.java類

package com.bin.demo;

public class Instance {
	
	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("調用了,私有的,並且有返回值String的,int參數的show4(): age = " + age);
		return "返回了String哦";
	}

}
package com.bin.demo;

import java.lang.reflect.Method;

public class Main {

	public static void main(String[] args) throws Exception {
		Class<?> c = Class.forName("com.bin.demo.Instance");
		
		System.out.println("***************獲取所有公有方法、包括繼承的公有方法*******************");
		Method[] methodArray = c.getMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
		
		System.out.println("***************獲取所有方法,包括公有、私有、保護、默認*******************");
		methodArray = c.getDeclaredMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
		
		System.out.println("***************獲取指定公有show1()方法並調用*******************");
		Method m = c.getMethod("show1", String.class);
		System.out.println(m);
		//創建一個對象
		Object obj = c.getConstructor().newInstance();
		m.invoke(obj, "超人");
		
		System.out.println("***************獲取指定私有的show4()方法並調用******************");
		m = c.getDeclaredMethod("show4", int.class);
		System.out.println(m);
		m.setAccessible(true); //解除私有權限,必須
		Object result = m.invoke(obj, 20);
		System.out.println("返回值:" + result);
	}

}

輸出

***************獲取所有公有方法、包括繼承的公有方法*******************
public void com.bin.demo.Instance.show1(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 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 com.bin.demo.Instance.show1(java.lang.String)
private java.lang.String com.bin.demo.Instance.show4(int)
protected void com.bin.demo.Instance.show2()
void com.bin.demo.Instance.show3()

***************獲取指定公有show1()方法並調用*******************
public void com.bin.demo.Instance.show1(java.lang.String)
調用了:公有的,String參數的show1(): s = 超人

***************獲取指定私有的show4()方法並調用******************
private java.lang.String com.bin.demo.Instance.show4(int)
調用了,私有的,並且有返回值String的,int參數的show4(): age = 20
返回值:返回了String哦

反射練習

一、通過反射越過泛型檢查

被提了無數次的例子

package com.bin.demo;

import java.util.ArrayList;

public class Main {

	public static void main(String[] args) throws Exception {
		ArrayList<Integer> list = new ArrayList<Integer>();
		list.add(1);
		list.add(2);
		list.add(3);
		
		ArrayList.class.getMethod("add", Object.class).invoke(list, "我是入侵者");
		
		for (Object obj : list) {
			System.out.println(obj);
		}
	}

}

輸出

1
2
3
我是入侵

二、通過配置文件創建指定對象、並調用指定方法

Instance.java類

package com.bin.demo;

public class Instance {
	
	public void show() {
		System.out.println("調用了show()方法");
	}
	
}

配置文件pro.txt

classPath=com.bin.demo.Instance
methodName=show
package com.bin.demo;

import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;

public class Main {

	public static void main(String[] args) throws Exception {
		Class<?> c = Class.forName(getValue("classPath"));
		Method m = c.getMethod(getValue("methodName"));
		m.invoke(c.newInstance());
	}
	
	// 此方法接收一個key,在配置文件中獲取相應的value
	public static String getValue(String key) throws IOException {
		Properties pro = new Properties();// 獲取配置文件的對象
		FileReader in = new FileReader("F:\\pro.txt");// 獲取輸入流
		pro.load(in);// 將流加載到配置文件對象中
		in.close();
		return pro.getProperty(key);// 返回根據key獲取的value值
	}

}

輸出

調用了show()方法

 

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