java反射機制

java程序的執行過程

ClassLoader講編譯好的class文件load到內存中的codeSegment代碼段;運行環境找到main方法開始執行;運行過程中會有更多的class被load到內存

ClassLoader

ClassLoader是類裝載器,用於將class裝載進內存

URL getResource(String name);用於獲取資源URL,尋找資源不依賴於相對路徑,不依賴於絕對路徑,只要資源處於classpath裏即可。方法執行時將找遍當前所設定的classpath目錄,eclipse裏src目錄默認地被編譯到bin目錄,bin目錄即項目本身的classpath,用於存放編譯好的類文件,所以約定俗成將資源放入工程src目錄下,這樣做的好處在於程序部署到何處都可以訪問到資源而不用修改代碼。

URL getResourceAsStream(String name);一般用於讀取配置文件

java ClassLoader動態加載機制

ClassLoader加載類的時候並非一次性加載,而是需要的餓時候才加載(運行期間加載)例:TestDynamicLoading.java 注意運行時要指定虛擬機參數java -vervose:class打印類裝載信息

public class TestDynamicLoading {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		new A();
		System.out.println("`````````````````````````````");
		new B();
		new C();
		new C();
		new D(1);
		new D(2);
	}
}
class A{}
class B{}
class C{
	static {
		System.out.println("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC");
	}
}
class D{
	D(int i){}
	{
		System.out.println("DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD");
	}
}

相關輸出:

[Loaded A from file:/E:/JAVA/workplace/TestReflection/bin/]
`````````````````````````````
[Loaded B from file:/E:/JAVA/workplace/TestReflection/bin/]
[Loaded C from file:/E:/JAVA/workplace/TestReflection/bin/]
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
[Loaded D from file:/E:/JAVA/workplace/TestReflection/bin/]
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD

從上面輸出可以看出以下問題

  1. ClassLoader加載類時候的動態加載機制:即需要時再加載
  2. 靜態語句塊static{}只在類加載時執行一次
  3. 動態語句塊{}在每次實例化對象的時候都執行一次,類似於強行嵌入了構造函數中

JDK內置ClassLoader

  1. bootstrap class loader  :由本地語言實現;加載jdk的核心類,首先load rt.jar裏的類以及其他高層classloader
  2. extension class loader  :加載jre/lib/ext裏的擴展類
  3. application class loader:加載 用戶自定義的類;可由ClassLoader.getSystemClassLoader()獲得該ClassLoader
  4. 其他class loader             :ClassLoader子類SecureClassLoader;SecureClassLoader子類URLClassLoader

JDK Classl的層次關係(不是繼承)

ClassLoader在加載class的時候首先通過getParent()方法找上一層Classloader,看該class是否被上層loader加載過了,如果已經加載了,就不在重複加載

如此保證了java核心類的安全性;此機制保證了class的逐層唯一加載,安全性好。

關於ClassLoader示例代碼如下:TestJDKClassLoader。java

public class TestJDKClassLoader {
	public static void main(String[] args) {
		//獲得加載了rt.jar中類的ClassLoader,即bootStrap ClassLoader,但是該類無名
		System.out.println(String.class.getClassLoader());
		//獲得加載擴張包中類的ClassLoader的名字,下面的類來自jre/lib/ext/sunjce_provider.jar
		System.out.println(com.sun.crypto.provider.AESCipher.class.getClassLoader().getClass().getName());
		//獲得加載了用戶自定義的類的Classloader的名字
		System.out.println(TestJDKClassLoader.class.getClassLoader().getClass().getName());
		//通過ClassLoader.getSystemClassLoader()獲得加載了用戶自定義的類的Classloader
		System.out.println(ClassLoader.getSystemClassLoader().getClass().getName());
		
		System.out.println("---------分割線-----------");
		
		//逐層獲取ClassLoader
		ClassLoader c = TestJDKClassLoader.class.getClassLoader();
		while(c != null) {
			System.out.println(c.getClass().getName());
			c = c.getParent();
		}
	}
}

輸出:

null
sun.misc.Launcher$ExtClassLoader
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$AppClassLoader
---------分割線-----------
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader

Class類

對於類裝載器而言每個class文件就是一個Class對象;Class類事對於編譯好的某一類對象的描述,描述了編譯好的class文件的信息,是對類信息的描述,是類的metainfo(元信息)或者metadata(元數據),即描述數據的數據

ClassLoader getClassLoader(),獲得該類的類裝載器

static Class forName(String); 可根據類名加載相應的類,返回一個Class對象

Object newInstance():實例化對象

其實從面向對象的高度看,類中的一個的屬性也是一個對象,方法也是對象,即屬性對象和方法對象

Method[]  getMethods()

     Class[] getParameterTypes()

     Class m.getReturnType()

     invoke(Object,Object... args)

Field[] getFields()

反射

  1. 反射機制即指java可以在運行期間通過Class的Class Class.forName(String)方法動態地加載一個類
  2. 繼而可以通過獲得的該類對應的的Class對象的Object newInstance()方法實例化出該類的對象
  3. 然後可以通過Method[]  getMethods() 和Field[] getFields()等方法瞭解類的內部結構
  4. 最後就可以根據方法的內部結構決定該方法應該怎麼樣去調用,從而動態地去調用類的方法m.invoke(Object,Object... args)

反射的例子:TestReflection.java

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class TestReflection {
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		String str = "T";
		Class c = Class.forName(str);
		Object o = c.newInstance();
		Method methods[] = c.getMethods();
		for(Method m : methods) {
//			System.out.println(m.getName());
			if(m.getName().equals("mm")) {
				m.invoke(o,1,2);
				Class p[] = m.getParameterTypes();
				for(Class pp : p) {
					System.out.println(pp.getName());
				}
			}else if (m.getName().equals("getS")) {
				System.out.println(m.getReturnType().getName());
			}
		}
	}
}

class T {
	int i;
	String s;
	static {
		System.out.println("load T");
	}
	
	T() {
		System.out.println("construct T");
	}
	
	public void mm(int i,int j) {
		this.i = i + j;
		System.out.println("mm is invoked");
	}
	
	public String getS() {
		return s;
	}
}

輸出:

load T
construct T
mm is invoked
int
int
java.lang.String

反射機制的好處在於可以使程序的可擴展性變的非常好;例如可以在配置文件裏配置類名,然後在程序中動態的加載加載配置文件中指定的類並調用其方法,調方法之前還可以先檢查這個人有沒有這個權限,在Spring Hibernate transaction的處理以及面向切面的編程AOP中都大量用到了反射機制

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