反射是java語言的一個特性,它允程序在運行時(注意不是編譯的時候)來進行自我檢查並且對內部的成員進行操作。例如它允許一個java的類獲取他所有的成員變量和方法並且顯示出來。這個能特定我們不常看到,但是在其他的比如C或者C++語言中很不就存在這個特性。一個常見的例子是在JavaBean中,一些組件可以通過一個構造器來操作。這個構造器就是用的反射在動態加載的時候來獲取的java中類的屬性的。
Java語言允許通過程序化的方式間接對Class進行操作,Class文件由類裝載器裝載後,在JVM中將形成一份描述Class結構的元信息對象,通過該元信息對象可以獲知Class的結構信息:如構造函數、屬性和方法等。Java允許用戶藉由這個Class相關的元信息對象間接調用Class對象的功能,這就爲使用程序化方式操作Class對象開闢了途徑。
Java反射機制主要提供了以下功能:
在運行時判斷任意一個對象所屬的類;
在運行時構造任意一個類的對象;
在運行時判斷任意一個類所具有的成員變量和方法;
在運行時調用任意一個對象的方法;生成動態代理。
簡單實例
package com.baobaotao.reflect;
public class Car {
private String brand;
private String color;
private int maxSpeed;
//①默認構造函數
public Car(){}
//②帶參構造函數
public Car(String brand,String color,int maxSpeed){
this.brand = brand;
this.color = color;
this.maxSpeed = maxSpeed;
}
//③未帶參的方法
public void introduce() {
System.out.println("brand:"+brand+";color:"+color+";maxSpeed:" +maxSpeed);
}
//省略參數的getter/Setter方法
…
}
一般情況下,我們會使用如下的代碼創建Car的實例:
Car car = new Car();
car.setBrand("紅旗CA72");
Car car = new Car("紅旗CA72","黑色");
以上兩種方法都採用傳統方式的直接調用目標類的方法,下面我們通過Java反射機制以一種更加通用的方式間接地操作目標類:
package com.baobaotao. reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectTest {
public static Car initByDefaultConst() throws Throwable
{
//①通過類裝載器獲取Car類對象
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class clazz = loader.loadClass("com.baobaotao.reflect.Car");
//②獲取類的默認構造器對象並通過它實例化Car
Constructor cons = clazz.getDeclaredConstructor((Class[])null);
Car car = (Car)cons.newInstance();
//③通過反射方法設置屬性
Method setBrand = clazz.getMethod("setBrand",String.class);
setBrand.invoke(car,"紅旗CA72");
Method setColor = clazz.getMethod("setColor",String.class);
setColor.invoke(car,"黑色");
Method setMaxSpeed = clazz.getMethod("setMaxSpeed",int.class);
setMaxSpeed.invoke(car,200);
return car;
}
public static void main(String[] args) throws Throwable {
Car car = initByDefaultConst();
car.introduce();
}
}
輸出結果:
brand:紅旗CA72;color:黑色;maxSpeed:200
這說明我們完全可以通過編程方式調用Class的各項功能,這和直接通過構造函數和方法調用類功能的效果是一致的,只不過前者是間接調用,後者是直接調用罷了。
在ReflectTest中,使用了幾個重要的反射類,分別是ClassLoader、Class、Constructor和Method,通過這些反射類就可以間接調用目標Class的各項功能了。在①處,我們獲取當前線程的ClassLoader,然後通過指定的全限定類“com.baobaotao.beans.Car”裝載Car類對應的反射實例。在②處,我們通過Car的反射類對象獲取Car的構造函數對象cons,通過構造函數對象的newInstrance()方法實例化Car對象,其效果等同於new Car()。在③處,我們又通過Car的反射類對象的getMethod(String methodName,Class paramClass)獲取屬性的Setter方法對象,第一個參數是目標Class的方法名;第二個參數是方法入參的對象類型。獲取方法反射對象後,即可通過invoke(Object obj,Object param)方法調用目標類的方法,該方法的第一個參數是操作的目標類對象實例;第二個參數是目標方法的入參。
粗體所示部分的信息即是通過反射方法操控目標類的元信息,如果我們將這些信息以一個配置文件的方式提供,就可以使用Java語言的反射功能編寫一段通用的代碼對類似於Car的類進行實例化及功能調用操作了。
IOC(Inverse of
Control)可翻譯爲“控制反轉”,但大多數人都習慣將它稱爲“依賴注入”。在Spring中,通過IOC可以將實現類、參數信息等配置在其對應的配置文件中,那麼當需要更改實現類或參數信息時,只需要修改配置文件即可,這種方法在上例的基礎上更進一步的降低了類與類之間的耦合。我們還可以對某對象所需要的其它對象進行注入,這種注入都是在配置文件中做的,Spring的IOC的實現原理利用的就是Java的反射機制