今天的討論主題又是老生常談,Java反射的鼎鼎大名我想諸君多多少少都有所耳聞。正所謂難者不會會者不難,對於瞭解反射機制的人來講也就那麼一回事,但對初學者來講確猶如隔霧看山。今天筆者一樣會以儘可能通俗的文字來同你們一塊兒揭開反射神祕的面紗。java
可以分析類能力的程序被稱爲反射(reflective),經過反射能夠在運行時(而不是在編譯時)動態地分析軟件組件並描述組件的功能。框架
首先咱們要明確一點,反射並非什麼很難很高深的東西。正相反,反射其實就是Java系統提供給咱們的一系列API,具體來講就是由java.lang.reflect包提供的。就跟調用其餘API一個道理,咱們只要瞭解了這個API用法,那就掌握了反射。函數
所謂存在即合理,既然Java給咱們提供了這個功能,那麼它必定是有用的。那它具體有啥用?經過反射,咱們能夠spa
試想這樣一個場景,咱們有一個產品類code
public class Product {
private String mName;
private int mPrice;
}
複製代碼
而後咱們如今要弄一個工廠類來生產這個產品。對象
有朋友說這有何難?看個人開發
public class Factory {
public Product create(){
return new Product();
}
}
複製代碼
而後調用這個工廠來生產便可get
public class Client {
public static void main(String[] args) {
Factory mFactory = new Factory();
Product product = mFactory.create();
}
}
複製代碼
乍一看好像沒毛病,工廠生產的時候new一個Product不就完事了嗎。string
那好,我如今要求這個工廠要生產兩個不一樣的產品又咋整?產品
朋友說,那也簡單,我再加一個create方法不就好了
class Factory {
public ProductA createA(){
return new ProductA();
}
public ProductB createB(){
return new ProductB();
}
}
複製代碼
那如今問題就來了:
有槓精可能會說:你是否是在存心刁難我?我本身寫的程序我還不曉得它要生產個啥?
你還別說,還真會有這種狀況。試想假如你如今是在開發一個框架給別人用,這個Product類是交由這個框架的使用者來定義的,那你怎麼能知作別人究竟是定義了一個牛仍是一個馬?那我如今要生產這個東西,可是我又不知道究竟是要生產一個啥玩意兒,那咋整?
嗯。。。好像確實不行。
那能不能有這樣一種牛逼的技術,一個方法就能夠生產一萬個不一樣的產品,甚至我不知道它具體是個啥東西我都能生產出來?
巧了,還真有,這個牛逼的技術就是咱們今天所說的反射。
咱們對工廠類進行改寫
public class Factory {
public <T> T create(Class clazz){
Object object = null;
try {
object = Class.forName(clazz.getName()).newInstance();
} catch (IllegalAccessException | InstantiationException | ClassNotFoundException e) {
e.printStackTrace();
}
return (T)object;
}
}
複製代碼
而後生產
public class Client {
public static void main(String[] args) {
Factory mFactory = new Factory();
Product product = mFactory.create(Product.class);
}
}
複製代碼
好了,這樣咱們就使用反射建立了一個產品的實例。要建立多個不一樣的產品,只須要傳入不一樣的參數便可。
如今來解釋一下這個create方法。
首先咱們定義返回類型爲泛型T,方法的形參爲Class類型代表傳入的是一個類。
而後Class.forName方法是經過類加載器來加載這個類,加載的實現遵循雙親委派機制。關於雙親委派機制的原理後面會寫專欄介紹。
最後調用newInstance方法來返回這個類的實例。
這樣咱們就經過反射得到了一個類的對象,而且在這個過程當中咱們並不關心這個加載的類究竟是個啥東西。
除了獲取類的對象以外,反射還能夠分析類的能力。
咱們先給產品類整幾個構造函數
public class Product {
private String mName;
private int mPrice;
public Product() { }
public Product(String name) {
mName = name;
}
public Product(int price) {
mPrice = price;
}
public Product(String name, int price) {
mName = name;
mPrice = price;
}
}
複製代碼
而後改寫工廠類
public class Factory {
public void create(Class clazz){
//得到構造函數
Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
System.out.println("" + constructor);
}
}
}
複製代碼
跑一下看看結果
public com.samiu.archi.reflect.Product(java.lang.String,int)
public com.samiu.archi.reflect.Product(int)
public com.samiu.archi.reflect.Product(java.lang.String)
public com.samiu.archi.reflect.Product()
複製代碼
OK,這不就把構造函數打印粗來了。
咱們對產品類進行改寫,多整幾個變量
public class Product {
//私有變量
private String mName;
private int mPrice;
//公共變量
public float width;
public float height;
}
複製代碼
而後改寫工廠類
public class Factory {
public void create(Class clazz){
//得到fields
Field[] fields = clazz.getFields();
System.out.println("fields:");
for (Field field : fields) {
System.out.println("" + field);
}
//得到declaredFields
Field[] declaredFields = clazz.getDeclaredFields();
System.out.println("declaredFields:");
for (Field field : declaredFields) {
System.out.println("" + field);
}
}
}
複製代碼
打印一下結果
fields:
public float com.samiu.archi.reflect.Product.width
public float com.samiu.archi.reflect.Product.height
declaredFields:
private java.lang.String com.samiu.archi.reflect.Product.mName
private int com.samiu.archi.reflect.Product.mPrice
public float com.samiu.archi.reflect.Product.width
public float com.samiu.archi.reflect.Product.height
複製代碼
能夠看到,getFields方法獲取了類的公共變量,而getDeclaredFields方法獲取了類的全部變量,包括私有變量
咱們先改寫產品類,給它多整幾個方法
public class Product {
private String mName;
private int mPrice;
public String getName() {
return mName;
}
private void setName(String name) {
mName = name;
}
public int getPrice() {
return mPrice;
}
private void setPrice(int price) {
mPrice = price;
}
}
複製代碼
而後改寫工廠類
public class Factory {
public void create(Class clazz){
//得到methods
Method[] methods = clazz.getMethods();
System.out.println("methods:");
for (Method method:methods){
System.out.println(""+method);
}
System.out.println("");
//得到declaredMethods
Method[] declaredMethods = clazz.getDeclaredMethods();
System.out.println("declaredMethods:");
for (Method method:declaredMethods){
System.out.println(""+method);
}
}
}
複製代碼
打印一下看看
methods:
public java.lang.String com.samiu.archi.reflect.Product.getName()
public int com.samiu.archi.reflect.Product.getPrice()
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()
declaredMethods:
public java.lang.String com.samiu.archi.reflect.Product.getName()
private void com.samiu.archi.reflect.Product.setName(java.lang.String)
private void com.samiu.archi.reflect.Product.setPrice(int)
public int com.samiu.archi.reflect.Product.getPrice()
複製代碼
能夠看到,getMethods獲取了類的全部公共方法,包括定義在Object類裏面的公共方法,而getDeclaredMethods則獲取了類本身定義的全部方法,包括私有的方法。
除了這些之外,反射還有不少實用的功能,這裏就不展開細講了,感興趣的朋友能夠本身去查閱。
今天回顧了一下Java的反射,但願能幫到一些朋友大體弄明白反射的機制。
1.《Java核心技術 第八版》
2.《Java徹底參考手冊 第八版》