Java基礎:反射機制的簡單總結

什麼是反射機制?

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

動態獲取類中信息,就是java反射 。
可以理解爲對類的解剖。

看了上面的文字可能對反射機制還是不會特別清楚,那麼我們就直接來看看反射機制的實現的代碼。

再講之前呢?我們需要做一件小小的事,不然後面無法演示:
創建一個 cn.itcast.bean 的包,然後創建一個Person的類:

package cn.itcast.bean;

public class Person {

	private int age;
	private String name;
	
	public Person(String name,int age) {
		super();
		this.age = age;
		this.name = name;
		
		System.out.println("Person param run...有參數構造函數     "+this.name+":"+this.age);
	
	}
	public Person() {
		super();		
		System.out.println("person run  無參數構造函數");		
		
	}
	
	public void show(){
		System.out.println("...show run...");
	}
	
	private void privateMethod(){
		System.out.println(" method run ");
	}
	
	public void paramMethod(String str,int num){
		System.out.println("paramMethod run....."+str+":"+num);
		
	}
	
	public static void staticMethod(){
		System.out.println(" static method run......");
	}
}

我們先來看看這樣一個問題:如何獲取字節碼文件對象呢?

這裏有三種方法,我們依次來看看。

方法一:

import cn.itcast.bean.Person;
public class RefllectDemo {
	public static void main(String[] args) {
		getClassObject();
	}
	/*
	 * 獲取字節碼對象的方式:
	 * 1,Object類中的getClass()方法的。
	 * 想要用這種方式,必須要明確具體的類,並創建對象。
	 * 麻煩 .
	 * 
	 */
	public static void getClassObject() {	
		Person p = new Person();	
		System.out.println(p.getClass());		
	}
}

方法二:

import cn.itcast.bean.Person;
public class RefllectDemo {
	public static void main(String[] args) {
		getClassObject();
	}

	/*
	 * 方式二:
	 * 2,任何數據類型都具備一個靜態的屬性.class來獲取其對應的Class對象。
	 * 相對簡單,但是還是要明確用到類中的靜態成員。
	 * 還是不夠擴展。 
	 * 
	 * 此方法不好的原因是,使用這種方法獲取,需要導入包,纔可以。
	 * 
	 */
	public static void getClassObject() {
		System.out.println(Person.class);		
	}
}

方法三:

public class RefllectDemo {
	public static void main(String[] args) throws ClassNotFoundException {
		getClassObject();
	}
	/*
	 * 方式三:
	 * 只要通過給定的類的 字符串名稱就可以獲取該類,更爲擴展。
	 * 可是用Class類中的方法完成。
	 * 該方法就是forName.
	 * 這種方式只要有名稱即可,更爲方便,擴展性更強。 
	 */
	public static void getClassObject() throws ClassNotFoundException {	
		String name = "cn.itcast.bean.Person";
		Class clazz = Class.forName(name);
		
		System.out.println(clazz);			
	}
}

這三種方法的運行結果中都包含:class cn.itcast.bean.Person

我們發現,只有第三種方法我們只需要知道類的完整名字就可以獲取這個類了,這種方法的擴展性更強,
爲什麼?
我簡單的說明一下,如果我們把我們需要的類的完整的名字放在一個文件裏面,然後在我們的程序中這個文件與IO流相連,然後我們在利用反射機制,調用這些類,執行其他程序,那麼以後呢?當我們想要執行其他類的時候,這時反射機制的用處就來了,我們不需要修改代碼,只需要將需要執行的類的完整名字放在文件中就可以了。

可能看到這裏還有疑惑,沒事,一會在舉一個代碼的例子,現在我們來看看反射機制中我們需要了解的東西。

獲取構造函數

獲取無參構造函數:

public class RefllectDemo2 {
	public static void main(String[] args) throws Exception {
		createNewObject();
	}

	/*
	 * 獲取無參構造函數
	 */
	public static void createNewObject() throws Exception {		
		//早期:new時候,先根據被new的類的名稱找尋該類的字節碼文件,並加載進內存,
//		並創建該字節碼文件對象,並接着創建該字節文件的對應的Person對象.
//		cn.itcast.bean.Person p = new cn.itcast.bean.Person();
		
		//現在:
		String name = "cn.itcast.bean.Person";
		//找尋該名稱類文件,並加載進內存,併產生Class對象。
		Class clazz = Class.forName(name);
		//如何產生該類的對象呢?
		Object obj  = clazz.newInstance();
		System.out.println(obj);
	}
}

運行結果:

person run  無參數構造函數
cn.itcast.bean.Person@15db9742

獲取有參構造函數:

import java.lang.reflect.Constructor;

public class RefllectDemo2 {
	public static void main(String[] args) throws Exception {
		createNewObject();
	}
	/*
	 * 獲取有參構造函數
	 */
	public static void createNewObject() throws Exception {		
		String name = "cn.itcast.bean.Person";
		Class clazz = Class.forName(name);

		//獲取有參構造函數
		Constructor constructor = clazz.getConstructor(String.class,int.class);				
		//public Constructor<T> getConstructor(Class<?>... parameterTypes)
		
		Object obj = constructor.newInstance("cxf",20);	
	}
}

運行結果:

Person param run...有參數構造函數     cxf:20

獲取字段

import java.lang.reflect.Field;
public class ReflectDemo3 {
	public static void main(String[] args) throws Exception {
		/*
		 * 獲取字節碼文件中的字段。
		 */
		
		Class clazz = Class.forName("cn.itcast.bean.Person");
		
		//Field field = clazz.getField("age");無法使用,因爲age是private所以無法返回所以必須使用getDeclaredField()方法。
		Field field = clazz.getDeclaredField("age");
		
		//對私有字段的訪問取消權限檢查。暴力訪問。
		field.setAccessible(true);
		//如果沒有這局代碼,那麼會拋java.lang.IllegalAccessException
		
		/*
		 * 我們獲取一個字段的基本思路是:先創建一個對象,然後在調用,所以在反射機制中也是這樣
		*	Person p = new Person()
		*	p.getAge()
		*/
				
		Object obj = clazz.newInstance();
		
		//設置obj對象的age的值
		field.set(obj, 89);
		
		//獲取obj對象的age的值
		Object o = field.get(obj);
		
		System.out.println(o);

	}
}

輸出結果:

person run  無參數構造函數
89

獲取一般方法

	/*
	 * 獲取所有公有方法,包括父類的方法
	 */	
	public static void getMethod() throws Exception {	
		Class clazz = Class.forName("cn.itcast.bean.Person");
		
		Method[] methods = clazz.getMethods();  
		//獲取所有公有方法,包括父類的方法
		
		for(Method method : methods)
			System.out.println(method);
	}

輸出結果:

public void cn.itcast.bean.Person.show()
public void cn.itcast.bean.Person.paramMethod(java.lang.String,int)
public static void cn.itcast.bean.Person.staticMethod()
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 static void getMethod_1() throws Exception {
		Class clazz = Class.forName("cn.itcast.bean.Person");
		
		Method[] methods = clazz.getDeclaredMethods(); 
		//獲本類中的所有方法
		
		for(Method method : methods)
			System.out.println(method);		
	}

輸出結果:

public void cn.itcast.bean.Person.show()
public void cn.itcast.bean.Person.paramMethod(java.lang.String,int)
private void cn.itcast.bean.Person.privateMethod()
public static void cn.itcast.bean.Person.staticMethod()

獲取無參一般函數:

import java.lang.reflect.Method;
public class ReflectDemo4 {
	public static void main(String[] args) throws Exception {
		getMethod();
	}
	//獲取無參函數
	public static void getMethod() throws Exception {	
		Class clazz = Class.forName("cn.itcast.bean.Person");
		
		//無參所以是null
		Method method = clazz.getMethod("show", null);
		
		/*
		 * 我們獲取一個字段的基本思路是:先創建一個對象,然後在調用,所以在反射機制中也是這樣
		*	Person p = new Person()
		*	p.getAge()
		*/
		//如果方法時私有的話可以使用
		//method.setAccessible(true);
		
		Object obj = clazz.newInstance();
		
		//運行此方法
		method.invoke(obj, null);		
	}
}

輸出結果:

person run  無參數構造函數
...show run...

獲取有參一般函數:

import java.lang.reflect.Method;
public class ReflectDemo4 {
	public static void main(String[] args) throws Exception {
		getMethod();
	}
	//獲取無參函數
	public static void getMethod() throws Exception {	
		Class clazz = Class.forName("cn.itcast.bean.Person");

		Method method = clazz.getMethod("paramMethod", String.class,int.class);		
		/*
		 * 我們獲取一個字段的基本思路是:先創建一個對象,然後在調用,所以在反射機制中也是這樣
		*	Person p = new Person()
		*	p.getAge()
		*/
		//如果方法時私有的話可以使用
		//method.setAccessible(true);
		
		Object obj = clazz.newInstance();
		
		//運行此方法
		method.invoke(obj, "cxf",20);		
	}
}

輸出結果:

person run  無參數構造函數
paramMethod run.....cxf:20

彙總

獲取構造方法

無參構造方法:

Class clazz = Class.forName(“cn.itcast.bean.Person”);
Object obj = clazz.newInstance();

有參構造方法:
Constructor constructor = clazz.getConstructor(Class<?>… parameterTypes)
獲取全部公有字段,包括父類

Constructor constructor = clazz.getDeclaredConstructor(Class<?>… parameterTypes)
獲取本類的所有字段,包括私有,但不包括父類。

Object obj = constructor.newInstance(Object … initargs);

獲取字段用:Field
可用方法:
getField( ):獲取全部公有字段,包括父類
getDeclaredField( ):獲取本類的所有字段,包括私有,但不包括父類。

獲取一般函數
字段用Method

Class clazz = Class.forName(“cn.itcast.bean.Person”);

Method methods[] = clazz.getMethods();
獲取共有方法,包括父類中的

Method methods[] = clazz.getDeclaredMethods();
獲取本類的所有字段,包括私有,但不包括父類。

如果想要使用私有的東西的話需要:
setAccessible(true)

反射機制樣例

創建一個包 cn.itcast.reflect.test
然後

創建 MainBoard 類:

package cn.itcast.reflect.test;

public class MainBoard {
	public static void run() {
		System.out.println("main board run..........");
	}
	
	public static void usePCI(PCI p) { // PCI p = new SoundBoard()
		p.open();
		p.close();
	}
}

創建 PCI 接口:

package cn.itcast.reflect.test;
public interface  PCI {
	
	public void open() ;
	public void close();
}

創建 SoundBoard 類:

package cn.itcast.reflect.test;

public class SoundBoard implements PCI {

	@Override
	public void open() {
		System.out.println("sound open........");
	}

	@Override
	public void close() {
		System.out.println("sound close........");
	}
}

創建 NetBoard 類:

package cn.itcast.reflect.test;

public class NetBoard implements PCI {

	@Override
	public void open() {
		System.out.println("net open.........");
	}

	@Override
	public void close() {
		// TODO Auto-generated method stub
		System.out.println("net close.........");
	}
}

創建 ReflectTest 類:

package cn.itcast.reflect.test;
import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;
public class ReflectTest {

	public static void main(String[] args) throws Exception {
		
		MainBoard mainboard = new MainBoard();
		
		mainboard.run();		
		//每次添加一個設備都需要修改代碼傳遞一個新創建的對象
//		mainboard.usePCI(new SoundCard());
		//能不能不修改代碼就可以完成這個動作。 
//		不用new來完成,而是隻獲取其class文件。在內部實現創建對象的動作。 
		
		File file = new File("className.properties");		
		if(!file.exists())
			file.createNewFile();
	
		FileInputStream fis = new FileInputStream(file);
	
		Properties prop = new Properties();
	
		prop.load(fis);
	
		for(int x = 1;x<=prop.size();x++) {
		
			String className = prop.getProperty("pci"+x);
		
			Class clazz = Class.forName(className);
		
			PCI p = (PCI)clazz.newInstance();
		
			mainboard.usePCI(p);			
		}		
		fis.close();		
	}
}

創建一個 className.properties 的配置文件

pci1=cn.itcast.reflect.test.SoundBoard
pci2=cn.itcast.reflect.test.NetBoard

運行結果:

main board run..........
sound open........
sound close........
net open.........
net close.........

在這裏呢?我只想說,這個例子進一步說明了反射機制的高效,我們不需要修改程序,我們只需要改動配置文件就可以讓程序產生不易樣的結果,使代碼變得更加的高效。

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