楼主是一名asp.net攻城狮,最近经常跑java组客串帮忙开发,所以最近对java的一些基础知识特别上心。却遇到需要将一个对象深拷贝出来做其他事情,而原对象保持原有状态的情况。(实在是不想自己new一个出来,然后对着一堆字段赋值......好吧,再此之前我没有关心是否项目框架有深拷贝的方法),然后就想着用反射实现吧....接下来

是我自己的原因,还是真的不存在这样的纯用反射实现的深拷贝方式....(c#是有纯反射实现的)

但也不能算自己白忙活吧,也找到了其他实现深拷贝的方式(但是每种方式我都觉得并不是太合理,也许是因为c#的方式带入了吧,最后贴出c#版本纯反射实现深拷贝的代码)

先说java的深拷贝方式0.0

java深拷贝方式一:实现Cloneable接口,重写clone方法

实体类:一个轮胎类,一个车辆类,车辆中包含轮胎

 /**轮胎类**/
public class Tire implements Cloneable {
public String color;
public int radius;
public Tire(){}
public Tire(String color, int radius) {
this.color = color;
this.radius = radius;
} @Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
/**车辆类**/
public class Car implements Cloneable{
public String name;
public String color;
public Tire tire;
public Car() {}
public Car(String name, String color, Tire tire) {
this.name = name;
this.color = color;
this.tire = tire;
}
public void whistle(){
System.out.println("汽车"+this.name+" 鸣笛...");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Tire getTire() {
return tire;
}
public void setTire(Tire tire) {
this.tire = tire;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

单元测试:

 @Test
public void test() throws CloneNotSupportedException {
Tire tire = new Tire("black",100);
Car car = new Car("奔驰","white",tire);
Car car_copy = (Car)car.clone();
System.out.println("car:"+car.hashCode()+" car.tire:"+car.tire.hashCode());
System.out.println("car_copy:"+car_copy.hashCode()+" car_copy.tire:"+car_copy.tire.hashCode());
car_copy.color = "blue";
System.out.println("car_copy:"+car_copy.color+" car:"+car.color);
}

输出结果:

car:1223737555 car.tire:906199566
car_copy:542081238 car_copy.tire:906199566
car_copy:blue car:white

从结果可以的之,car与car_copy的内存地址并不一致,但car.tire与car_copy.tire的内存地址却是一致的,说明“奔驰”车确实又造出了一辆,但却公用同一幅轮胎(这种情形....哈哈哈),好吧,也就是只复制了tire的引用,这可以说是深拷贝的不彻底 (hashCode()的值可以当作是内存地址来理解),那么要怎样才能彻底,真正的深拷贝?

修改Car类中的clone方法:

 @Override
protected Object clone() throws CloneNotSupportedException {
Car car = (Car)super.clone();
car.tire = (Tire)car.tire.clone();
return car;
}

输出结果:

car:1223737555 car.tire:906199566
car_copy:542081238 car_copy.tire:1133736492
car_copy:blue car:white

这样最终实现了,但这种方式用到项目中并不是很合适吧,每个需要深拷贝的类,都要实现Cloneable接口,并覆盖其clone方法,遇到引用其他类时候更是需要修改clone方法,要是引用其他类,其他类再引用其他类呢?这不好吧......

java深拷贝方式二:通过序列化与反序列化实现(实现Serializable接口)

实体类:与第一种方式类似,换成实现Serializable接口,去掉clone方法

/**轮胎类**/
@SuppressWarnings("serial")
public class Tire implements java.io.Serializable {
public String color;
public int radius;
public Tire(){}
public Tire(String color, int radius) {
this.color = color;
this.radius = radius;
}
}
/**车辆类**/
@SuppressWarnings("serial")
public class Car implements java.io.Serializable{
public String name;
public String color;
public Tire tire;
public Car() {}
public Car(String name, String color, Tire tire) {
this.name = name;
this.color = color;
this.tire = tire;
}
public void whistle(){
System.out.println("汽车"+this.name+" 鸣笛...");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Tire getTire() {
return tire;
}
public void setTire(Tire tire) {
this.tire = tire;
}
}

深拷贝方法:

 @SuppressWarnings("unchecked")
public static Object deepClone(Object obj)
{
Object copyObj = null;
ObjectOutputStream out = null;
ObjectInputStream in = null;
try {
// 序列化
ByteArrayOutputStream bufferOut = new ByteArrayOutputStream();
out = new ObjectOutputStream(bufferOut); out.writeObject(obj); // 反序列化
ByteArrayInputStream bufferIn = new ByteArrayInputStream(bufferOut.toByteArray());
in = new ObjectInputStream(bufferIn);
copyObj = in.readObject();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally{
try{
if(in != null){
in.close();
}
if(out!=null){
out.close();
}
}catch(IOException e){
throw new RuntimeException(e);
}
}
return copyObj;
}

单元测试:

 @Test
public void test() throws CloneNotSupportedException {
Tire tire = new Tire("black",100);
Car car = new Car("奔驰","white",tire);
Car car_copy = (Car)deepClone(car);
System.out.println("car:"+car.hashCode()+" car.tire:"+car.tire.hashCode());
System.out.println("car_copy:"+car_copy.hashCode()+" car_copy.tire:"+car_copy.tire.hashCode());
car_copy.color = "blue";
System.out.println("car_copy:"+car_copy.color+" car:"+car.color);
}

输出结果:

car:2019524978 car.tire:855703640
car_copy:1407965019 car_copy.tire:545768040
car_copy:blue car:white

从结果集中可以看出是深拷贝是正确的,但是每个类还是需要实现Serializable,好像也不合适吧......

优化一下深拷贝方法:将其换成泛型,这样拷贝出来就不需要强转了(好吧,其实也没比上面的方法好到哪去...)

 @SuppressWarnings("unchecked")
public static <T> T deepClone(T obj)
{
T copyObj = null;
ObjectOutputStream out = null;
ObjectInputStream in = null;
try {
// 序列化
ByteArrayOutputStream bufferOut = new ByteArrayOutputStream();
out = new ObjectOutputStream(bufferOut); out.writeObject(obj); // 反序列化
ByteArrayInputStream bufferIn = new ByteArrayInputStream(bufferOut.toByteArray());
in = new ObjectInputStream(bufferIn);
copyObj = (T)in.readObject();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally{
try{
if(in != null){
in.close();
}
if(out!=null){
out.close();
}
}catch(IOException e){
throw new RuntimeException(e);
}
}
return copyObj;
}

通过序列化与反序列化深拷贝还有更简单的实现方式,就是需要导个包(拷贝的类也必须实现Serializable接口),当然,我已经为你们准备好了 点击->org.apache.commons.lang

深拷贝方法:就一行代码...

 public Object deepClone(Object obj){
return org.apache.commons.lang.SerializationUtils.clone((Serializable)obj);
}

好了,java的暂时就到这里了,当然对于这两种方式并不是很满意...

-------------------------------------------------

C#深拷贝 反射实现

下面方法是c#的深拷贝,纯反射实现,无需实现任何接口,哦对,需要实体类有个无参的构造方法,简单使用强大,微软大法好啊......有需要用到的同学就拿去用吧,目前经过一个几百W的项目框架中考验,真的强大实用

 /// <summary>
/// 对象拷贝
/// </summary>
/// <param name="obj">被复制对象</param>
/// <returns>新对象</returns>
private object CopyOjbect(object obj) {
if (obj == null) {
return null;
}
Object targetDeepCopyObj;
Type targetType = obj.GetType();
//值类型
if (targetType.IsValueType == true) {
targetDeepCopyObj = obj;
}
//引用类型
else {
targetDeepCopyObj = System.Activator.CreateInstance(targetType); //创建引用对象
System.Reflection.MemberInfo[] memberCollection = obj.GetType().GetMembers(); foreach (System.Reflection.MemberInfo member in memberCollection) {
//拷贝字段
if (member.MemberType == System.Reflection.MemberTypes.Field)
{
System.Reflection.FieldInfo field = (System.Reflection.FieldInfo)member;
Object fieldValue = field.GetValue(obj);
if (fieldValue is ICloneable)
{
field.SetValue(targetDeepCopyObj, (fieldValue as ICloneable).Clone());
}
else
{
field.SetValue(targetDeepCopyObj, CopyOjbect(fieldValue));
} }//拷贝属性
else if (member.MemberType == System.Reflection.MemberTypes.Property) {
System.Reflection.PropertyInfo myProperty = (System.Reflection.PropertyInfo)member; MethodInfo info = myProperty.GetSetMethod(false);
if (info != null) {
try {
object propertyValue = myProperty.GetValue(obj, null);
if (propertyValue is ICloneable) {
myProperty.SetValue(targetDeepCopyObj, (propertyValue as ICloneable).Clone(), null);
}
else {
myProperty.SetValue(targetDeepCopyObj, CopyOjbect(propertyValue), null);
}
}
catch (System.Exception ex) { }
}
}
}
}
return targetDeepCopyObj;
}

一种c#深拷贝方式完胜java深拷贝(实现上的对比)的更多相关文章

  1. java 下载文件的两种方式和java文件的上传

    一:以网络的方式下载文件 try { // path是指欲下载的文件的路径. File file = new File(path); // 以流的形式下载文件. InputStream fis = n ...

  2. 一步步分析Java深拷贝的两种方式-clone和序列化

    今天遇到一道面试题,询问深拷贝的两种方法.主要就是clone方法和序列化方法.今天就来分析一下这两种方式如何实现深拷贝.如果想跳过解析的朋友,直奔"重点来了!"寻找答案. clon ...

  3. Java 深拷贝和浅拷贝 利用序列化实现深拷贝

    Java 深拷贝和浅拷贝 转自:http://www.cnblogs.com/mengdd/archive/2013/02/20/2917971.html 深拷贝(deep clone)与浅拷贝(sh ...

  4. Java深拷贝与序列化

    对基本类型的变量进行拷贝非常简单,直接赋值给另外一个对象即可: int b = 50; int a = b; // 基本类型赋值 对于引用类型的变量(例如 String),情况稍微复杂一些,因为直接等 ...

  5. java 深拷贝与浅拷贝机制详解

    概要: 在Java中,拷贝分为深拷贝和浅拷贝两种.java在公共超类Object中实现了一种叫做clone的方法,这种方法clone出来的新对象为浅拷贝,而通过自己定义的clone方法为深拷贝. (一 ...

  6. java Thread编程(三) 同步的两种不同实现方式

    1,创建需要同步的对象(方式一) package concurrency; public class Bank { private double amount; public Bank(double ...

  7. 【转】Java 5种字符串拼接方式性能比较。

    最近写一个东东,可能会考虑到字符串拼接,想了几种方法,但对性能未知,于是用Junit写了个单元测试. 代码如下: import java.util.ArrayList; import java.uti ...

  8. Java基础知识强化之IO流笔记35:InputStreamReader(Reader字符流的子类)2种read数据方式

    1. InputStreamReader(Reader字符流的子类)2种read数据方式: InputStreamReader的read方法: int read():一次读取一个字符 int read ...

  9. Java基础知识强化之IO流笔记34:OutputStreamWriter(Writer字符流的子类)5种write数据方式

    1. OutputStreamWriter (转换流) OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节. 同时OutputS ...

随机推荐

  1. java对身份证验证及正则表达式解析

    原文地址:http://www.cnblogs.com/zhongshengzhen/ java对身份证验证及正则表达式解析 package service; import java.text.Par ...

  2. 【Android - MD】之Snackbar的使用

    Snackbar 是 Android 5.0 新特性--Material Design 中的一个控件,用来代替 Toast ,Snackbar与Toast的主要区别是:Snackbar可以滑动退出,也 ...

  3. 在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke。

    本文转载:http://blog.csdn.net/playing9c/article/details/7471918 http://blog.csdn.net/beelinkerlidejun/ar ...

  4. 利用Chrome模拟访问移动端网页

    很多网站都通过User-Agent来判断浏览器类型,如果是3G手机,显示手机页面内容,如果是普通浏览器,显示普通网页内容. 谷歌Chrome浏览器,可以很方便地用来当3G手机模拟器.在Windows的 ...

  5. 关于python文件操作 (转载)

    总是记不住API.昨晚写的时候用到了这些,但是没记住,于是就索性整理一下吧: python中对文件.文件夹(文件操作函数)的操作需要涉及到os模块和shutil模块. 得到当前工作目录,即当前Pyth ...

  6. AVAudioRecorder、AVAudioPlayer录音及播放

    #pragma mark - 设置录制的音频文件的位置 - (NSString *)audioRecordingPath{ NSString *str_date=[TimeTransform Date ...

  7. 【10】令operator=返回一个reference to *this

    1.令operator= 返回一个reference to *this,为什么? 这只是一个协议,并无强制性.但是,为了与基本类型的行为保持一致性,强烈建议这么做.设计class 有一个宝典:一旦有疑 ...

  8. Android ListView快速定位(四)

    方法四: 添加一个EditText,作为搜索框 + Filter 其实这个不算第四个方法,因为与第二个一样,主要是实现Filter. 但是对于EditText的监听,我以前也没有写过,所以也记录一下. ...

  9. android131 360 02 设置中心

    // 判断是否需要自动更新 boolean autoUpdate = mPref.getBoolean("auto_update", true); if (autoUpdate) ...

  10. 双tomcat的部署

    由于开发环境使用的tomcat需频繁开启关闭,所以决定另外搭建一个tomcat部署后台供前台调用接口,顺便记录一下备忘 我的部署环境为windows7 tomcat7 将下载的tomcat放到其他位置 ...