Java RTTI和反射機制

一、Java的RTTI 

       RTTIRun-Time Type Identification,通過運行時類型判定)的含義就是在運行時識別一個對象的類型,其對應的類是Class對象,每個java裏面的類都對應一個Class對象(在編寫並且編譯後),這個對象被保存在這個類的同名class文件裏。

      RTTI有兩種形式:(1)傳統的RTTI;(2)反射reflection機制。

1. 類型檢查

作用: 避免類型向下轉型是發生 ClassCastException 類型轉換異常。

兩種方法
   1. 通過 instanceof 運算符
           對象引用    instanceof  類名  
   2. Class對象調用 isInstance (對象引用)

		//1. 獲取類的 Class 對象
		Class clazz = Class.forName("com.jq.reflection.Student");
		
		//2. 反射創建類對象
		Object obj = clazz.newInstance();
		
		//第一種方法
		if(obj instanceof Student) {
			Student stu = (Student)obj;
			System.out.println("instanceof 檢查 向下轉型成功 " + stu);
		}
		
		//第二種方式
		if(clazz.isInstance(obj)) {
			Student stu = (Student)obj;
			System.out.println("clazz 檢查 向下轉型成功: " + stu);
		}

----結果----
instanceof 檢查 向下轉型成功 com.jq.reflection.Student@7852e922
clazz 檢查 向下轉型成功: com.jq.reflection.Student@7852e922

 

二、反射機制概述

    JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱爲java語言的反射機制。

     1. Java反射機制的作用:

   在運行時判斷任意一個對象所屬的類;在運行時構造任意一個類的對象;在運行時判斷任意一個類所具有的成員變量方法;在運行時調用任意一個對象的方法;生成動態代理。

    1).反射創建該類的字節碼對象

    2)分析類的結構(類中的屬性、方法)

    反射機制的源頭Class對象,通過Class對象來獲取構造器,方法,字段並進行操作。

 

、獲取Class對象引用的三種方式

    在Java 中可以通過三種方法獲取類的字節碼(Class)對象

   Class 對象:是對一個普通類運行時狀態的描述。  任何一個類都有一個對應的 Class 對象,在類編譯通過時生成最初保存在與其同名的 .class 字節碼文件中。這個類第一次使用時, JVM 會檢查該類的 Class 對象有沒有被加載, 只加載一次

1. 通過對象調用 getClass() 方法
        需要提前 new 一個對象, 一般用於類型檢查。    
2. 通過 Class 類中的靜態方法 forName(全限定名) 即包名.類名
        一般用於加載驅動。
3.  通過 類名 .class 字面量(基本類型int,double等也可以使用.class)
        一般用於參數傳遞。

//1.通過對象調用 getClass() 方法
Student stu = new Student();
Class c1 = stu.getClass();
System.out.println(c1);

//2. 通過 Class 類中的靜態方法 forName(全限定名) 即包名.類名
Class c2 = null;
try {
	 c2  = Class.forName("com.jq.reflection.Student");
	System.out.println(c2);
} catch (ClassNotFoundException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
}

//3. 通過 類名.class 字面量(基本類型int,double等也可以使用.class)
Class<Student> c3 = Student.class;
System.out.println(c3);

//注意3種方式獲得的Class對象是同一個
System.out.println(c1==c2);
System.out.println(c1==c3);
System.out.println(c2==c3);


----結果----
class com.jq.reflection.Student
class com.jq.reflection.Student
class com.jq.reflection.Student
true
true
true

注意:在運行期間,一個類,只有一個Class對象產生。即3種方式獲得的Class對象是同一個 Class 對象引用。

 

四、通過反射機制獲取類的結構

     查看Class類在java中的api詳解, 取消對非公共屬性/方法/構造器的訪問權限檢查

1、通過反射創建類的對象

       1)調用 Class 對象的 newInstance( )方法,實例化對象,注意:類必須要有無參數的構造器

		/**
		 * 1. 類必須要有無參構造器,否則報 java.lang.InstantiationException 實例化異常
		 */
		//1. 獲取類的 Class 對象
		Class c = Student.class;
		try {
			Student stu = (Student) c.newInstance();	//注意:返回類型是Object,向下轉型
			System.out.println(stu);
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}

----結果----
com.jq.reflection.Student@7852e922

     2)通過 Class 對象獲取 xxxConstructor 構造器,由構造器調用 newInstance( )方法 ,實例化對象

    • Constructor<T> getConstructor(<?>... parameterTypes)

      返回一個 Constructor對象,該對象反映 Constructor對象表示的類的指定的公共 函數。

      Constructor<?>[] getConstructors()

      返回包含一個數組 Constructor對象反射由此表示的類的所有公共構造 對象。

      • public void setAccessible(boolean flag)
                           throws SecurityException
        將此對象的accessible標誌設置爲指示的布爾值。 true的值表示反射對象應該在使用時抑制Java語言訪問檢查。 false的值表示反映的對象應該強制執行Java語言訪問檢查。

        首先,如果有一個安全管理器,它的checkPermission方法被調用一個ReflectPermission("suppressAccessChecks")權限。

        SecurityException如果升高flagtrue但此對象的可訪問性可以不改變(例如,如果該元素對象是Constructor對象爲類 )。

        一個SecurityException如果這個對象是提高Constructor對象類java.lang.Class ,以及flag是真的。

類的構造方法:

//---------------構造方法-------------------
		
		//1. 無參構造方法
		public Student(){
		}
		
		//2. 有一個參數的構造方法
		public Student(String username) {
			this.username = username;
		}
		
		//3. 有多個參數的構造方法
		public Student(Integer id, String username, String sex) {
			this.id = id;
			this.username = username;
			this.sex = sex;
			System.out.println("學號:"+id+ "姓名:"+username+ " 性別:"+sex);
		}

		//4.受保護的構造方法
		protected Student(boolean n){
			System.out.println("受保護的構造方法 n = " + n);
		}
		
		//5. 私有構造方法
		private Student(int id){
			this.id = id;
			System.out.println("私有的構造方法   學號:"+ id);
		}
		//5. 缺省構造方法
		Student(int id, String sex){
			this.id = id;
		}

獲取構造器及類對象: 私有構造器創建類對象時,con3.setAccessible(true);  //取消對私有構造器的訪問權限檢查

//1. 獲取類的 Class 對象
Class clazz = Class.forName("com.jq.reflection.Student");

//2.1 獲取所有的公共的構造器
System.out.println("----所有公共的構造方法----"); 
Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
	System.out.println(constructor);
}
 
//2.2 獲取所有的構造器 
 System.out.println("----所有構造方法(包括 公共,私有,受愛護和缺省 構造器)----"); 
 Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
 for (Constructor constructor : declaredConstructors) {
	 System.out.println(constructor);
}

//2.3 獲取公有、無參的構造器 
System.out.println("----獲取公有、無參的構造方法----");
Constructor con = clazz.getConstructor(null);	//無參的構造方法de類型是一個null,不寫也可以
System.out.println(con);

//2.3 獲取公有、帶參的構造器 
System.out.println("----獲取公有、無參的構造方法----");
Constructor con2 = clazz.getConstructor(Integer.class, String.class, String.class);	//切記是Class類型
System.out.println(con2);

System.out.println("----獲取私有構造方法,並調用----");
Constructor con3 = clazz.getDeclaredConstructor(int.class); 
System.out.println(con3);

//3.1 公有構造方法獲取類的對象
System.out.println("\n----## 獲取類的對象----");
Student stu2 = (Student) con2.newInstance(001,"李四","男");
System.out.println(stu2);

//3.2 私用有構造方法獲取類的對象
con3.setAccessible(true);  //取消對私有構造器的訪問權限檢查
Student stu3 = (Student) con3.newInstance(002);
System.out.println(stu3);

結果:

----所有公共的構造方法----
public com.jq.reflection.Student()
public com.jq.reflection.Student(java.lang.String)
public com.jq.reflection.Student(java.lang.Integer,java.lang.String,java.lang.String)
----所有構造方法(包括 公共,私有,受愛護和缺省 構造器)----
com.jq.reflection.Student(int,java.lang.String)
private com.jq.reflection.Student(int)
protected com.jq.reflection.Student(boolean)
public com.jq.reflection.Student()
public com.jq.reflection.Student(java.lang.String)
public com.jq.reflection.Student(java.lang.Integer,java.lang.String,java.lang.String)
----獲取公有、無參的構造方法----
public com.jq.reflection.Student()
----獲取公有、無參的構造方法----
public com.jq.reflection.Student(java.lang.Integer,java.lang.String,java.lang.String)
----獲取私有構造方法,並調用----
private com.jq.reflection.Student(int)

----## 獲取類的對象----
學號:1姓名:李四 性別:男
com.jq.reflection.Student@7852e922
私有的構造方法   學號:2
com.jq.reflection.Student@4e25154f

 

2、通過反射分析類的屬性

    • Field getField(String name)

      返回一個 Field對象,它反映此表示的類或接口的指定公共成員字段 對象。

      Field[] getFields()

      返回包含一個數組 Field對象反射由此表示的類或接口的所有可訪問的公共字段 對象。

    • Field getDeclaredField(String name)

      返回一個 Field對象,它反映此表示的類或接口的指定已聲明字段 對象。

      Field[] getDeclaredFields()

      返回的數組 Field對象反映此表示的類或接口聲明的所有字段 對象。

字段:

    public Integer id;
    String username;
    private String sex;

		//1. 獲取類的 Class 對象
		Class clazz = Class.forName("com.jq.reflection.Student");
		
		//2. 反射創建類對象
		Student stu = (Student) clazz.newInstance();
		
		//3.1 獲取所有的公共的屬性,包括繼承來的屬性
		System.out.println("----所有的公共的屬性,包括繼承來的屬性----");
		Field[] fields = clazz.getFields();
		for (Field field : fields) {
			System.out.println(field);
		}
		//3.2 獲取所有的所有權限的屬性,包括private修飾的,不包括繼承來的
		System.out.println("----所有權限的屬性,包括private修飾的,不包括繼承來的屬性----");
		Field[] fields1 = clazz.getDeclaredFields();
		for (Field field : fields1) {
			System.out.println(field);
		}
		 
		System.out.println("----屬性值得get set----");
		//3.3 獲取指定的公共的屬性
		Field fid = clazz.getField("id"); //注意參數是屬性名稱,String類型的
		fid.set(stu, 101);
		System.out.println("id="+fid.get(stu)); 
		
		//3.4  獲取指定的私有的屬性
		Field fsex = clazz.getDeclaredField("sex");
		fsex.setAccessible(true); //取消對私有屬性的訪問權限檢查
		System.out.println("sex=" + fsex.get(stu));
                fsex.set(stu, "女");
		System.out.println("sex=" + fsex.get(stu)); 


----結果----
----所有的公共的屬性,包括繼承來的屬性----
public java.lang.Integer com.jq.reflection.Student.id
----所有權限的屬性,包括private修飾的,不包括繼承來的屬性----
public java.lang.Integer com.jq.reflection.Student.id
java.lang.String com.jq.reflection.Student.username
private java.lang.String com.jq.reflection.Student.sex
----屬性值得get set----
id=101
sex=null
sex=女

 

3、通過反射獲取類的方法並 invoke() 調用

在運行時調用任意一個對象的方法;生成動態代理

    • 方法 getMethod(String name, <?>... parameterTypes)

      返回一個 方法對象,它反映此表示的類或接口的指定公共成員方法 對象。

      方法[] getMethods()

      返回包含一個數組 方法對象反射由此表示的類或接口的所有公共方法 對象,包括那些由類或接口和那些從超類和超接口繼承的聲明。

    • 方法 getDeclaredMethod(String name, <?>... parameterTypes)

      返回一個 方法對象,它反映此表示的類或接口的指定聲明的方法 對象。

      方法[] getDeclaredMethods()

      返回包含一個數組 方法對象反射的類或接口的所有聲明的方法,通過此表示 對象,包括公共,保護,默認(包)訪問和私有方法,但不包括繼承的方法。

方法:

	//1. 無參公共方法
	public void sleep(){
		System.out.println("睡覺--"); 
	}
	//2. 帶參公共方法
	public void game(String game){
		System.out.println("打遊戲:" + game);
	}
	//2. 帶參私有方法
	private int study(String course){
		System.out.println("學習"+course+"--"); 
		return 100;
	}

        //getter setter 方法

測試

		//1. 獲取類的 Class 對象
		Class clazz = Class.forName("com.jq.reflection.Student");
		
		//2. 反射創建類對象
		Student stu = (Student) clazz.newInstance();
		
		//3.1 獲取類中的所有的公共的方法,包括繼承的方法
		System.out.println("----所有的公共的方法,包括繼承的方法----");
		Method[] methods = clazz.getMethods();
		for (Method method : methods) {
			System.out.println(method);
		}
		//3.2 獲取所有權限的方法,包括private修飾的,但不包括繼承來的方法
		System.out.println("----所有權限的方法,包括private修飾的,但不包括繼承來的方法----");
		Method[] declaredMethods = clazz.getDeclaredMethods();
		for (Method method : declaredMethods) {
			System.out.println(method);
		}
		 
		System.out.println("----invoke方法調用----");
		//3.3 獲取指定的公共無參方法
		Method mSleep =clazz.getMethod("sleep", null);	
		mSleep.invoke(stu, null);	//類對象,方法參數(無可不寫)
		
		//3.3 獲取指定的公共有參方法
		Method mGame =clazz.getMethod("game", String.class);//方法名(String類型) 參數Class類型
		mGame.invoke(stu, "王者榮耀");
		
		//3.4  獲取指定的私有帶參方法
		Method mStudy = clazz.getDeclaredMethod("study", String.class);
		mStudy.setAccessible(true); //取消對私有方法的訪問權限檢查
		mStudy.invoke(stu, "java課程");

----結果----
所有的公共的方法,包括繼承的方法----
public java.lang.Integer com.jq.reflection.Student.getId()
public void com.jq.reflection.Student.sleep()
public void com.jq.reflection.Student.setId(java.lang.Integer)
public void com.jq.reflection.Student.setSex(java.lang.String)
public java.lang.String com.jq.reflection.Student.getUsername()
public java.lang.String com.jq.reflection.Student.getSex()
public void com.jq.reflection.Student.game(java.lang.String)
public void com.jq.reflection.Student.setUsername(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()
所有權限的方法,包括private修飾的,但不包括繼承來的方法----
public java.lang.Integer com.jq.reflection.Student.getId()
public void com.jq.reflection.Student.sleep()
public void com.jq.reflection.Student.setId(java.lang.Integer)
public void com.jq.reflection.Student.setSex(java.lang.String)
public java.lang.String com.jq.reflection.Student.getUsername()
public java.lang.String com.jq.reflection.Student.getSex()
public void com.jq.reflection.Student.game(java.lang.String)
public void com.jq.reflection.Student.setUsername(java.lang.String)
private int com.jq.reflection.Student.study(java.lang.String)
invoke方法調用----
睡覺--
打遊戲:王者榮耀
學習java課程--

 

 

 

 

 

 

 

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