正文
预备测试对象
下面先定义一个测试的类TestUser,只需id跟name属性,以及它们的getter/setter要领,别的另有一个自定义的sayHi要领。
public class TestUser { private Integer id; private String name; public String sayHi(){ return "hi"; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
测试建立100万个对象
// 经由过程一般体式格局建立TestUser对象@Testpublic void testCommon(){ long start = System.currentTimeMillis(); TestUser user = null; int i = 0; while(i<1000000){ ++i; user = new TestUser(); } long end = System.currentTimeMillis(); System.out.println("一般对象建立耗时:"+(end - start ) + "ms"); }//一般对象建立耗时:10ms
// 经由过程反射体式格局建立TestUser对象@Testpublic void testReflexNoCache() throws Exception { long start = System.currentTimeMillis(); TestUser user = null; int i = 0; while(i<1000000){ ++i; user = (TestUser) Class.forName("ReflexDemo.TestUser").newInstance(); } long end = System.currentTimeMillis(); System.out.println("无缓存反射建立对象耗时:"+(end - start ) + "ms"); }//无缓存反射建立对象耗时:926ms
在上面这两个测试要领中,笔者各自测了5次,把他们斲丧的时刻取了一个平均值,在输出效果中能够看到一个是10ms,一个是926ms,在建立100W个对象的情况下,反射竟然慢了90倍摆布。wtf?差异竟然这么大?岂非反射真的这么慢?下面笔者换一种反射的姿态,继承测试一下,看看效果如何?
// 经由过程缓存反射体式格局建立TestUser对象@Testpublic void testReflexWithCache() throws Exception { long start = System.currentTimeMillis(); TestUser user = null; Class rUserClass = Class.forName("RefleDemo.TestUser"); int i = 0; while(i<1000000){ ++i; user = (TestUser) rUserClass.newInstance(); } long end = System.currentTimeMillis(); System.out.println("经由过程缓存反射建立对象耗时:"+(end - start ) + "ms"); }//经由过程缓存反射建立对象耗时:41ms
实在经由过程代码我们能够发明,是Class.forName这个要领比较耗时,它现实上挪用了一个当地要领,经由过程这个要领来请求JVM查找并加载指定的类。所以我们在项目中运用的时刻,能够把Class.forName返回的Class对象缓存起来,下一次运用的时刻直接从缓存内里猎取,如许就极大的进步了猎取Class的效力。同理,在我们猎取Constructor、Method等对象的时刻也能够缓存起来运用,防止每次运用时再来消耗时刻建立。
测试反射挪用要领
@Testpublic void testReflexMethod() throws Exception { long start = System.currentTimeMillis(); Class testUserClass = Class.forName("RefleDemo.TestUser"); TestUser testUser = (TestUser) testUserClass.newInstance(); Method method = testUserClass.getMethod("sayHi"); int i = 0; while(i<100000000){ ++i; method.invoke(testUser); } long end = System.currentTimeMillis(); System.out.println("反射挪用要领耗时:"+(end - start ) + "ms"); }//反射挪用要领耗时:330ms
@Testpublic void testReflexMethod() throws Exception { long start = System.currentTimeMillis(); Class testUserClass = Class.forName("RefleDemo.TestUser"); TestUser testUser = (TestUser) testUserClass.newInstance(); Method method = testUserClass.getMethod("sayHi"); int i = 0; while(i<100000000){ ++i; method.setAccessible(true); method.invoke(testUser); } long end = System.currentTimeMillis(); System.out.println("setAccessible=true 反射挪用要领耗时:"+(end - start ) + "ms"); }//setAccessible=true 反射挪用要领耗时:188ms
这里我们反射挪用sayHi要领1亿次,在挪用了method.setAccessible(true)后,发明快了快要一半。检察API能够相识到,jdk在设置猎取字段,挪用要领的时刻会实行平安接见搜检,而此类操纵会比较耗时,所以经由过程setAccessible(true)的体式格局能够封闭平安搜检,从而提拔反射效力。
极致的反射
除了上面的手腕,另有没有什么方法能够更极致的运用反射呢?这里引见一个高机能反射工具包ReflectASM。它是经由过程字节码生成的体式格局来完成的反射机制,下面是一个跟java反射的机能比较。
结语
末了总结一下,为了更好的运用反射,我们应该在项目启动的时刻将反射所须要的相干设置及数据加载进内存中,在运转阶段都从缓存中取这些元数据举行反射操纵。人人也不必恐惧反射,虚拟机在不停的优化,只需我们要领用的对,它并没有”听说“中的那末慢,当我们对机能有极致寻求的时刻,能够斟酌经由过程三方包,直接对字节码举行操纵。
相干教程:Java视频教程
以上就是如何进步运用Java反射的效力的细致内容,更多请关注ki4网别的相干文章!