JavaSE學習筆記(14.Java之反射機制)

1. 類加載過程

Java是一門編譯加解析的語言,編譯器先將代碼編譯成字節碼文件(class文件),Jvm加載class文件進行解析運行!

類加載通常分三個步驟:class文件的加載、類的鏈接、類的初始化!

1.1 class文件的加載:

將編譯後的class文件,加載到內存中,解析字節碼,並生成一個Class對象!

1.1.1 類加載器介紹:

class文件的加載,並不是通過一個加載器實現,是通過多組類加載器共同實現的,共分四類:根類加載器、擴展類加載器、系統類加載器、用戶類加載器!

根類加載器:在sun.boot.class.path系統變量中的路徑中尋找class文件,並進行加載!

擴展類加載器:在java.ext.dirs系統變量中的路徑中尋找class文件,並進行加載!類型爲ExtClassLoader

系統類加載器:在java.class.path系統變量中的路徑中尋找class文件,並進行加載!類型爲AppClassLoader;可以通過ClassLoader.getSystemClassLoader()靜態方法獲取!

用戶類加載器:用戶自定義類加載器,根據自定義邏輯進行類加載,一般情況下父類加載器爲系統類加載器!

Ps:

  • Jvm系統變量可以通過System.getProperties()方式獲取並查看!
  • 上面四類類加載是存在層級關係的,從上到下:根類加載器、擴展類加載器、系統類加載器、用戶類加載器
  • 上面四類類加載器只有根類加載器不是ClassLoader的子類,其餘類加載器都是ClassLoader的子類,因爲根類加載器是Jvm實現!

1.1.2 類加載過程介紹:

當加載一個類的時候,首先判斷緩存中是否存在,如果不存在,會通過當前使用的類加載器找到根類加載器,然後一層層向下使用類加載器尋找class文件,加載成功後,退出並返回Class對象;如果到用戶類加載器依然沒有找到class文件,Jvm拋出ClassNotFoundException!(手動進行類加載的時候,可以指定類加載器;如果依賴於Jvm自動進行類加載的時候,默認使用系統類加載器)

1.1.3 自定義類加載器:

可以通過繼承ClassLoader類,來實現自定義用戶類加載器,主要通過重寫loadClass()和findClass()兩個方法來實現,具體細節不再展開描述!好處就是可以定製特定類加載前的部分實現邏輯!

1.2 類的鏈接:

將class文件加載到內存中的二進制數據合併到JRE中:

1.3 類的初始化:

  • 對類變量進行初始化賦值
  • 執行靜態初始化代碼塊

Ps:

  • 調用ClassLoader.loadClass(類名)只會執行類的加載和鏈接,不會進行類的初始化,等到類使用的時候纔會執行初始化!
  • 調用Class.forName(類名)會執行類的加載、鏈接、並強制初始化!

1.4 觸發類加載條件:

  1. 創建類實例
  2. 訪問類方法
  3. 訪問類成員
  4. 調用Class.forName()方法
  5. 加載子類的時候,會先加載所有父類
  6. 直接使用java.exe命令運行某個主類,會先加載主類

Ps:訪問static final屬性的類成員變量,如果該對象在編譯期間就能確定內容;編譯器會在使用它的地方直接替換爲它的值,因此訪問的時候,不通過類訪問,所以不會觸發類加載!

 

2. Java的反射機制

2.1 反射機制的原理:

Java中的反射是通過類加載後生成的Class對象實現的,該對象提供了所有的類操作和類信息獲取能力!

獲取Class對象的方法:

  • 調用Class.forName()類方法,獲取Class對象!
  • 類.class,通過類的class屬性,獲取Class對象!
  • 通過對象調用Object.getClass()實例方法,獲取Class對象!

Ps:後面兩種方式,必須保證類加載已經完成,否則無法獲取;Class.forName()方式可以在類沒有加載的時候調用,觸發類加載!

2.2 通過反射獲取信息:

2.2.1 獲取構造器、獲取成員、獲取方法相關信息:

Ps:

  • getXXX獲取某個特定類型的public屬性的數據!
  • getXXXs獲取所有public屬性的數據!
  • getDeclaredXXX獲取某個特定類型的所有屬性的數據!
  • getDeclaredXXXs獲取所有屬性的數據!
  • 數據獲取的時候,都是包括繼承的數據!!!

2.2.2 獲取類相關信息:

/*獲取Class對象所屬的外部類*/
public Class<?> getDeclaringClass() throws SecurityException

/*獲取類中的所有內部類*/
public Class<?>[] getDeclaredClasses() throws SecurityException

/*獲取類中所有public屬性的內部類*/
public Class<?>[] getClasses()

/*獲取類實現的接口*/
public Class<?>[] getInterfaces()

/*獲取類繼承的父類*/
public native Class<? super T> getSuperclass()

2.2.3 獲取註解相關信息:

/*獲取指定類型的註解, 包括父類註解*/
public <A extends Annotation> A getAnnotation(Class<A> annotationClass)

/*獲取指定類型的註解,不包括父類註解*/
public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass)

/*獲取當前類的所有註解,包括父類註解*/
public Annotation[] getAnnotations()

/*獲取當前類的所有註解,不包括父類註解*/
public Annotation[] getDeclaredAnnotations() 

/*針對Java8 重複註解功能,提供的獲取指定類型註解,包括父類註解*/
public <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationClass)

/*針對Java8 重複註解功能,提供的獲取指定類型註解,不包括父類註解*/
public <A extends Annotation> A[] getDeclaredAnnotationsByType(Class<A> annotationClass)

2.2.4 獲取類基本信息:

/*獲取包信息*/
public Package getPackage() 

/*獲取類名字*/
public String getName()

/*獲取類的簡稱*/
public String getSimpleName()

/*獲取類的修飾符常量值,通過Modifier工具類解碼*/
public native int getModifiers()

/*class信息判斷*/
/*判斷obj對象是否屬於當前類的實例,與instanceof用法一致*/
public native boolean isInstance(Object obj)

/*判斷當前類是否是一個接口*/
public native boolean isInterface()

/*判斷當前類是否是一個數組類*/
public native boolean isArray()

/*判斷當前類是否爲基礎類類型,Boolean、Character、Byte、Short、Integer、Long、Double、Void*/
public native boolean isPrimitive()

/*判斷當前類是否是一個枚舉類*/
public boolean isEnum()

/*判斷當前類是否是一個匿名類*/
public boolean isAnonymousClass()

/*判斷當前類是否被annotationClass註解修飾*/
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

instanceof與isInstatnce解析:

public class InstanceTest
{
	public static void main(String[] args)
	{
		ClassF f = new ClassF();
		ClassS s = new ClassS();
		/*instanceof Test*/
		System.out.println("instanceof Test");
		/*class是object本身類,編譯通過,返回true*/
		System.out.println(f instanceof ClassF);
		/*class是object父類,編譯通過,返回true*/
		System.out.println(s instanceof ClassF);
		/*class是object子類,編譯通過,返回false*/
		System.out.println(f instanceof ClassS);
		/*class與object不存在任何關係,編譯報錯!!!*/
		//System.out.println(f instanceof String);
		
		/*isInstance Test*/
		System.out.println("isInstance Test");
		/*class是object本身類,編譯通過,返回true*/
		System.out.println(ClassF.class.isInstance(f));
		/*class是object父類,編譯通過,返回true*/
		System.out.println(ClassF.class.isInstance(s));
		/*class是object子類,編譯通過,返回false*/
		System.out.println(ClassS.class.isInstance(f));
		/*class與object不存在任何關係,編譯通過,返回false*/
		System.out.println(String.class.isInstance(f));
		
	}
	
}

class ClassS extends ClassF
{
    
}

class ClassF 
{
    
}

Ps:

  • instanceof是關鍵詞,isInstance是Class對象的方法
  • 兩者用法基本相同,只是當class與object沒有關係的時候,instanceof編譯報錯;isInstance編譯通過,返回false!

2.2.5 獲取方法的參數信息:

Java8,Method類和Constructor類新增了一個共同的抽象類Executable,Executable類中提供了一系列關於Parameter類的方法:

/*Executable類主要方法*/
/*獲取方法或構造器的參數個數*/
public int getParameterCount() 

/*獲取方法或構造器的參數類型數組*/
public Type[] getGenericParameterTypes()

/*獲取方法或構造器的參數數組*/
public Parameter[] getParameters()

Parameter類中也提供了一系列獲取具體參數信息的方法(獲取參數類型、獲取參數修飾符、獲取參數名、獲取參數泛型類型、參數是否可變參數等等):

 

2.3 通過反射操作類:

java.lang.reflect包中除了提供了Class類,同時也提供了Package、Method、Field、Constructor、Array、Type、ParameterizedType等類,爲獲取到的反射數據提供操作能力!

2.3.1 創建對象:

通過反射創建對象有兩種方式:Class對象的newInstance方法和Constructor對象的newInstance方法!

public class ReflectTest
{
	public static void main(String[] args)
	{
		/*通過反射創建對象的兩種方式*/
		/*通過Class對象的newInstance()方法,只能訪問public屬性的構造器*/		
		try 
		{
			ReflectPublic r1 = ReflectPublic.class.newInstance();
			r1.hello();						
		} 
		catch (Exception e)
		{		
			e.printStackTrace();
		}			
		
		/*通過Constructor對象的newInstance()方法,可以通過setAccessible設置,不進行訪問權限檢測,訪問private屬性的構造器*/		
		Constructor<ReflectPrivate> constructor;
		try 
		{
			constructor = ReflectPrivate.class.getDeclaredConstructor();
			constructor.setAccessible(true);
			ReflectPrivate r2 = constructor.newInstance();
			r2.hello();
		} 
		catch (Exception e1) 
		{
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}	
		
	}
	
}

class ReflectPrivate
{
	private ReflectPrivate()
	{
		
	}
	
	public void hello()
	{
		System.out.println("private hello");
	}
}

class ReflectPublic
{
	public ReflectPublic()
	{
		
	}
	
	public void hello()
	{
		System.out.println("public hello");
	}
}

Ps:setAccessible()不進行類型訪問權限設置,同樣可以使用在Method和Field兩個類,用來訪問private屬性的方法和成員!

2.3.2 調用方法:

通過Method類中的invoke方法進行方法調用(Object入參爲null,調用靜態方法)!

public class MothodTest
{
	public static void main(String[] args)
	{
		try
		{		
			/*通過反射調用類的靜態方法*/
			Method method = ReflectMothod.class.getMethod("staticfun");			
			method.invoke(null);
			
			/*通過反射對象的實例方法*/
			ReflectMothod r = new ReflectMothod();
			Method method1 = ReflectMothod.class.getMethod("fun");	
			method1.invoke(r);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}		
		
	}
	
}

class ReflectMothod
{
	public static void staticfun()
	{
		System.out.println("staticfun()");
	}
	
	public static void fun()
	{
		System.out.println("fun()");
	}
}

2.3.3 訪問成員:

代碼示例:

public class FieldTest
{
	public static void main(String[] args)
	{
		ReflectField r = new ReflectField();
		r.i = 1;
		r.s = "hello";
		try
		{
			/*獲取普通類型的實例成員,並修改*/
			Field field = ReflectField.class.getField("i");
			System.out.println(field.getInt(r));			
			field.setInt(r, 100);
			
			/*獲取引用類型的實例成員,並修改*/
			Field field1 = ReflectField.class.getField("s");
			System.out.println(field1.get(r));			
			field1.set(r, "hello100");
			
			/*獲取類靜態成員,並修改*/
			
			Field field2 = ReflectField.class.getField("b");
			System.out.println(field2.getBoolean(null));			
			field2.setBoolean(null, true);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		
		System.out.println(r.i);
		System.out.println(r.s);
		System.out.println(ReflectField.b);
	}
	
}

class ReflectField
{
	public int i;
	public String s;
	public static boolean b;
}

結果輸出:

2.3.4 操作數組:

java.lang.reflect包中還提供了一個Array類,用來進行數組類型的反射行爲!

public class ArrayTest
{
	public static void main(String[] args)
	{
		int[] a = new int[5];
		
		/*通過Array類反射創建數組*/
		int[] a1 = (int [])Array.newInstance(int.class, 5);
		
		/*通過Array類反射操作數組*/
		Array.set(a1, 0, 1);
		System.out.println(Array.get(a1, 0));
	}	
}

 

3. 動態代理:

Java基於反射的動態代理就是,將一個接口中的所有方法實現都抽象到InvocationHandler接口中的invoke方法中,再通過Proxy或者其子類創建這個接口的實現類,實現類中通過InvocationHandler接口中的invoke方法來代理原接口中的方法!

public class ReflectProxyTest
{
	public static void main(String[] args)
	{
        /*通過Proxy.newProxyInstance方法,創建ReflectProxy接口的實現類對象*/
		ReflectProxy reflectProxy = 
		(ReflectProxy)Proxy.newProxyInstance(ReflectProxy.class.getClassLoader(), new Class[]{ReflectProxy.class}, new InvocationHandlerTest());
		
		System.out.println(reflectProxy.fun(100));
		reflectProxy.run();
		
		try
		{
            /*通過Proxy.getProxyClass方法,創建ReflectProxy接口的實現類的Class對象*/
			Class<?> cls = Proxy.getProxyClass(ReflectProxy.class.getClassLoader(), new Class[]{ReflectProxy.class});
            /*通過Class對象獲取構造器,參數必須是InvocationHandler.class*/
			Constructor<?> constructor = cls.getConstructor(new Class[]{InvocationHandler.class});
            /*通過構造器創建ReflectProxy接口的實現類對象*/
			ReflectProxy reflectProxy1 = (ReflectProxy)constructor.newInstance(new Object[]{new InvocationHandlerTest()});
			
			System.out.println(reflectProxy1.fun(500));
			reflectProxy1.run();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		
	}	
}

interface ReflectProxy
{
	int fun(int i);
	void run();
}


/*實現InvocationHandler接口中的invoke方法,用於代理ReflectProxy接口中的fun方法和run方法
*invoke的參數說明:
*proxy爲動態代理對象
*method當前調用的方法
*args當前調用方法的入參
*/
class InvocationHandlerTest implements InvocationHandler
{
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
	{
		if ("fun".equals(method.getName()))
		{
			System.out.println("do fun() arg is "+args[0]);
			return args[0];
		}
		
		if ("run".equals(method.getName()))
		{
			System.out.println("do run()");			
		}
		return null;
	}
	
}

結果輸出:

動態代理原理:Proxy.getProxyClass通過反射獲取到接口中的所有抽象方法,再通過ProxyGenerator.generateProxyClass()方法生成一個接口的實現類的字節碼文件(這個實現類中的有一個入參爲InvocationHandler的構造器,並將所有方法的實現都轉爲InvocationHandler的invoke方法進行代理!),最終返回這個接口實現類的Class對象!

 

4. 基於動態代理實現AOP:

可以通過Java對接口的動態代理實現對特定方法增加切面,實現面向切面編程!示例如下:

public class ReflectProxyTest
{
	public static void main(String[] args)
	{
        /*通過Proxy.newProxyInstance方法,創建ReflectProxy接口的實現類對象*/
		ReflectProxy reflectProxy = 
		(ReflectProxy)Proxy.newProxyInstance(ReflectProxy.class.getClassLoader(), new Class[]{ReflectProxy.class}, new InvocationHandlerTest(new ReflectProxyTemp1()));
		
		System.out.println(reflectProxy.fun(100));
		reflectProxy.run();
	}	
}

interface ReflectProxy
{
	int fun(int i);
	void run();
}

/*通過實現InvocationHandler接口,來實現對ReflectProxyTemp1類中方法增加切面*/
class InvocationHandlerTest implements InvocationHandler
{
	private Object tager;
	
	public InvocationHandlerTest(Object tager)
	{
		this.tager = tager;
	}
	
	private void aopFun1()
	{
		System.out.println("aopFun1()");
	}
	
	private void aopFun2()
	{
		System.out.println("aopFun2()");
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
	{
		/*增加Aop切面1*/
		this.aopFun1();
		
		/*通過tager將原始實現傳入InvocationHandler實現類*/
		method.invoke(this.tager, args);
		
		/*增加Aop切面2*/
		this.aopFun2();
		
		if ((null != args) && (0 != args.length))
		{
			return args[0];
		}
		
		return null;
	}	
}

/*原始類型,期望在ReflectProxyTemp1類的fun()方法和run()方法上增加切面*/
class ReflectProxyTemp1 implements ReflectProxy
{
	@Override
	public int fun(int i) 
	{
		System.out.println("ReflectProxyTemp1 fun() i is " + i);
		return 0;
	}

	@Override
	public void run() 
	{		
		System.out.println("ReflectProxyTemp1 run()");
	}
	
}

結果輸出:

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