今天突然想到Java反射。然后测试了一下1.8.0_191版本的反射性能,没想到反射竟然比直接调用性能还高。
Java反射是指在程序运行状态中,能检查任意对象的内容并调用任意方法。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。
反射调用的类
package com.yq.myreflect;
public class ClassA {
private String name;
public void setName(String name) {
this.name = name;
}
}
测试主类
package com.yq.myreflect;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Method;
@Slf4j
public class MapMain {
private static final int COUNT = 100000;
public static void main(String[] args) {
System.out.println("starting ...");
long start = System.currentTimeMillis();
for (int i = 0; i < COUNT; i++) {
ClassA clzA = new ClassA();
clzA.setName("A" + i);
}
long end = System.currentTimeMillis();
long diff = end - start;
System.out.println("native call:" + diff);
try {
long start2 = System.currentTimeMillis();
for (int i = 0; i < COUNT; i++) {
Class clzA = Class.forName("com.yq.myreflect.ClassA");
Class<?>[] argsType = new Class[1];
argsType[0] = String.class;
Method m = clzA.getMethod("setName", argsType);
Object obj = clzA.newInstance();
m.invoke(obj, "A" + i);
}
long end2 = System.currentTimeMillis();
long diff2 = end2 - start2;
System.out.println("reflect call:" + diff2);
} catch (Exception ex) {
ex.printStackTrace();
}
try {
long start3 = System.currentTimeMillis();
//缓存类,避免多次查找
Class clzA = Class.forName("com.yq.myreflect.ClassA");
Class<?>[] argsType = new Class[1];
argsType[0] = String.class;
//缓存方法,避免多次查找
Method m = clzA.getMethod("setName", argsType);
for (int i = 0; i < COUNT; i++) {
Object obj = clzA.newInstance();
m.invoke(obj, "A" + i);
}
long end3 = System.currentTimeMillis();
long diff3 = end3 - start3;
System.out.println("cache call:" + diff3);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
多次运行的平均结果
starting ...
native call:33
reflect call:126
cache call:16
可以看到如果我们缓存了类,以及要调用的方法后,使用反射比直接调用还快。
//缓存类,避免多次查找
Class clzA = Class.forName("com.yq.myreflect.ClassA");
Class>[] argsType = new Class[1];
argsType[0] = String.class;
//缓存方法,避免多次查找
Method m = clzA.getMethod("setName", argsType);
针对网友提出的,测试的ClassA过于简单,是否能反映真实情况,我把ClassA多添加了一些属性,也在调用方式时,多条用了一些。 结果是类似的。
public class ClassA {
private String name;
private int age;
private String address;
private Map<String, String> projectIdDetailMap = new HashMap<>();
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setAddress(String address) {
this.name = address;
}
public void addProjectExperience(String projectName, String details) {
projectIdDetailMap.put(projectName, details);
}
}
@Slf4j
public class MapMain {
private static final int COUNT = 100000;
public static void main(String[] args) {
System.out.println("starting ...");
long start = System.currentTimeMillis();
for (int i = 0; i < COUNT; i++) {
ClassA clzA = new ClassA();
clzA.setName("A" + i);
clzA.setAge(i);
clzA.setAddress("西安市雁塔区");
clzA.addProjectExperience("持续集成产品", "agent开发");
clzA.addProjectExperience("持续部署产品", "引擎开发");
clzA.addProjectExperience("云平台", "新的平台规划主要功能开发");
}
long end = System.currentTimeMillis();
long diff = end - start;
System.out.println("native call:" + diff);
try {
long start2 = System.currentTimeMillis();
for (int i = 0; i < COUNT; i++) {
Class clzA = Class.forName("com.yq.myreflect.ClassA");
Class<?>[] argsType = new Class[1];
argsType[0] = String.class;
Class<?>[] argsTypeInt = new Class[1];
argsTypeInt[0] = Integer.class;
Class<?>[] argsTypeMap = new Class[2];
argsTypeMap[0] = String.class;
argsTypeMap[1] = String.class;
Method setName = clzA.getMethod("setName", argsType);
Method setAge = clzA.getMethod("setAge", argsTypeInt);
Method setAddress = clzA.getMethod("setAddress", argsType);
Method addProjectExperience = clzA.getMethod("addProjectExperience", argsTypeMap);
Object obj = clzA.newInstance();
setName.invoke(obj, "A" + i);
setAge.invoke(obj, i);
setAddress.invoke(obj, "西安市雁塔区");
addProjectExperience.invoke(obj, "持续集成产品", "agent开发");
addProjectExperience.invoke(obj, "持续部署产品", "引擎开发");
addProjectExperience.invoke(obj, "云平台", "新的平台规划主要功能开发");
}
long end2 = System.currentTimeMillis();
long diff2 = end2 - start2;
System.out.println("reflect call:" + diff2);
} catch (Exception ex) {
ex.printStackTrace();
}
try {
long start3 = System.currentTimeMillis();
Class clzA = Class.forName("com.yq.myreflect.ClassA");
Class<?>[] argsType = new Class[1];
argsType[0] = String.class;
Class<?>[] argsTypeInt = new Class[1];
argsTypeInt[0] = Integer.class;
Class<?>[] argsTypeMap = new Class[2];
argsTypeMap[0] = String.class;
argsTypeMap[1] = String.class;
Method setName = clzA.getMethod("setName", argsType);
Method setAge = clzA.getMethod("setAge", argsTypeInt);
Method setAddress = clzA.getMethod("setAddress", argsType);
Method addProjectExperience = clzA.getMethod("addProjectExperience", argsTypeMap);
for (int i = 0; i < COUNT; i++) {
Object obj = clzA.newInstance();
setName.invoke(obj, "A" + i);
setAge.invoke(obj, i);
setAddress.invoke(obj, "西安市雁塔区");
addProjectExperience.invoke(obj, "持续集成产品", "agent开发");
addProjectExperience.invoke(obj, "持续部署产品", "引擎开发");
addProjectExperience.invoke(obj, "云平台", "新的平台规划主要功能开发");
}
long end3 = System.currentTimeMillis();
long diff3 = end3 - start3;
System.out.println("cache call:" + diff3);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
我的Java8, Windows10上面多次运行结果,
native call:91
reflect call:530
cache call:71