Java 反射入门 | A short introduction to Java reflection

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

Leave a Comment

Your email address will not be published.