Java反射动态的获取到对象的信息以及灵活的调用对象方法等,但是听说效率很慢,测试下。

普通创建对象对比反射创建对象

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;}
}public class TestDemo {public static void main(String[] argv){testCommon();testReflexNoCache();}public 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");}// 通过反射方式创建TestUser对象public 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");}
}输出:
普通对象创建耗时:10ms
无缓存反射创建对象耗时:966ms

创建100W个对象的情况下,反射居然慢了90倍左右!

通过缓存反射方式创建对象

public 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等对象的时候也可以缓存起来使用,避免每次使用时再来耗费时间创建。

为什么Java反射性能慢效率低
程序运行期的即时编译器(JIT 编译器,Just In Time Compiler)把字节码文件编译成机器码的过程;其中即时编译器(JIT)在运行期的优化过程对于程序运行来说更重要,Java虚拟机在编译阶段的代码优化就在这里进行,由于反射涉及动态解析的类型,因此无法执行某些Java虚拟机优化。因此,反射操作的性能要比非反射操作慢,因此应该避免在对性能敏感的应用程序中频繁使用Java反射来创建对象。

反射调用方法

public 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

通过setAccessible(true)的方式可以关闭安全检查

public 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

查看API可以了解到,jdk在设置获取字段,调用方法的时候会执行安全访问检查,而此类操作会比较耗时,所以通过setAccessible(true)的方式可以关闭安全检查,从而提升反射效率

高性能反射工具包ReflectASM

ReflectASM通过字节码生成的方式实现了更为高效的反射机制。执行时会生成一个存取类来 set/get 字段,访问方法或创建实例。

依赖:

<!--java 反射工具包 -->
<!-- https://mvnrepository.com/artifact/com.esotericsoftware/reflectasm -->
<dependency><groupId>com.esotericsoftware</groupId><artifactId>reflectasm</artifactId><version>1.11.3</version>
</dependency>

asm框架包依赖:

<dependency><groupId>org.ow2.asm</groupId><artifactId>asm</artifactId><version>9.2</version>
</dependency>

ConstructorAccess反射来调用构造方法

  • public static ConstructorAccess get(Class type):生成字节码的方式创建 ConstructorAccess

    • public abstract T newInstance():创建实例。

创建测试类User:

class User {public int age;public String name;public User() {}public User(int age, String name) {this.age = age;this.name = name;}@Overridepublic String toString() {return "User{" + "age=" + age + ", name='" + name + '\'' + '}';}public void update(int age, String name) {this.age = age;this.name = name;}
}

ReflectAsm反射来调用构造方法

public class ReflectAsmDemo {public static void main(String[] args) throws Exception {testConstructorAccess();}/*** ReflectAsm反射来调用构造方法*/public static void testConstructorAccess() {ConstructorAccess<User> constructorAccess = ConstructorAccess.get(User.class);User user = constructorAccess.newInstance();System.out.println(user);}
}输出:
User{age=0, name='null'}

FieldAccess反射获取属性

  • public static FieldAccess get(Class type):生成字节码的方式创建 FieldAccess

    • public void set(Object instance, String fieldName, Object value):设置fieldName属性值。
    • public Object get(Object instance, String fieldName):获取fieldName属性值。
    • public int getFieldCount():获取字段总个数。
    • public String[] getFieldNames():获取字段名称数组。
    • public int getIndex(String fieldName):获取指定字段index。
public class ReflectAsmDemo {public static void main(String[] args) throws Exception {testFieldAccess();}/*** ReflectAsm反射来set/get字段值*/public static void testFieldAccess() {FieldAccess fieldAccess = FieldAccess.get(User.class);String[] fieldNames = fieldAccess.getFieldNames();for (String fieldName : fieldNames) {System.out.println("fieldName=" + fieldName);}System.out.println("FieldCount=" + fieldAccess.getFieldCount());System.out.println("index=" + fieldAccess.getIndex("name"));User target = new User(28,"lili");fieldAccess.set(target, "age", 1);int age = (Integer)fieldAccess.get(target, "age");System.out.println(age);}
}输出:
fieldName=age
fieldName=name
FieldCount=2
index=1
1

FieldAccess访问私有字段,会报错Unable to find non-private field: …。
如果想访问私有字段,可以使用反射功能先放开权限。
Field field = Animal.class.getDeclaredField(“id”);
field.setAccessible(true);

MethodAccess反射调用方法

  • public static MethodAccess get(Class type):生成字节码的方式创建 MethodAccess

    • public String[] getMethodNames():获取方法名称数组。
    • public Object invoke(Object object, String methodName, Object… args):用方法名定位反射方法
    • public int getIndex(String methodName, Class… paramTypes):用方法和字段的索引定位反射方法,性能高
    • public abstract Object invoke(Object var1, int index, Object… var3):index是上面函数获取的索引index。
public class ReflectAsmDemo {public static void main(String[] args) throws Exception {testMethodAccess();testMethodIndexAccess();}/*** ReflectAsm反射调用方法* 用名称定位反射方法*/public static void testMethodAccess() {User target = new User();MethodAccess methodAccess = MethodAccess.get(User.class);String[] methodNames = methodAccess.getMethodNames();for (String methodName : methodNames) {System.out.println("methodName=" + methodName);}methodAccess.invoke(target, "update", 1, "jack");System.out.println(target);}/*** ReflectAsm反射调用方法* 用方法和字段的索引定位反射方法,性能高*/public static void testMethodIndexAccess() {User target = new User();MethodAccess methodAccess = MethodAccess.get(User.class);int index = methodAccess.getIndex("update", int.class, String.class);methodAccess.invoke(target, index, 1, "jack");System.out.println(target);}
}输出:
methodName=toString
methodName=update
User{age=1, name='jack'}
User{age=1, name='jack'}

ReflectASM执行效率比对

public class ReflectASMDemo {public static void main(String[] args) throws Exception {ReflectASMDemo test = new ReflectASMDemo();test.testJdkReflect();test.testReflectAsmName();test.testReflectAsmIndex();}/*** JDK反射调用方法* @throws Exception*/public void testJdkReflect() throws Exception {User target = new User();long start = System.currentTimeMillis();Method method = target.getClass().getMethod("update", int.class, String.class);for (int i = 0; i < 100000000; i++) {method.invoke(target, 1, "jack");}long end = System.currentTimeMillis();System.out.println("timeout=" + (end - start));//418 450 426 430}/*** ReflectAsm反射调用方法* 用名称定位反射方法*/public void testReflectAsmName() {User target = new User();MethodAccess access = MethodAccess.get(User.class);//生成字节码的方式创建UserMethodAccesslong start = System.currentTimeMillis();for (int i = 0; i < 100000000; i++) {access.invoke(target, "update", 1, "jack");}long end = System.currentTimeMillis();System.out.println("timeout=" + (end - start));//88 64 66 50}/*** ReflectAsm反射调用方法* 用方法和字段的索引定位反射方法,性能高*/public void testReflectAsmIndex() {User target = new User();MethodAccess access = MethodAccess.get(User.class);int index = access.getIndex("update", int.class, String.class);long start = System.currentTimeMillis();for (int i = 0; i < 100000000; i++) {access.invoke(target, index, 1, "jack");}long end = System.currentTimeMillis();System.out.println("timeout=" + (end - start));//14 12 11 13}/*** ReflectAsm反射来set/get字段值*/public void testFieldAccess() {User target = new User();FieldAccess fieldAccess = FieldAccess.get(target.getClass());fieldAccess.set(target, "age", 1);int age = (Integer)fieldAccess.get(target, "age");System.out.println(age);}/*** ReflectAsm反射来调用构造方法*/public void testConstructorAccess() {ConstructorAccess<User> constructorAccess = ConstructorAccess.get(User.class);User userService = constructorAccess.newInstance();System.out.println(userService);}/*** 查找方法的索引*/public void testIndex() {User target = new User();MethodAccess methodAccess = MethodAccess.get(target.getClass());int index = methodAccess.getIndex("update", int.class, String.class);System.out.println(index);}
}class User {public int age;public String name;public void update(int age, String name) {}
}

结果发现,reflectASM效率明显高于Java反射获取,并且reflectASM提供通过方法名定位索引,然后通过索引调用方法,进一步提升方法调用效率。

通过reflectASM实现对象拷贝

public class BeanUtil {/***  大小写可以忽略* 下划线 _ 被忽略* NULL值和空字符串不会覆盖新值** @param source* @param target* @param <T>* @return*/public static <T> T copyPropertiesIgnoreCase(Object source, Object target) {Map<String, Field> sourceMap = CacheFieldMap.getFieldMap(source.getClass());CacheFieldMap.getFieldMap(target.getClass()).values().forEach((it) -> {Field field = sourceMap.get(it.getName().toLowerCase().replace("_", ""));if (field != null) {it.setAccessible(true);field.setAccessible(true);try {//忽略null和空字符串if(field.get(source)!=null)it.set(target, field.get(source));} catch (IllegalAccessException e) {e.printStackTrace();}}});System.out.println(target.toString());return (T) target;}private static class CacheFieldMap {private static Map<String, Map<String, Field>> cacheMap = new HashMap<>();private static Map<String, Field> getFieldMap(Class clazz) {Map<String, Field> result = cacheMap.get(clazz.getName());if (result == null) {synchronized (CacheFieldMap.class) {if (result == null) {Map<String, Field> fieldMap = new HashMap<>();for (Field field : clazz.getDeclaredFields()) {fieldMap.put(field.getName().toLowerCase().replace("_", ""), field);}cacheMap.put(clazz.getName(), fieldMap);result = cacheMap.get(clazz.getName());}}}return result;}}
}

MapToObjectIgnoreCaseUtil:

package 对象拷贝;import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;public class MapToObjectIgnoreCaseUtil {public static void main(String[] args) throws Exception {Map<Object, Object> map = new HashMap<>();map.put("fc_id", "TK");map.put("fc_merch_id", "123456");map.put("fc_merch_name", "中金");MerchInfo merchInfo = new MerchInfo();System.out.println(merchInfo);merchInfo = (MerchInfo) MapToObjectIgnoreCaseUtil.mapToObject(map, merchInfo.getClass());System.out.println(merchInfo);}public static Object mapToObject(Map<Object, Object> map, Class<?> beanClass) throws Exception {if (map == null)return null;Map<Object, Object> map2 = new HashMap<>();Set<Object> keySet = map.keySet();for(Object key : keySet){map2.put(key.toString().toLowerCase().replace("_", ""), map.get(key));}Object obj = beanClass.newInstance();Field[] fields = obj.getClass().getDeclaredFields();for (Field field : fields) {int mod = field.getModifiers();if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {continue;}field.setAccessible(true);if (map2.containsKey(field.getName().toLowerCase().replace("_", ""))) {field.set(obj, map2.get(field.getName().toLowerCase().replace("_", "")));}}return obj;}
}class MerchInfo{public String fcID;public String fcMerchId;public String fcMerchName;public String getFcID() {return fcID;}public void setFcID(String fcID) {this.fcID = fcID;}public String getFcMerchId() {return fcMerchId;}public void setFcMerchId(String fcMerchId) {this.fcMerchId = fcMerchId;}public String getFcMerchName() {return fcMerchName;}public void setFcMerchName(String fcMerchName) {this.fcMerchName = fcMerchName;}@Overridepublic String toString() {return "MerchInfo{" +"fcID='" + fcID + '\'' +", fcMerchId='" + fcMerchId + '\'' +", fcMerchName='" + fcMerchName + '\'' +'}';}
}

Java-反射性能优化和工具包ReflectASM相关推荐

  1. Java中性能优化的35种方法汇总

    原文地址:http://www.jb51.net/article/102831.htm 前言 对程序员们来说,代码优化是一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于 ...

  2. 44个Java代码性能优化总结

    转载自 44个Java代码性能优化总结 代码优化的最重要的作用应该是:避免未知的错误.在代码上线运行的过程中,往往会出现很多我们意想不到的错误,因为线上环境和开发环境是非常不同的,错误定位到最后往往是 ...

  3. 【Java】44个Java代码性能优化总结

    1.概述 转载:44个Java代码性能优化总结 代码优化的最重要的作用应该是:避免未知的错误.在代码上线运行的过程中,往往会出现很多我们意想不到的错误,因为线上环境和开发环境是非常不同的,错误定位到最 ...

  4. Java程序性能优化——设计优化

    原文出自:http://blog.csdn.net/anxpp/article/details/51914119,转载请注明出处,谢谢! 1.前言 OK,之前写了一篇文章:"23种设计模式介 ...

  5. java 代码性能优化_Java代码性能优化的几个小技巧

    Java代码性能优化的几个小技巧 时间:2017-08-07     来源:华清远见JAVA学院 代码优化是程序员必须懂得一门学问,所以不管是程序员还是准程序员,养成良好的代码优化习惯都是必须要养成的 ...

  6. java代码统计收藏量_干货收藏 | 35个Java 代码性能优化总结(上)

    原标题:干货收藏 | 35个Java 代码性能优化总结(上) 前言 代码优化,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这 ...

  7. [译]GC专家系列5-Java应用性能优化的原则

    原文链接:http://www.cubrid.org/blog/dev-platform/the-principles-of-java-application-performance-tuning/ ...

  8. java常见性能优化_十大最常见的Java性能问题

    java常见性能优化 Java性能是所有Java应用程序开发人员都关心的问题,因为快速使应用程序与使其正常运行同等重要. 史蒂文·海恩斯(Steven Haines)使用他在Java性能问题上的个人经 ...

  9. Java 程序性能优化《第一章》Java性能调优概述 1.4小结

    Java 程序性能优化<第一章>1.4小结 通过本章的学习,读者应该了解性能的基本概念及其常用的参考指标.此外,本章还较为详细的介绍了与性能调优相关的两个重要理论--木桶原理以及Amdah ...

最新文章

  1. javascript iterator
  2. 9path 导致的一场冤假错案
  3. UA OPTI512R 傅立叶光学导论15 2-D Fourier变换与Hankel变换
  4. 数据中心电池室管理之经济实用性方法的探索
  5. 以 Kubernetes 为代表的容器技术,已成为云计算的新界面
  6. xxx in ‘Anonymous class derived from xxx clashes with ‘call(T)‘ in xxx attempting to use incompati
  7. python 操作redis,存取为字节格式,避免转码加
  8. Win7 U盘安装Ubuntu16.04 双系统
  9. LXReorderableCollectionViewFlowLayout
  10. 将ubuntu的home迁移至第二块磁盘
  11. mysql数据库连接数瓶颈_MySQL数据库性能优化之硬件瓶颈分析
  12. 笔记本电脑配置知识大全
  13. 64qam带宽计算_64QAM有效传输速率计算
  14. 信号与系统matlab实验报告,信号与系统实验报告.doc
  15. Spring之IOC概念、Bean对象创建及DI注入的三种方式
  16. Delphi7 将Excel导入数据库
  17. Git上传代码报错Push rejected: Push to origin/master was rejected
  18. 开启子进程的两种方式,孤儿进程与僵尸进程,守护进程,互斥锁,IPC机制,生产者与消费者模型...
  19. android 一直开机画面,解决:Android模拟器一直停留在开机画面
  20. 微信小程序自动保留空格换行

热门文章

  1. 在html中写js打开是乱码,javascript脚本中文乱码如何解决?
  2. 计算机架构设计的8个伟大思想
  3. 51单片机矩阵键盘C程序
  4. 自学软件测试的面试经验(一)~
  5. 3D模型 在Android Studio 中的应用
  6. win10远程计算机或设备将不接受连接
  7. php 框架注解,laravel框架常用的注解
  8. 二元logistic模型案例_SPSS二项logistic回归分析案例实践,做个预测模型
  9. [css] 页面重构“鑫三无准则” 之“无宽度”准则
  10. 计算机技术类岗位英文简历,计算机科学与技术类专业英文简历模板.docx