java反射(2)

EmineWang發表於2013-11-08
所謂的反射機制就是java語言在執行時擁有一項自觀的能力。

通過這種能力可以徹底的瞭解自身的情況為下一步的動作做準備。

Reflection。這個字的意思是“反射、映象、倒影”,用在Java身上指的是我們可以於執行時載入、探知、使用編譯期間完全未知的classes。換句話說,Java程式可以載入一個執行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其物件實體、或對其fields設值、或喚起其methods。這種“看透class”的能力(the ability of the program to examine itself)被稱為introspection(內省、內觀、反省)。Reflection和introspection是常被並提的兩個術語。

在JDK在主要實現反射機制類都位於java.lang.reflect包中:

1.Class類:代表一個類

2.Field類:代表類的成員變數(成員變數也稱為類的屬性)

3.Method類:代表類的方法。

4.Constructor類:代表類的構造方法。

5.Array類

後面四個看看API就明白了



這裡主要說下Class類

Class是Reflection故事起源。針對任何您想探勘的class,唯有先為它產生一個Class object。

Class物件怎樣產生的?

根據API解釋

Class 沒有公共構造方法。Class 物件是在載入類時由 Java 虛擬機器以及通過呼叫類載入器中的 defineClass 方法自動構造的。

當一個class被載入,或當載入器(class loader)的defineClass()被JVM呼叫,JVM 便自動產生一個Class object。注意Class並沒有public constructor。


怎麼得到Class物件?

在我上一篇部落格上反射(1)也有提到。

這裡再說一下:

一般有三種方式得到Class物件

1、利用每個類都有的getClass()方法(java.lang.Object),

如String str = "xx";

Class c1 = str.getClass();

c1.getName();得到java.lang.String

2、利用static method------Class.forName()(最常被使用)

如Class c2 = Class.forName ("java.lang.String");

c2.getName();得到java.lang.String


3、類名.class方法

Class c3 = String.class;

c3.getName();得到java.lang.String


1)對於一些基本型別資料還可以通過.TYPE的方式,且看下面程式碼


package com.testclass;

import java.lang.reflect.Method;

public class Reflection {
	public static void main(String args[])
	{	
		try {
			/**
			 * 我們的程式中的每個類都有一個相應的Class物件.每當新的類被編譯
			 * 完成,就會產生一個Class物件儲存與相同的.class檔案內.執行期間
			 * 當你想要產生該class的物件是,JVM便會檢查該型別的Class物件是  
			 * 否被載入.如果沒被載入,JVM會根據名稱找到.class檔案並載入它
			 * 
			 * java中每個class都有一個相應的Class物件,當編寫好一個類,編譯完成後,
			 * 在生成的.class檔案中,就產生一個Class物件,用來表示
			 * 這個類的型別資訊。獲得Class例項的三種方式
			 */
			//1、使用Class的靜態方法forName(),用類的名字獲取一個Class例項
			Class c = Class.forName("com.testclass.TestOne");
			System.out.println(c.getName());//輸出com.md5.TestOne
			
			TestOne test = (TestOne)c.newInstance();//產生這個class類物件的一個例項,呼叫該類的無參的構造方法,作用等同於new TestOne();
			/**
			 * 有異常處理
			 * newInstance建立物件例項的時候會呼叫無參的建構函式,
			 * 所以必需確保類中有無引數的建構函式,否則將會丟擲java.lang.InstantiationException異常。
			 */
			
			System.out.println("=====================================================");
			
			//2、利用物件呼叫getClass()方法獲取該物件的的Class例項,物件實現存在
			TestOne  test2 = new TestOne();
			Class c2 = test2.getClass();
			System.out.println(c2.getName());//輸出com.md5.TestOne
			Method[] m = c2.getDeclaredMethods();//得到TestOne中的所有型別的自定義的方法,
//			Method[] m = c2.getMethods();//得到TestOne中的public型別的方法,不僅僅是自定義的,還有繼承於Object類的
			System.out.println("TestOne中的所有方法");
			for(Method m2 : m)
			{
				System.out.println(m2);
			}
			
			System.out.println("======================================================");
			//3-1、運用.class方式獲取Class例項(類)			
			Class c3 = TestOne.class;
			System.out.println(c3.getName());//輸出com.md5.TestOne
			
			
			//3-2 運用.class的方式獲取Class例項(基本型別)
			Class c4 = int.class;
			System.out.println("基本型別Class例項:"+c4.getName());//輸出int
			
			//3-3運用.class的方式獲取Class例項(封裝型別型別)
			Class c5 = Integer.TYPE;//獲取的是這個Integer型別
			System.out.println(c5.getName());//輸出int
			
			
			Class c6 = Integer.class;//獲取的是這個Integer的Class物件
			System.out.println(c6.getName());//輸出java.lang.Integer
			
			
		} catch (ClassNotFoundException e) {//如果包下不存在相應的com.md5.TestOne的.class檔案,會丟擲異常
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		catch (InstantiationException e) {
			e.printStackTrace();
		} 
		catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		//使用一般方法構造例項
		TestOne two = new TestOne();//靜態塊只會被載入一次,第二次建立物件,不會再載入靜態塊,只執行構造方法
	}
	
}

class TestOne{
	static {
		System.out.println("靜態塊執行");
	}
	public TestOne(){
		System.out.println("構造方法執行");
	}
	void getTest()
	{
		
	}
	public void getTest2()
	{
		
	}
	
}

2)對於method方法還可以輸出方法的引數等資訊、

package com.testclass;

import java.lang.reflect.Method;

public class Reflection2 {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		try {
			Class c = Class.forName("com.testclass.Test2");
			Method[] m = c.getDeclaredMethods();

			for(Method method : m)
			{
				Class[] pre = method.getParameterTypes();
				/**
				 * 按照宣告順序返回 Class 物件的陣列,這些物件描述了此 Method 物件所表示的方法的形參型別。
				 * 如果底層方法不帶引數,則返回長度為 0 的陣列
				 */
				for(Class cc : pre)
				{
//					System.out.println(cc);//輸出所有引數型別
					System.out.println(cc.getName());//輸出所有引數型別,這樣輸出更好
				}
			}
			
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
//		Class c = Integer.class;
//		System.out.println(c);//輸出class java.lang.Integer
		
//		Class c = int.class;
//		System.out.println(c);//輸出int

	}

}

class Test2
{
	public void getTest(Integer a,int b)
	{
		
	}
	
}


3)利用反射可以驗證一個物件是否屬於這個類

package com.testclass;

public class Reflection3 {
	public static void main(String[] args) {
		try {
			Class c = Class.forName("com.testclass.Test2");//!!!
			
			String s = "";
			System.out.println(c.isInstance(s));//輸出false
			System.out.println(c.isInstance(new Test2()));//輸出true
			
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}

4)反射在框架中用的很多,工廠模式,當你不斷的new 就不斷的分配記憶體空間,當你new到一定程度沒有空間的時候,不就出問題了。一般都不考慮用new來構造例項,除非特殊情況。

new是不在記憶體中有了空間的分配,當知道型別的時候可以new,當不知道型別的時候,不能new


還要明白在java裡面任何class都要裝載在虛擬機器上才能執行。Class.forName就是裝載類用的(和new 不一樣,要分清楚)。

Class.forName("oracle.jdbc.driver.OracleDriver");只有載入了資料庫驅動(就是載入這個.class檔案)之後,才能用DriverManager。

DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:orcl","xxx","xxx");


5)利用反射,動態載入類

要動態載入類,必須有個介面或者是抽象類(A),反射所載入的類是A的實現類或者是子類,否則動態載入的類毫無意義可言。

AA a = (AA)(Class.forName("xxx.xxx.xxx.AAImplClass").newInstance());//其中的forName中的類路徑可以動態的傳進來


7)陣列物件的反射(這個還在研究中)

int[] a = new int[2];
int[]b = new int[5];
System.out.println(a.getClass().getName());//輸出結果 [I
System.out.println(b.getClass().getName());////輸出結果 [I



6)此外,我們還可以在servlet中發現發射的影子

有時我們會奇怪,當我們把一個form表單提交給一個servlet處理時,我們並沒有建立這個servlet物件,但是怎麼呢呼叫其中的方法的呢?

當一個request請求傳遞給web伺服器後,web伺服器利用發射機制建立相應servlet物件,這個物件呼叫init方法將這個物件例項載入記憶體中(init方法只呼叫一次),然後就可以用這個例項呼叫servlet中的各種方法





相關文章