有很多场景需要运行时创建对象,比如Copy对象到指定类型的对象中,比如根据指定的字段和值创建指定类型的对像。使用JDK自带的反射(java.lang.reflect)或者自省(java.beans.Introspector)都可以可实现。自省的方式会对类型信息做缓存因而效率更加高,推荐优先使用。

  1. copy对象

两个步骤完成,一是动态创建指定类型的对象,二是获取据源对象属性的值并赋到新对象对应的属性上。

      public static void doCopyNotNull(Class<?> clazz, Object source, Object target) {
List<Field> fieldArray = getAllFields(clazz);
if (fieldArray != null && fieldArray.size() > 0) {
for (Field field : fieldArray) {
try {
// log.info("set field name=[{}]",field.getName());
if (Modifier.isFinal(field.getModifiers())) {
// log.info("Modifier is final");
continue;
}
field.setAccessible(true);
Object value = field.get(source);
// log.info("the value is [{}]",value); if (value != null) {
// log.info("field name=[{}] do set value [{}]",field.getName(),value);
field.set(target, value);
} } catch (IllegalArgumentException e) {
log.error("局部更新失败", e);
} catch (IllegalAccessException e) {
log.error("局部更新失败", e);
}
}
}
} public static List<Field> getAllFields(Class clazz) {
List<Field> resultFieldList = Lists.newArrayList();
while (clazz != null && clazz != Object.class) {
Field[] fields = clazz.getDeclaredFields();
resultFieldList.addAll(Arrays.asList(fields));
clazz = clazz.getSuperclass();
}
return resultFieldList;
}
  1. 使用自省创建动态创建对象。

使用场景:创建指定类型的对象并根据key为字段名称value为对应值的java.util.Map初始化对象,对象完成返回给调用方。

特别要强调的是指定的java类型需要符合java bean命名规则。

2.1. 获取指定Class的key为filed的名称,value为PropertyDescriptor的map

 /**
* 返回指定key为field名称,value为PropertyDescriptor的map
*
* @param clazz
* @return
*/
public static Map<String, PropertyDescriptor> getPropertyDescriptorMap(Class clazz) {
Map<String, PropertyDescriptor> map = Maps.newHashMap();
try {
BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); if (propertyDescriptors != null && propertyDescriptors.length > 0) {
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
map.put(propertyDescriptor.getName(), propertyDescriptor);
}
}
} catch (IntrospectionException e) {
e.printStackTrace();
}
return map;
}

2.2 . 创建对象并初始化对象

 /**
* 创建指定类型的对象并根据传入的字段和对应的值给对象赋值。
*
* @param fieldValueMap
* @param clazz
* @param <T>
* @return
*/
public static <T> T createBean(Map<String, Object> fieldValueMap, Class<T> clazz) {
if (fieldValueMap == null | fieldValueMap.isEmpty()) {
return null;
} try {
//创建对象
T obj = clazz.newInstance();
//给对象赋值
Map<String, PropertyDescriptor> propertyDescriptorMap = getPropertyDescriptorMap(clazz);
fieldValueMap.forEach((k, v) -> {
if (v == null) {
return;
} PropertyDescriptor propertyDescriptor = propertyDescriptorMap.get(k);
if (propertyDescriptor==null){
log.warn("类[{}]中没有字段[{}],对应的值[{}]将无法设置.请检查是否有问题.",clazz,k,v);
return;
}
try {
if (propertyDescriptor.getPropertyType() == BigDecimal.class && v.getClass() != BigDecimal.class) {
v = new BigDecimal(v.toString());
}
propertyDescriptor.getWriteMethod().invoke(obj, v);
} catch (IllegalAccessException | InvocationTargetException e) {
new ServiceException("创建对象失败,异常如下:", e);
}catch(Throwable e){
new ServiceException("创建对象失败,异常如下:", e);
} });
return obj;
} catch (InstantiationException | IllegalAccessException e) {
new ServiceException("创建对象失败", e);
}
return null;
}
  • fieldValueMap:以property名称为key值为value的map。
  • clazz:创建对象的类型。

推荐使用自省的方式创建对象,因为自省会缓存类型信息,从而不用每次调用重新获取类型信息,因而可以提高在多次使用时的性能。

查看源码如下:

 public static BeanInfo getBeanInfo(Class<?> beanClass)
throws IntrospectionException
{
if (!ReflectUtil.isPackageAccessible(beanClass)) {
return (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo();
}
ThreadGroupContext context = ThreadGroupContext.getContext();
BeanInfo beanInfo;
synchronized (declaredMethodCache) {
beanInfo = context.getBeanInfo(beanClass);
}
if (beanInfo == null) {
beanInfo = new Introspector(beanClass, null, USE_ALL_BEANINFO).getBeanInfo();
synchronized (declaredMethodCache) {
context.putBeanInfo(beanClass, beanInfo);
}
}
return beanInfo;
}

注意: synchronized (declaredMethodCache) {
beanInfo = context.getBeanInfo(beanClass);
}

首先从缓存中获取bean信息,如果不存在才会重新获取,因此创建多个类型相同的对象时可以大大提高性能 。

java运行时创建对象的更多相关文章

  1. java运行时内存模式学习

    学习java运行时内存模式: 各区介绍: 方法区(线程共享):用于存放被虚拟机加载的类的元数据:静态变量,常量,以及编译和的代码(字节码),也称为永久代(所有该类的实例被回收,或者此类classLoa ...

  2. Jvm基础(1)-Java运行时数据区

    最近在看<深入理解Java虚拟机>,里面讲到了Java运行时数据区,这是Jvm基本知识,把读书笔记记录在此.这些知识属于常识,都能查到的,如果我有理解不对的地方,还请指出. 首先把图贴上来 ...

  3. Java运行时内存

    对于java程序员来说,并不必显示地对内存进行管理,一切都交给java虚拟机去做吧,而且,你也不一定做得比java虚拟机来得专业.好像所有内存管理都交给虚拟机去做就万事大吉了,但是,事实有时并非如此, ...

  4. 读书笔记-浅析Java运行时数据区

    作为一个 Java 为主语言的程序员,我偶尔也需要 用 C/C++ 写程序,在使用时让我很烦恼的一件事情就是需要对 new 出来的对象进行 delete/free 操作,我老是担心忘了这件事情,从而导 ...

  5. Java运行时环境---内存划分

    背景:听说Java运行时环境的内存划分是挺进BAT的必经之路. 内存划分: Java程序内存的划分是交由JVM执行的,而不像C语言那样需要程序员自己买单(C语言需要程序员为每一个new操作去配对del ...

  6. JVM发展史和Java运行时内存区域

    目前三大主流JVM: Sun HotSpot:Sun于1997年收购Longview Technologies公司所得.Sun于2009年被Oracle收购. BEA JRockit:BEA于2002 ...

  7. JAVA运行时异常及常见的5中RuntimeExecption

    最近在抽时间看面试题,很多面试题都提出了写出java常见的5个运行时异常.现在来总结一下, java运行时异常是可能在java虚拟机正常工作时抛出的异常. java提供了两种异常机制.一种是运行时异常 ...

  8. Java 内存管理、JVM 工作原理与 Java 运行时系统

    Java 虚拟机规范中说明:所有的对象实例(all class instances)以及数组都要在堆上分配: the heap is the runtime data area from which ...

  9. Java常见的异常,Java运行时异常和一般异常的区别

    Java常见的异常,Java运行时异常和一般异常的区别 异常和错误二者的不同之处: Exception: 1.可以是可被控制(checked,检查异常) 或不可控制的(unchecked,非检查异常) ...

随机推荐

  1. Sysenter/Kifastcallentry hook 检测与恢复

    关于Sysenter.Kifastcallentry.中断之类的内核入口hook技术早就烂大街了,可是对hook的检测与恢复代码却是寥寥无几,一切抛开代码将原理的行为都是耍流氓. 下面以Sysente ...

  2. 扬我国威,来自清华的开源项目火爆Github

    前几天TJ君跟大家分享了几个有趣的Github项目(加密解密.食谱.新冠序列,各种有趣的开源项目Github上都有),其中呢,有不少是来自斯坦福大学的项目,当时TJ君就不由得想,什么时候能看到的项目都 ...

  3. 韩顺平Java(持续更新中)

    原创上课笔记,转载请注明出处 第一章 面向对象编程(中级部分) PDF为主 1.1 IDEA 删除当前行,ctrl+y 复制当前行,ctrl+d 补全代码,alt+/ 添加或者取消注释,ctrl+/ ...

  4. SQL 注入基础

    SQL注入 SQL注入是服务器端未严格校验客户端发送的数据,而导致服务端SQL语句被恶意修改并成功执行的行为. 本质:把用户输入的数据当作代码执行.任何和数据库产生交互的地方便有可能存在注入. SQL ...

  5. CF1177A Digits Sequence (Easy Edition) 题解

    Content 一个序列由从 \(1\) 开始的数字不断在末端拼接,就像这样:\(12345678910111213141516...\).现在,给定一个数字 \(k\),请输出这个序列的第 \(k\ ...

  6. java 多线程:线程通信-等待通知机制wait和notify方法;(同步代码块synchronized和while循环相互嵌套的差异);管道通信:PipedInputStream;PipedOutputStream;PipedWriter; PipedReader

    1.等待通知机制: 等待通知机制的原理和厨师与服务员的关系很相似: 1,厨师做完一道菜的时间不确定,所以厨师将菜品放到"菜品传递台"上的时间不确定 2,服务员什么时候可以取到菜,必 ...

  7. 【LeetCode】1166. Design File System 解题报告 (C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 字典 目录树 日期 题目地址https://leetc ...

  8. 【LeetCode】695. Max Area of Island 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 方法一:DFS 方法二:BFS 日期 题目地址:ht ...

  9. RabbitMQ,RocketMQ,Kafka 事务性,消息丢失和消息重复发送的处理策略

    消息队列常见问题处理 分布式事务 什么是分布式事务 常见的分布式事务解决方案 基于 MQ 实现的分布式事务 本地消息表-最终一致性 MQ事务-最终一致性 RocketMQ中如何处理事务 Kafka中如 ...

  10. Second Order Optimization for Adversarial Robustness and Interpretability

    目录 概 主要内容 (4)式的求解 超参数 Tsiligkaridis T., Roberts J. Second Order Optimization for Adversarial Robustn ...