在運行時識別對象和類的信息——傳統的RTTI和Java反射機制

1. 引言

此處先回顧以下反射的概念:反射就是類在運行的過程中,通過Java API 提供的反射技術,動態的獲取類的屬性和方法,進而通過類的對象來調用他的方法。

對於反射技術自己只是一知半解,網上的博客大多千篇一律,此處根據Class類和JVM的類加載機制的加載過程中,從邏輯上分析反射技術。

類加載機制的加載過程:

第一步:通過一個類的權限定名獲取此類,並將其轉換爲字節碼文件

第二步:將字節碼文件轉換爲方法區的運行時數據結構

第三步:在內存中生成一個代表該類的java.lang.Class對象,作爲方法區這個類的各種數據結構的入口。

2. 運行時識別對象和類的信息(RTTI)

在閱讀《java編程思想》這本書-類型信息-章節時對RTTI和反射有了更深層次的認識

java中的“向上轉型,向下轉型”時要檢查對象的類型信息,在向上轉型時不需要顯示轉換,它是安全的;但是在向下轉型時需要進行顯示轉換。

2.1. 傳統的RTTI

RTTI指的是在運行時識別類和對象的信息,識別類和對象信息的有兩種方式,一種是傳統的"RTTI",假設我們在編譯時已經知道了所有的類型信息;一種是"反射"機制
,它允許我們在運行時發現和使用類的信息

RTTI和反射之間真正的區別在於,對RTTI來說,編譯器在編譯期間打開和檢查.class文件,而對於反射機制來說,.class文件在編譯時是不可獲取的,所以在運行時打開和檢查.class文件

  • 傳統的RTTI形式包括三種:

第一種:傳統的類型轉換,在需要被轉換的對象前面添加圓括號,括號中爲轉換後的對象

第二種:通過關鍵字instanceof來判斷類型是否合法,即對象是否屬於某個特定類的實例

第三種:通過Class.forName(String)來獲取Class對象

對於第三種方法一般認爲是在運行時獲取類型信息的,但是name參時是classpath路徑下.class文件的文件名,因此在編譯時編譯器已經知道所要處理的類

3. 反射

3.1. Class

Class類表示正在運行的java應用程序中的類和接口,(通俗的講:Class類代表的是你要反射生成的那個類),它保存着運行時的類的所有信息

Class類沒有構造方法,Class對象是在加載類時由JVM和類加載器生成的,也就是加載過程的第三步。

  • 創建Person類
public class Person {

	//創建私有屬性
	private String name;
	private int age;
	private String sex;

	//構造方法
	Person() {

	}

	Person(String name, int age, String sex) {
		this.name = name;
		this.sex = sex;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	//get和set方法
	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

}

3.1.1. 如何獲取Class類的實例

1.Object類的getClass()方法,
2.Class的forName()方法
3.要被反射的類的class屬性

public static void main(String[] args) throws ClassNotFoundException {
		Person person = new Person();
		/*
		 * 獲取代表運行時類的Class對象
		 */

		//1.通過Object.getClass()
		Class class1 = person.getClass();

		//2.通過運行時類的class屬性
		Class class2 = Person.class;

		//3.通過Class類的forName()方法
		Class class3 = Class.forName("reflect.Person");
	}

3.2. Constructor

提供了對類的構造函數的信息和訪問信息

3.2.1. 在獲取Class對象之後,如何獲取反射類的對象

第一種:先獲取class對象,然後通過class對象的newInstance()方法創建類對象

newInstance()方法在創建對象的時候只能調用類的無參構造函數,如果需要調用類的有參構造函數還需要使用Constructor類中的newInstance()方法


Class class3 = Class.forName("reflect.Person");
		
Object object = class1.newInstance();

第二種:先獲取類的構造方法對象Constructor,然後通過Constructor的newInstance()方法創建類對象

	//獲取類的所有公共構造方法,只能調用public構造方法
    Constructor[] constructors1 = class1.getConstructors();

    //獲取類的所有構造方法,包括私有的和非私有的
    Constructor[] constructors2 = class1.getDeclaredConstructors();

    //獲取類的無參私有構造方法
    Constructor constructor3 = class1.getDeclaredConstructor();

    //獲取類的帶參數的私有構造方法
    Constructor constructor4 = class1.getDeclaredConstructor(new Class[] {});

    //創建對象實例
    Person porson = constructor3.newInstance();

可以根據構造方法的參數,在獲取類的構造方法時指定參數.

3.3. Method

獲取某個類的全部構造方法或着自定義的構造方法或着指定的構造方法

    
    //獲取某個類下的一個指定的非私有方法
    Method method = class1.getMethod("method",new Class[] { });
        
    //獲取類下的所有方法,包括父類(Object)的方法
    Method[] method1 = class1.getMethods();
    
    //獲取類的自定義的方法
    Method[] methods = class1.getDeclaredMethods();
		 

3.4. Field

獲取類中的成員變量

    //獲取指定的成員變量
    Field field = class1.getField("name");
    System.out.println("獲取指定的name成員變量:" + field);

    //獲取類的所有成員變量
    Field[] fields = class1.getDeclaredFields();
    for (Field item : fields) {
        System.out.println(item + "   ");
    }
    //獲取類的public成員變量
    Field[] fields1 = class1.getFields();
    for (Field item : fields1) {
        System.out.println(item + "   ");
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章