反射在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的例子。