JAVA反射總結(4)

上兩篇博客,主要介紹了反射的基本應用,這篇博客,主要寫一些關於利用反射來越過泛型約束,通過配置文件的方式使用反射,編寫泛型工具類等!


首先,我們來介紹利用配置文件的方式使用反射,在總結一里面,我們寫道如何使用反射,想要使用反射,就要拿到反射入口,而拿反射入口的方式有三種,其中有一種我們不需要利用現有的類或者對象,那就是Class.forName(String className),這個方法,我們只需將一個類的全類名寫進去,就能拿到該類的反射入口。

而反射其中有一個作用就是:

在運行時調用任意一個對象的方法;

所以我們現在考慮一下,是否可以將該全類名放入一個配置文件,我們訪問通過配置文件的方式來獲取該值,然後拿到反射入口呢,答案當然是可以的,我們不僅可以通過訪問配置文件的方式拿到反射入口,我們還能將該類對應的屬性或者方法都放入配置文件中直接反射操作,這樣,無論是什麼類,在我們要使用其方法或者是業務需求有更新的時候,我們一般不需要去修改源代碼,修改配置文件就能達到我們的目的,說這麼多廢話,下面我們通過一個demo來完成一個小項目。


我們先建立兩個類,TestClass1.java和TestClass2.java。其代碼如下所示:

package com.charles.reflectDemo;

public class TestClass1 {
	private String name;
	private int id;
	public TestClass1() {}
	public TestClass1(String name) {
		super();
		this.name = name;
	}
	public TestClass1(String name, int id) {
		super();
		this.name = name;
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public void sayHello()
	{
		System.out.println("This is test 01:Say Hello");
	}

}

該類包含了一些屬性以及其set和get方法以及一個sayHello()方法;接下來我們看另外一個類TestClass2.java

package com.charles.reflectDemo;

public class TestClass2 {
	private String name;
	private int id;
	public TestClass2() {}
	public TestClass2(String name) {
		super();
		this.name = name;
	}
	public TestClass2(String name, int id) {
		super();
		this.name = name;
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public void sayHi()
	{
		System.out.println("This is test 02:Say Hi");
	}

}

該類包含了一些屬性以及其set和get方法以及一個名爲sayHi()的方法


接下來,我們在項目路徑下建立一個property.txt的屬性文件,其內容如下:

classPath=com.charles.reflectDemo
method=sayHello

我們使用左右相等的形式,即K-V對來保存一些信息,可以看到,第一個等號的右邊是一個全類名,第二個等號的右邊是一個方法名;


我們再建立測試類

package com.charles.reflectDemo;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class TestClass {

	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException
	{
		
		//IO操作
		Properties property=new Properties();
		//加載配置文件
		property.load(new FileReader("property.txt"));	
		//獲取全類名和方法名
		String clazzName=property.getProperty("classPath");
		String methodName=property.getProperty("method");
		//獲取反射入口
		Class<?> testClazz=null;
		testClazz=Class.forName(clazzName);
		//獲取一個反射類的方法對象
		Method method=testClazz.getDeclaredMethod(methodName);
		//執行該方法
		method.invoke(testClazz.newInstance());
		
	}

}

我們通過IO操作配置文件的方式,讀取到了文件裏的類名已經方法名,然後通過反射,執行了該方法(testClass1的sayHello),執行的結果如下:

那麼,我們怎麼體現出:在運行時調用任意一個對象的方法 呢?

現在,我們打開剛剛寫好的配置文件,將其改成:

classPath=com.charles.reflectDemo.TestClass2
method=sayHi

其他代碼我麼都不用改,直接執行,觀察結果:

此時,執行的結果就變成了另一個類的另一個方法,而我們的Java代碼絲毫未變,這就是反射的魅力之一所在,也證實了剛剛我們說的話,其實這是個很簡單的例子,但是也有最基本的精髓在裏面,如果自己想創造出一套框架,就可以利用反射思考,也可以看別人的框架是怎麼實現的。當然,關於這個demo中的io操作此處就不詳述了,那是j2se中的內容了。接下來我們來介紹用java繞過泛型檢查!


我們用一個demo來介紹反射繞過泛型檢查,當然,在平常的使用中基本不會着麼做,因爲這樣會破壞正常的代碼機制和約定。大家知道就行了。代碼如下:

package com.charles.reflectDemo;

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

public class templateTest {
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
	{
		//定義一個動態數組泛型爲Integer對象NumberList
		ArrayList<Integer> NumberList=new ArrayList<Integer>();
		NumberList.add(123456);
		NumberList.add(234567);
		NumberList.add(345678);
		System.out.println(NumberList);
		//開始利用反射越過泛型了
		Class<?> listClazz=ArrayList.class;
		Method method =listClazz.getMethod("add",Object.class);
		method.invoke(NumberList, "charles");
		System.out.println(NumberList);
		
	}

}

執行結果如下:

 

這裏稍稍解釋一下,首先我們定義一個動態數組(集合),並利用泛型約束只能存入Integer類型數據,我們存入三個整形數據,然後打印,此時保存的都是整形數據;然後我們開始利用泛型搞事,先拿到該集合的反射入口,然後通過反射入口拿add

這個方法,將第二個參數設置成Object.class,表示將add方法的反射方法的參數設置爲Object類型,而在之前,我們通過泛型,其只能是Integer類型。利用invoke來執行該方法,將第一個參數放之前的集合對象,第二個參數就可以設置任何類型了,我們這裏用的String類型。最後執行程序,沒問題,我們已經越過泛型。

最後,我們來寫一個簡單的泛型工具類來結束此篇博文,也結束反射的總結!


先說一下我們接下來的這個demo的需求,在本篇博文最開始的那裏有兩個java類,而且可以看見裏面都有很多set方法爲類中的屬性賦值,其實對於此類類,我們稱之爲javabean,有的也叫domain,還有其他地方叫做pojo。那麼我們下面的demo的目的就是寫一個幫助類,爲任意的類的成員的任意屬性賦值。我們建立JAVA文件propertyUtils.java;代碼如下:

package com.charles.reflectDemo;

import java.lang.reflect.Field;

public class propertyUtils {
	public static void setProperty(Object obj,String fieldName,Object value) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException
	{
		Class<?> Clazz=obj.getClass();
		Field field=Clazz.getDeclaredField(fieldName);
		field.setAccessible(true);
		field.set(obj, value);
	}
}

解釋一下:該類只有一個方法public static void setProperty(Object obj,String fieldName,Object value),該方法一共有三個參數,,第一個參數是你想要爲其賦值的對象,第二個參數是該對象的哪個屬性名,第三個參數是你想給該參數賦的值,這裏至於第一個參數和第三個參數類型是Object類型,這裏就不多解釋了,依然是j2se中多態的知識了。我們來執行該方法,建立測試類testUtils.java;代碼如下:

package com.charles.reflectDemo;

public class testUtils {
	public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException
	{
		TestClass1 test=new TestClass1();
		propertyUtils.setProperty(test, "name", "charles");
		System.out.println(test.getName());
		
	}


}

實例化一個TestClass1對象,調用我們自己寫的幫助類的setProperty方法來爲該對象的name屬性賦值爲charles,打印,最後執行結果如下:

至此,反射的相關知識點我個人的話認爲是基本總結完成,當然,也有可能存在沒有說到的地方,歡迎大家留言,更多可以去看源碼或者自己動手玩。

告辭!java反射! 

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