之前和群友吹水突然聊到反射,说起第一反应是耗时,但为啥耗时,大脑空空说不上来,为了防止下次面试有人问赶紧测试记录一下,没想到测试结果出人意料。
反射是一种编程技术,它允许在运行时获取和操作一个程序的元数据(例如类、字段、方法、构造函数等),以及在运行时动态地创建对象、调用方法和访问成员。
除了Java,许多编程语言也支持类似的反射或元编程特性,允许在运行时获取和操作程序的元数据。以下是一些支持反射或类似特性的编程语言:
使用反射的前提是目标编程语言必须支持反射机制。反射是一种高级特性,它允许在运行时动态地获取、检查和操作程序的元数据,如类、方法、字段等信息。在使用反射时,需要满足以下前提条件:
写一个简单的反射案例和正常调用分别跑1000次看他们的区别
public static void normalExecution(int count) {
long startTime = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
MyClass instance = new MyClass();
instance.setValue(i);
instance.doSomething();
}
long endTime = System.currentTimeMillis();
System.out.println("正常执行循环" + count + "次耗时:" + (endTime - startTime) + "毫秒");
}
public static void reflectionExecutionMethod(int count) {
long startTime = System.currentTimeMillis();
try {
Class<MyClass> clazz = MyClass.class;
Method doSomethingMethod = clazz.getMethod("doSomething");
for (int i = 0; i < count; i++) {
MyClass instance = clazz.newInstance();
doSomethingMethod.invoke(instance);
}
} catch (Exception e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("使用反射执行 getMethod 循环" + count + "次耗时:" + (endTime - startTime) + "毫秒");
}
public static void reflectionExecutionDeclaredField(int count) {
long startTime = System.currentTimeMillis();
try {
Class<MyClass> clazz = MyClass.class;
Field valueField = clazz.getDeclaredField("value");
valueField.setAccessible(true);
for (int i = 0; i < count; i++) {
MyClass instance = clazz.newInstance();
valueField.set(instance, i);
}
} catch (Exception e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("使用反射 getDeclaredField 执行循环" + count + "次耗时:" + (endTime - startTime) + "毫秒");
}
static class MyClass {
private int value;
public void doSomething() {
}
public void setValue(int value) {
this.value = value;
}
}
这块代码我分别在编译器和Android虚拟机执行,Android虚拟机**(Pixel 4 XL API 29)** 循环1000次的结果 编译器:getDeclaredField比getMethod快
Android虚拟机:getDeclaredField比getMethod快
循环100000次 编译器:getDeclaredField比getMethod快
Android虚拟机:getMethod比getDeclaredField快
循环1000000次, 编译器:getMethod比getDeclaredField快
Android虚拟:getDeclaredField比getMethod快
当我第一次看到这个结果的时候也是十分不解,连续点了半个小时下来发现结果依然不同,这时我突然想到是不是编译器的JVM与Android虚拟机的JVM不一样导致的,赶紧查了下资料。
编译器的JVM(Java Virtual Machine)和Android虚拟机的JVM是两种不同的虚拟机,用于执行Java代码。它们在功能、设计和用途上有一些区别,以下是它们的比较: 编译器的JVM:
Android虚拟机的JVM:
字节码解释和JIT编译: 在不同的JVM环境下,字节码的解释和JIT编译可能有所不同。在某些情况下,JIT编译器可能会对频繁调用的方法进行优化,使得getMethod在某些情况下执行更快。而getDeclaredField涉及到访问私有字段并且需要额外的访问权限,可能在某些情况下执行较慢。 优化策略: 不同的JVM可能有不同的优化策略,例如内联、方法内联等,这些优化策略会影响方法的执行性能。 类加载和初始化: 在不同的环境下,类的加载和初始化顺序可能不同,这可能会影响方法调用和字段访问的性能。 运行时环境: 不同的JVM运行在不同的硬件和操作系统上,硬件和操作系统的差异也会影响性能表现。
上面的测试并不全面,但在一定程度上能够反映出反射的确会导致性能问题,同时不同的JVM优化策略区别。如果后面有必要进一步测试,我会从下面几个方面作进一步测试: