Spring生态在Java项目中被广泛应用,从架构到技术应用再到常用的基本功能,Spring给我们的开发带来了很大的便利。今天翻到项目中导出报表功能的时候,发现经常复制对象的方法:

 BeanUtils.copyProperties;

把源对象的属性值赋值给目标对象,Spring和Apache和其他的一些框架都给我们提供了对象属性的拷贝方法:

org.springframework.beans.BeanUtils.copyProperties(Object source, Object target)

org.apache.commons.beanutils.BeanUtils.copyProperties(Object dest, Object orig)

使用的时候注意这两个方法的参数顺序,源和目标是相反的。这两个方法底层都用到了反射,心血来潮之下我自己造了一个对象拷贝的轮子,代码如下:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap; public class ObjConvertor {
private static HashMap<String,HashMap<String,Method>> mapSetMethods=new HashMap<String,HashMap<String,Method>>();
private static HashMap<String,HashMap<String,Method>> mapGetMethods=new HashMap<String,HashMap<String,Method>>(); static
{ } public static boolean Convert(Object src,Object dst) {
HashMap<String,Method> mapSrc=new HashMap<String,Method>();
HashMap<String,Method> mapDst=new HashMap<String,Method>();
if(mapGetMethods.containsKey(src.getClass().getTypeName()))
mapSrc=mapGetMethods.get(src.getClass().getTypeName());
else
{
Method srcMethods[]=src.getClass().getMethods();
mapGetMethods.put(src.getClass().getTypeName(),mapSrc);
for(Method method:srcMethods)
{
String name=method.getName();
if(name.startsWith("get"))
{
String prop=name.substring(3).toLowerCase();
mapSrc.put(prop, method);
}
}
}
if(mapSetMethods.containsKey(dst.getClass().getTypeName()))
mapDst=mapSetMethods.get(dst.getClass().getTypeName());
else
{
Method dstMethods[]=dst.getClass().getMethods();
mapSetMethods.put(dst.getClass().getTypeName(),mapDst);
for(Method method:dstMethods)
{
String name=method.getName();
if(name.startsWith("set"))
{
String prop=name.substring(3).toLowerCase();
mapDst.put(prop, method);
}
}
}
for(String prop:mapSrc.keySet())
{
if(mapDst.containsKey(prop)){
Method setMethod=mapDst.get(prop);
Method getMethod=mapSrc.get(prop);
try {
Object data=getMethod.invoke(src);
if(data==null)
continue;
if(data instanceof java.sql.Time)
{
if(setMethod.getParameterTypes()[0]==String.class)
data=data.toString();
}
else if(data instanceof java.sql.Date)
{
if(setMethod.getParameterTypes()[0]==String.class)
data=new SimpleDateFormat("yyyy-MM-dd").format(data);
}
else if(data instanceof java.util.Date)
{
if(setMethod.getParameterTypes()[0]==String.class)
data=new SimpleDateFormat("yyyy-MM-dd").format((java.util.Date)data);
}
else if(data instanceof java.sql.Timestamp)
{
if(setMethod.getParameterTypes()[0]==String.class)
data=data.toString();
}
if(setMethod.getParameterTypes()[0]==java.sql.Time.class)
{
if(data instanceof String)
data=java.sql.Time.valueOf((String)data);
}
else if(setMethod.getParameterTypes()[0]==java.sql.Date.class)
{
if(data instanceof String)
data=java.sql.Date.valueOf((String)data);
}
else if(setMethod.getParameterTypes()[0]==java.util.Date.class)
{
if(data instanceof String)
{
data=(new SimpleDateFormat("yyyy-MM-dd")).parse((String)data);
}
}
else if(setMethod.getParameterTypes()[0]==java.sql.Timestamp.class)
{
if(data instanceof String)
data=java.sql.Timestamp.valueOf((String)data);
}
if(data==null)
continue;
setMethod.invoke(dst, data);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
}
return true;
}
}

对象拷贝的功能至此完结,如果项目中需要一些特殊格式的数据,可以考虑自己造轮子,这个就看业务需求了。

接下来谈谈对象拷贝,如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行引用的传递,而没有真实的创建一个新的对象,则认为是浅拷贝,比如Spring和Apache的copyProperties和上面自己的拷贝方法。反过来,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝。

通过对比不难发现:深拷贝相当于创建了一个新的对象,只是这个对象的所有内容,都和被拷贝的对象一模一样,即两者的修改是隔离的,相互之间没有影响;而浅拷贝也是创建了一个对象,但是这个对象的某些内容(比如A)依然是被拷贝对象的,即通过这两个对象中任意一个修改A,两个对象的A都会受到影响。

Spring和Apache的copyProperties都属于浅拷贝,在日常开发中基本可以满足使用了。需要注意的是,在Java开发手册中提到,不建议用Apache的BeanUtils工具类,因为其中有很多的检验、类型转换、日志打印等,实现复杂度高,性能比较差。

那么如果要实现高性能且安全的深度拷贝呢?深拷贝就是被拷贝对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量,那些引用其他对象的变量将指向被拷贝过的新对象,而不再试原有的那些被引用的对象,就是说,深拷贝把要拷贝的对象所引用的对象都复制了一遍。我觉得最靠谱的深拷贝还是要用序列化后写流的方法,这种方法需要实现Serializable接口,多层拷贝时,引用类型都要实现Serializable接口。代码如下:

import java.io.*;
import java.util.Date; public class CopyDemo {
class User implements Serializable {
private int id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
private Person person; //引用类型 public User myColon(){
User copy=null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
//将流序列化成对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
copy = (User) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return copy;
} //省略get-set方法代码
} class Person implements Serializable {
private int id;
private String userName ;
private int age ;
private String mobilePhone ;
public Person(){}
public Person(int id,String userName, int age, String mobilePhone) {
this.id = id;
this.userName = userName;
this.age = age;
this.mobilePhone = mobilePhone;
}
//省略get-set方法
}
}

在Java语言里深拷贝一个对象,先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象。通过代码可以更明显的看到这个过程。把对象写到流里的过程是串行化(Serilization)过程,而把对象从流中读出来是并行化(Deserialization)过程。需要注意的是,需要拷贝的对象可能包含多层引用类型,多层拷贝不仅要将拷贝对象实现序列化接口,引用对象也同样的要实现序列化接口。

 

Java中把一个对象的值复制给另外一个对象引发的思考的更多相关文章

  1. JAVA中获取文件MD5值的四种方法

    JAVA中获取文件MD5值的四种方法其实都很类似,因为核心都是通过JAVA自带的MessageDigest类来实现.获取文件MD5值主要分为三个步骤,第一步获取文件的byte信息,第二步通过Messa ...

  2. Java中的阻塞和非阻塞IO包各自的优劣思考(经典)

    Java中的阻塞和非阻塞IO包各自的优劣思考 NIO 设计背后的基石:反应器模式,用于事件多路分离和分派的体系结构模式. 反应器(Reactor):用于事件多路分离和分派的体系结构模式 通常的,对一个 ...

  3. Java中传参的值传递和引用传递问题(转)

    今天遇到了一个java程序,需要用参数来返回值(虽然最后用另一种方法实现了),在网上看到这样一篇文章,很受启发. 本文章来自于http://hi.baidu.com/xzhilie/blog/item ...

  4. Java中初始变量默认值

    Java语言中有8种基本数据类型,基本情况汇总如下: 序号 数据类型 大小/位 封装类 默认值 可表示数据范围 1 byte(位) 8 Byte 0 -128~127 2 short(短整数) 16 ...

  5. JAVA中char和String/值类型和引用类型的区别

    import java.util.*; class test { public static void main(String[] args) { char a[] = {'b', 'a', 'c'} ...

  6. Java中Map根据键值(key)或者值(value)进行排序实现

    我们都知道,java中的Map结构是key->value键值对存储的,而且根据Map的特性,同一个Map中 不存在两个Key相同的元素,而value不存在这个限制.换句话说,在同一个Map中Ke ...

  7. Java中float/double取值范围与精度

    Java浮点数 浮点数结构 要说清楚Java浮点数的取值范围与其精度,必须先了解浮点数的表示方法,浮点数的结构组成,之所以会有这种所谓的结构,是因为机器只认识01,你想表示小数,你要机器认识小数点这个 ...

  8. Java中调用MatLab返回值

    当在Java中使用MatLab函数时,由于语言语法的不同,Matlab返回多个数据时,想在Java中获取到并进行使用.查阅了网上资料,翻箱倒柜加上自己实战,得出方法如下: 如MatLab函数返回的是N ...

  9. Java中不得不谈的值传递和地址传递

    个人的一些认识,希望能对初学Java的你,或者困惑于方法参数传递的你祈祷一丝帮助! 下面是一些作者的个人观点,如果有错,欢迎各位大牛指出错误,灰常感谢您的观看与支持... -------------- ...

  10. 关于Java中的HashMap的深浅拷贝的测试与几点思考

    0.前言 工作忙起来后,许久不看算法,竟然DFA敏感词算法都要看好一阵才能理解...真是和三阶魔方还原手法一样,田园将芜,非常可惜啊. 在DFA算法中,第一步是需要理解它的数据结构,在此基础上,涉及到 ...

随机推荐

  1. 【javaweb学习】XML和约束模式

    xml:可扩展标记语言,w3c组织发布的,用于保存有关系的数据,作为配置文件,描述程序模块之间的关系 <?xml version="1.0" encoding="g ...

  2. alert对ajax阻塞调查(IE, Chrome, FF)

    前阵子做保守工作,对一个js效果进行了改进,由于自己在chrome下测试没问题就丢给同事测试,同事用的是FF,发现不正常,后来又发现这个js在IE10下也不行,不得不调查,结果发现Chrome的ale ...

  3. core--进程

    前面我们说了线程是一系列的指令,那么进程是什么呢?进程就是装下这些指令的容器.该容器除了线程,还包含资源等内容.一个进程至少要有一个线程.没有线程的进程就不叫进程 "进程和程序的区别:进程必 ...

  4. ###《Machine Learning》by Andrew NG

    点击查看Evernote原文. #@author: gr #@date: 2014-10-17 #@email: forgerui@gmail.com Fundamental 一. 矩阵的迹.秩 矩阵 ...

  5. 翻书的效果:FMX.TSwipeTransitionEffect Animation

    This example shows how to use a TSwipeTransitionEffect transition and a TPathAnimation to simulate t ...

  6. 学以致用三十三-----django生命周期

    听了讲解django的视频后,受益匪浅,每个知识点老师都会总结整理.学会总结,存为己用. django生命周期弄明白后,对于整个django的框架会有一个清晰的了解.先上图 1.客户端发送请求,在dj ...

  7. eclipse c++11 cmake gnuradio

    承接之前的脚本.修改一下这个脚本的代码就可以让eclipse使用C++11了 #!/bin/sh echo "creat_debug for sdk" echo "mkd ...

  8. js五道经典练习题--第四道qq好友列表

    <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...

  9. 策略模式原理及Java代码实例

    一.策略模式的定义 —— 定义了一组算法,将每个算法包装起来,并且使它们之间可以互换 —— 策略模式使这些算法在客户端调用它们的时候能够相互不影响的变化,改变不同算法的实现方式不影响客户端的使用,即策 ...

  10. Linux及安全实践二

    Linux及安全实践二   基本内核模块 20135238 龚睿 1.  理解模块原理 linux模块是一些可以作为独立程序来编译的函数和数据类型的集合.之所以提供模块机制,是因为Linux本身是一个 ...