Java中把一个对象的值复制给另外一个对象引发的思考
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中把一个对象的值复制给另外一个对象引发的思考的更多相关文章
- JAVA中获取文件MD5值的四种方法
JAVA中获取文件MD5值的四种方法其实都很类似,因为核心都是通过JAVA自带的MessageDigest类来实现.获取文件MD5值主要分为三个步骤,第一步获取文件的byte信息,第二步通过Messa ...
- Java中的阻塞和非阻塞IO包各自的优劣思考(经典)
Java中的阻塞和非阻塞IO包各自的优劣思考 NIO 设计背后的基石:反应器模式,用于事件多路分离和分派的体系结构模式. 反应器(Reactor):用于事件多路分离和分派的体系结构模式 通常的,对一个 ...
- Java中传参的值传递和引用传递问题(转)
今天遇到了一个java程序,需要用参数来返回值(虽然最后用另一种方法实现了),在网上看到这样一篇文章,很受启发. 本文章来自于http://hi.baidu.com/xzhilie/blog/item ...
- Java中初始变量默认值
Java语言中有8种基本数据类型,基本情况汇总如下: 序号 数据类型 大小/位 封装类 默认值 可表示数据范围 1 byte(位) 8 Byte 0 -128~127 2 short(短整数) 16 ...
- java中为什么只存在值传递(以传入自定义引用类型为例)
java中只有值传递 为什么这么说?两个例子: public class Student { int sage = 20; String sname = "云胡不归"; publi ...
- JAVA中char和String/值类型和引用类型的区别
import java.util.*; class test { public static void main(String[] args) { char a[] = {'b', 'a', 'c'} ...
- Java中Map根据键值(key)或者值(value)进行排序实现
我们都知道,java中的Map结构是key->value键值对存储的,而且根据Map的特性,同一个Map中 不存在两个Key相同的元素,而value不存在这个限制.换句话说,在同一个Map中Ke ...
- Java中float/double取值范围与精度
Java浮点数 浮点数结构 要说清楚Java浮点数的取值范围与其精度,必须先了解浮点数的表示方法,浮点数的结构组成,之所以会有这种所谓的结构,是因为机器只认识01,你想表示小数,你要机器认识小数点这个 ...
- Java中调用MatLab返回值
当在Java中使用MatLab函数时,由于语言语法的不同,Matlab返回多个数据时,想在Java中获取到并进行使用.查阅了网上资料,翻箱倒柜加上自己实战,得出方法如下: 如MatLab函数返回的是N ...
随机推荐
- MySQL数据库---配置文件及数据文件
1.主配置文件 #/usr/local/mysql/bin/mysqld --verbose --help |grep -A 1 'Default options' #cat /etc/my.cnf ...
- 26.Apache
1.Apache简介 Apache HTTP Server(简称Apache)是Apache软件基金会的一个开放源代码的网页服务器软件,可以在大多数电脑操作系统中运行,由于其跨平台和安全性(尽管不断有 ...
- 弱网测试之Fidder
是用Fidder可以模拟若罔测试. 1.Fiider设置 fiddler中选中Rules->Cutomize Rules,在文件中搜索关键字:m_SimulateModem: 修改m_Simul ...
- 搜索Ex (洛谷提高历练地)
搜索Ex P1120 小木棍 题意:不超过65根长度小于50的小木棍(是由一批等长木棍砍断得到的),求原始木棍的最小可能长度 分析:优化+减枝爆搜 搜索状态要记录当前尝试的已经填好的长度,当前尝试填的 ...
- LaTeX 环境安装&编译器配置
推荐网站:https://www.latexstudio.net/articles/ (基本所有的latex疑惑都可以在这里得到解决) 入门视频:两小时短小精悍,让你轻松入门,https://www. ...
- 【bzoj2429】[HAOI2006]聪明的猴子(图论--最小瓶颈生成树 模版题)
题意:有M只猴子,他们的最大跳跃距离为Ai.树林中有N棵树露出了水面,给出了它们的坐标.问有多少只猴子能在这个地区露出水面的所有树冠上觅食. 解法:由于要尽量多的猴子能到达所有树冠,便用Kruskal ...
- The 10th Shandong Provincial Collegiate Programming Contest(11/13)
$$The\ 10th\ Shandong\ Provincial\ Collegiate\ Programming\ Contest$$ \(A.Calandar\) 签到 //#pragma co ...
- 【uva 1610】Party Games(算法效率--构造 dfs)
题意:有一个N个字符串(N≤1000,N为偶数)的集合,要求找一个长度最短的字符串(可不在集合内)S,使得集合中恰好一半的串小于等于S,另一半大于S.如果有多解,要求输出字典序最小的解. 解法:本来我 ...
- Smith Numbers POJ - 1142 暴力递归枚举
题意: 给你一个数x,把这个分解成素数之积(假设是x1*x2*x3),如果 x的每一数位的和 等于 x1每一数位的和加上x2每一数位的和加上x3每一数位的和,那么他就是题目要找的数 示例: ...
- PowerShell随笔10---Module
我们总不能把所有脚本文件零散的放着,就像创建一个项目一样,我们创建一个Module. 创建好之后如何让系统认识我们创建的Module呢,我们需要放在指定的位置. 那到底应该放在什么位置呢? 我们可以通 ...