故事背景
小白和小胖在讨论该不该使用反射上,发生了热烈的争执。
小胖:“反射好,可以在运行期间调用对象的任何方法,现在计算机性能高,不用在乎那点损耗”。
小白:“反射性能差,大量使用这种方式进行调用,会有性能或内存隐患。”
围绕这个问题,争论了半天,大家最后达成一致意见。
Talk is Cheap,Show Me The Code
放码过来!
小白很快写出了一个用例:
import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; public class ReflectionTest { private static final int batchsize=1000000; public static void main(String[] args) { //非反射方式测试 long start=System.currentTimeMillis(); addWithoutReflect(); System.out.println(System.currentTimeMillis()-start); //反射方式测试 start=System.currentTimeMillis(); addWithReflect(); System.out.println(System.currentTimeMillis()-start); } //非反射方式 static void addWithoutReflect() { List<String> list=new ArrayList<>(); for(int i=0;i<batchsize;i++) { list.add(""+i); } } //反射方式 static void addWithReflect() { List<String> list=new ArrayList<>(); Method method=null; try { method = list.getClass().getMethod("add", Object.class); } catch (NoSuchMethodException | SecurityException e) { e.printStackTrace(); } for(int i=0;i<batchsize;i++) { try { method.invoke(list, ""+i); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); } } } }
经过不同级别的测试,测试结果如下(本次测试,不做任何数据的修饰):单位毫秒
| 方式 \ 数据量 | 10 | 100 | 1000 | 1w | 10w | 100w | 1kw | 10kw |
| 普通方式 | 0 | 1 | 1 | 4 | 24 | 96 | 6384 | 107714 |
| 反射方式 | 1 | 3 | 4 | 9 | 35 | 117 | 2004 | 71285 |
可以大致看出,在百万以下级别,反射方式的耗时是普通方式的 1~4 倍,千万级别以上的话,反而普通方式比较慢,原因未知,一般情况下,1w 次以下的反射对性能的影响微乎其微(0.05 秒内),可以忽略。
总结
如果只是以非常有限的形式使用反射机制,虽然也要付出少许代价,但是可以获得许多好处。使用反射可能会付出的代价:
- 丧失了编译时类型检查的好处,包含异常检查;
- 执行反射访问所需要的代码非常笨拙和冗长;
- 性能损失;
- 对性能敏感的接口尽量不使用反射或者少使用反射。
