對Java中反射機制的理解

簡單的說,反射機制就是在程序的運行過程中被允許對程序本身進行操作,比如自我檢查,進行裝載,還可以獲取類本身,類的所有成員變量和方法,類的對象,還可以在運行過程中動態的創建類的實例,通過實例來調用類的方法,這就是反射機制一個比較重要的功能了。那麼要通過程序來理解反射機制,首先要理解類的加載過程。

在Java程序執行的時候,要經歷三個步驟:加載、連接和初始化。首先程序要加載到JVM的方法區中,然後進行連接,最後初始化。這裏就主要介紹一下類的加載。如上圖,首先,JVM會從硬盤中讀取Java源文件並將其加載到方法區中同時生成類名.class文件,也就是類對象,這個類對象中包含了我們創建類的實例時所需要的模板信息,也就是源代碼中的成員變量和方法等。Class本身也是一個類,它的主要功能之一就是生成類加載時的class文件,爲類的初始化及實例化做準備。而我們在程序中通過關鍵字new創建的對象創建的是類的對象,而不是類對象,二者的區別如圖中所示。

對類的加載有了一個大致的理解之後,我們來看一下實現反射機制的具體操作:

反射機制在我們所學習的框架中有很大的應用,而在我們實際開發中用的並不多,所以理解反射機制對我們學習框架來說很有幫助。

首先我們創建一個實體類User.java

package cn.itcast_01;

public class User extends Person{
	
	public String name;
	private int age;
	
	public User(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	
	private User(int age)
	{
		super();
		this.age = age;
		
	}
	
	public User(String name)
	{
		super();
		this.name = name;
	}
	
	public User() {
		super();
		// TODO Auto-generated constructor stub
	}
	@Override
	public String toString() {
		return "User [name=" + name + ", age=" + age + "]";
	}
	
	public void exit()
	{
		System.out.println(name+"退出系統");
	}
	
	public void login(String username,String password)
	{
		System.out.println("用戶名:"+username);
		System.out.println("密碼:"+password);
	}
	
	private String CheckInfo()
	{
		return "年齡:"+age;
	}

}

其中包括成員變量,構造方法和一些成員方法。

利用反射機制可以獲取類對象(也就是我們前面介紹的類對象,獲取類對象之後我們便獲取了類的模板,可以對類進行一些操作),有以下三種方法:

1.類名.class()

2.對象名.getClass()

3.Class.forName(具體的類名)

我們通過代碼來看一下具體的操作:

package cn.itcast_01;

public class Demo {
	
	public static void main(String[] args) throws Exception {
		
		//1.類名.class
		
		Class clz = User.class;
		System.out.println(clz);
		
		//2.對象名.getClass()
		
		Class clz1 = new User().getClass();	
		System.out.println(clz==clz1);
		
		//3.Class.forName()
		
		//Class clz2 = Class.forName("User");
		Class clz2 = Class.forName("cn.itcast_01.User");
		System.out.println(clz==clz2);
	}

}

控制檯輸出如下:

第一行就是所獲取的類對象,下面兩行true的結果表示類對象只有一個(這裏要注意的是第三種方法中類名一定要寫全稱,包名也要包括進去,不然JVM會無法定位這個類的具體位置)。下面在用一張圖來解釋這三種方法的執行時機:

在類加載的三個階段裏都可以獲取類對象,其中第三種方法,在源碼中獲取類對象是最常用的,也是反射機制在框架中的應用,鑑於我目前的理解,在框架中的應用可以是通過配置文件寫入所創建的類名,再利用第三種方法獲取類對象。

獲取類對象之後就可以對類進行一些創建對象、調用方法、訪問成員變量的操作了:

1.創建對象:

Object obj = 類對象.newInstance();

實例:

package cn.itcast_01;

public class Demo2 {
	
	public static void main(String[] args) throws Exception {
		
		Class clz = Class.forName("cn.itcast_01.User");
		System.out.println(clz);
		
		Object obj = clz.newInstance();
		System.out.println(obj);
	}

}

結果:

2.調用方法:

Method md = 類對象.getMethod("類中的公有方法名");

獲取公有方法,其中md是Method類型的方法名,

Object obj1 = lm.invoke(u2,"老趙","aixiaoba");

爲獲取到的方法命名,方便調用。

Method dm = 類對象.getDeclaredMethod("類中的私有方法名");

獲取私有方法名,又叫暴力獲取,此方法無視方法的訪問權限,即使是被private修飾的方法也會被獲取到。

Method lm = 類對象.getMethod("有參方法方法名",參數的類對象...);

獲取有參方法,同時要獲取參數的類對象,格式爲:參數類型.class

Object obj1 = lm.invoke(類的對象,"參數1","參數2");

調用獲取到的方法,使用invoke關鍵字,此處表示調用有參方法。

以上方法中由於不確定獲取到的對象類型,所以用Object接收。

實例1:

package cn.itcast_05_Method;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import cn.itcast_01.User;

public class Demo {
	
	public static void main(String[] args) throws Exception {
		
		User u = new User("小巴",18);
		User u2 = new User("老趙",20);
		
		Class clz = Class.forName("cn.itcast_01.User");
		
		Method em = clz.getMethod("exit");
		
		Object obj = em.invoke(u2);
		
		System.out.println(obj);
		
		Method lm = clz.getMethod("login",String.class,String.class);
		
		Object obj1 = lm.invoke(u2,"老趙","aixiaoba");
		
		System.out.println(obj1);
	}

}

結果:

方法名參考實體類User.java,因爲方法爲void類型,所以運行結果中會出現null,表示返回值爲空。

實例2:

package cn.itcast_05_Method;

import java.lang.reflect.Method;

import cn.itcast_01.User;

public class Demo2 {
	
	public static void main(String[] args) throws Exception {
		
		User u = new User("小巴",18);
		User u2 = new User("老趙",20);
		
		Class clz = Class.forName("cn.itcast_01.User");
		
		Method dm = clz.getDeclaredMethod("CheckInfo");
		
		dm.setAccessible(true);
		
		Object obj = dm.invoke(u2);
		
		System.out.println(obj);
	}

}

結果:

要注意的是,在暴力獲取私有方法後還要獲取調用該方法的權限:

dm.setAccessible(true);

將參數設置爲true.

3.訪問成員變量:

最重要的一個關鍵字:Field

Field nf = 類對象.getField("成員變量名");

和調用私有方法一樣,要訪問私有成員變量也要通過暴力獲取的的方式,同時也要獲取訪問私有成員變量的權限。

Field af = 類對象.getDeclaredField("私有成員變量名");
af.setAccessible(true);

實例1:

package cn.itcast_02Field;

import java.lang.reflect.Field;

public class Demo {
	
	public static void main(String[] args) throws Exception {
		
		Class clz = Class.forName("cn.itcast_01.User");
		
		Object obj = clz.newInstance();
		
		Field nf = clz.getField("name");
		
		nf.set(obj, "小巴");
		
		Object object = nf.get(obj);
		
		System.out.println(object);
	}

}

結果:

實例2:

package cn.itcast_02Field;

import java.lang.reflect.Field;

import cn.itcast_01.User;

public class Demo2 {
	
	public static void main(String[] args) throws Exception {
		
		User u = new User("小巴",18);
		
		Class clz= Class.forName("cn.itcast_01.User");
		
		Field af = clz.getDeclaredField("age");
		
		af.setAccessible(true);
		
		Object obj = af.get(u);
		
		System.out.println(obj);
		
		
		
	}

}

結果:

反射機制中還有很多知識需要學習,比如獲取構造方法,獲取全部屬性,獲取全部方法,等等等等,希望能和大家共勉,共同進步!!!

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