反射在Java是個比較奇怪的概念,背後的思想其實很深刻。一般程序包括程序(code)和數據(data)兩部分。如果一個程序計算1+1,那麼1和1是數據,而加法這個操作是code,code是編譯時就決定的,編譯後就不能再更改,比如這裡的加法。而反射這個概念允許了data也可以是code的概念。比如現在程序變成了f(1, 1),f是一個未知函數,它也是”data”,可以由用戶提供。用戶提供了乘法,那就是乘法,提供了減法,那就是減法。這樣提供一個讓程序在運行時修改自己的機制。這樣能提高程序的表達力,一些Java的框架比如Spring就大量用到反射。Spring裏面很多magical的現象就是這樣實現的,比如我明明只寫了一個class,另一個地方我就自動有了一個這個class的object,而我明明沒有在任何地方初始化這麼一個對象。IDE裏面自動提示一個class裏面的available methods應該也是用到了reflection。在應用中有些壞處也是顯而易見,需要小心。在Java裏面reflection速度慢還是其次,IDE很難檢測到代碼被引用,還有很多compiler可以檢測到的錯誤被漏過了,只能在run time出問題。而且允許外部輸入代碼也帶來安全問題。很多網絡攻擊的原理比如SQL injection就是利用了data也是code的這種特性。關於refelection很多的用法以後我會再寫。
本文通過一個簡單的Java例子來演示一下Java的reflection。Just get a feeling.
Animal.java
package learn.java.reflection;
public interface Animal {
public void whoami();
}
Tiger.java
package learn.java.reflection;
public class Tiger implements Animal {
@Override
public void whoami() {
System.out.println("I am a tiger");
}
}
Lion.java
package learn.java.reflection;
public class Lion implements Animal {
@Override
public void whoami() {
System.out.println("I am a lion");
}
}
Zoo.java
package learn.java.reflection;
import java.util.Scanner;
public class Zoo {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Scanner scanner = new Scanner(System.in);
System.out.println("Choose [1] for tiger, [2] for lion: ");
String opt = scanner.nextLine();
String className = null;
while (true) {
if ("1".equals(opt)) {
className = "learn.java.reflection.Tiger";
break;
} else if ("2".equals(opt)) {
className = "learn.java.reflection.Lion";
break;
} else {
System.out.println("Choose [1] for tiger, [2] for lion: ");
opt = scanner.nextLine();
}
}
Class cls = Class.forName(className);
Animal animal = (Animal) cls.newInstance();
animal.whoami();
}
}
運行結果
Choose [1] for tiger, [2] for lion:
1
I am a tiger
Choose [1] for tiger, [2] for lion:
2
I am a lion
Zoo.java 為主程序。裏面的animal是什麼要看用戶提供的data決定而不是compile time決定的。在IDE裏面,都找不到Lion.java和Tiger.java的引用。因為他們是這樣被用到的:Class cls = Class.forName(className)
。className還是個runtime才能知道的String,連人類都不是很容易能發現,IDE能知道就真的實現人工智能了。
雖然我本人並不是Reflection的粉絲,但是因為合作中同事可能會用到,或者公司所選的框架用到,比如Spring之類也是被廣泛使用,還是應該抱有開放的心態學習並駕馭它。
另外,本文提供的小程序程序也包含了Java從鍵盤輸入data的例子。