在使用缓存读取数据后修改发现缓存被修改。于是找了下复制对象的方法。

关于对象克隆


按我的理解,对象是包含引用+数据。通常变量复制都是将引用传递过去。比如:

 Person p1 = new Person();
Person p2 = p1;

这两句话,创建两个引用p1,p2,但指向共同的内存大堆数据。修改任何一个,另一个的数据也将修改。

直接引用传递测试用例:

1.实体类:

 package com.test.java;

 import java.io.Serializable;

 /**
* Created by Administrator on 2015/9/16.
*/
public class Person implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
int id;
String name;
int age;
Country Country; @Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", Country=" + Country +
", hashcode=" + hashCode() +
'}';
} public Person() {
} public Person(Country country, int id, String name) {
Country = country;
this.id = id;
this.name = name;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public com.test.java.Country getCountry() {
return Country;
} public void setCountry(com.test.java.Country Country) {
this.Country = Country;
} public Person(String name,int age){ this.name=name;
this.age=age;
}
} class Country implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
int code;
String name; public Country() {
} public Country(int code) {
this.code = code;
} public Country(int code, String name) {
this.code = code;
this.name = name;
} public int getCode() {
return code;
} public void setCode(int code) {
this.code = code;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "Country{" +
"code=" + code +
", name='" + name + '\'' +
", hashcode='" + hashCode() + '\'' +
'}';
}
}

2.测试类

 package com.test.java;

 /**
* Created by Administrator on 2015/11/26.
* 测试对象引用
*/
public class TestRef { public static void main(String[] args) {
Country country = new Country(1,"china");
Person person = new Person(country,1,"test"); Country country1 = country;
Person person1 = person; System.out.println("创建国家 :"+country);
System.out.println("引用传递国家 :"+country1);
System.out.println("创建人 :"+person);
System.out.println("引用传递创建人:"+person1); } }

3.打印结果:

4.分析:

通过hashcode可以证明,数据实体的地址是相同的。关于基本类型和引用类型的内存关系,可以参考这篇

同样,通过实现clone接口,重载clone方法,然后调用person.clone()来复制对象的浅克隆是一样。参考这篇

当然,采用深度克隆的话就可以生成两个完全不同的对象。

然而,我们创建的实体通常是不会实现和覆盖clone的,这种办法只能提前写好对应的类才可以实现。因此,不推荐使用。

那么,我们可以通过反射或者序列化来实现。


关于序列化


参考博客Java序列化是指把Java对象转换为字节序列的过程;而Java反序列化是指把字节序列恢复为Java对象的过程。字节码可以存储,无状态,而对象在内存中开辟空间,有地址。

由此,可以把对象序列化后反序列化。相当于破碎重组。

前提是:实体类需要实现序列化接口

1.序列化实现对象复制

 // 用序列化与反序列化实现深克隆
public static Object cloneBySer(Object baseObj) {
Object o = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(baseObj);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos
.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
o = ois.readObject();
ois.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return o;
}

2.测试用例:

  public static void main(String[] args) {
Country country = new Country(1,"china");
Person person = new Person(country,1,"test");
//引用传递
Country country1 = country;
Person person1 = person;
//序列化和反序列化
Object person2 = ObjectCopy.cloneBySer(person);
Object country2 = ObjectCopy.cloneBySer(country); // person.setAge(12); System.out.println("创建国家 :"+country);
System.out.println("引用传递国家 :"+country1);
System.out.println("序列化复制国家 :"+country2);
System.out.println("创建人 :"+person);
System.out.println("引用传递人:"+person1);
System.out.println("序列化复制人:"+person2); }

3.控制台打印:

4.分析

序列化完全实现了对象拷贝。要求:对象都实现序列化,对象hashcode和equals方法默认或者包含全部信息。


通过反射


反射可以复制一个对象的属性,从而实现对象拷贝

反射代码:

 /**
* COPY对象(毛病还是很多的。。)
*对基本类型的过滤
* @author Lv9
* @since 2010.03.09
* baseObject 要拷贝的对象
* noCopyClassNames 不深度拷贝的对象属性
*/
public static Object coloneByRef(Object baseObject,
String... noCopyClassNames) throws Exception {
Object copyObject = baseObject.getClass().newInstance();
Field[] fields = baseObject.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if (checkClassType(field.getType().getName(), noCopyClassNames)) {
field.set(copyObject, field.get(baseObject));
} else {
field.set(copyObject, coloneByRef(field.get(baseObject),
noCopyClassNames));
}
}
return copyObject;
} public static boolean checkClassType(String className,
String[] noCopyClassNames) {
for (String noCopyClassName : noCopyClassNames) {
if (className.equals(noCopyClassName)) {
return true;
}
}
return false;
}

一个失败的用例:

反射用的不太会

 package com.test.reflect;

 import java.io.*;
import java.lang.reflect.Field; /**
* Created by Administrator on 2015/11/25.
* 对象复制
*/
public class ObjectCopy { public static void main(String[] args) throws Exception {
A baseObject = new A(new B(new C("bString1", "bString2"), 1, 2), new C(
"cString1", "cString2"));
A copyObject = (A) coloneByRef(baseObject, "java.lang.Integer",
"java.lang.String"); System.out.println(baseObject);
System.out.println(copyObject);
System.out.println("===================分割线===================");
System.out.println("原对象修改前:"+baseObject);
A a = (A)cloneBySer(baseObject);
System.out.println("复制对象 :"+a);
a.setC(new C("cchange1","cchange2"));
System.out.println("复制后修改对象:"+a);
System.out.println("原对象修改后 :"+baseObject); } /**
* COPY对象(毛病还是很多的。。)
*对基本类型的过滤
* @author Lv9
* @since 2010.03.09
* baseObject 要拷贝的对象
* noCopyClassNames 不深度拷贝的对象属性
*/
public static Object coloneByRef(Object baseObject,
String... noCopyClassNames) throws Exception {
Object copyObject = baseObject.getClass().newInstance();
Field[] fields = baseObject.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if (checkClassType(field.getType().getName(), noCopyClassNames)) {
field.set(copyObject, field.get(baseObject));
} else {
field.set(copyObject, coloneByRef(field.get(baseObject),
noCopyClassNames));
}
}
return copyObject;
} public static boolean checkClassType(String className,
String[] noCopyClassNames) {
for (String noCopyClassName : noCopyClassNames) {
if (className.equals(noCopyClassName)) {
return true;
}
}
return false;
} // 用序列化与反序列化实现深克隆
public static Object cloneBySer(Object baseObj) {
Object o = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(baseObj);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos
.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
o = ois.readObject();
ois.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return o;
}
} class A implements Serializable{
/**
*
*/
private B b;
private C c; public A() { } public A(B b, C c) {
this.b = b;
this.c = c;
} public B getB() {
return b;
} public void setB(B b) {
this.b = b;
} public C getC() {
return c;
} public void setC(C c) {
this.c = c;
} @Override
public String toString() {
return "A{" +
" b=" + b +
", c=" + c +
'}';
}
} class B implements Serializable{
/**
*
*/
private C c;
private Integer int1;
private Integer int2; public B() { } public B(C c, Integer int1, Integer int2) {
this.c = c;
this.int1 = int1;
this.int2 = int2;
} public C getC() {
return c;
} public void setC(C c) {
this.c = c;
} public Integer getInt1() {
return int1;
} public void setInt1(Integer int1) {
this.int1 = int1;
} public Integer getInt2() {
return int2;
} public void setInt2(Integer int2) {
this.int2 = int2;
} @Override
public String toString() {
return "[B: int1 = " + int1 + ",int2 = " + int2 + ",c = " + c
+ ",hashCode = " + hashCode() + "]";
}
} class C implements Serializable{
/**
*
*/
private String string1;
private String string2; public C() { } public C(String string1, String string2) {
this.string1 = string1;
this.string2 = string2;
} public String getString1() {
return string1;
} public void setString1(String string1) {
this.string1 = string1;
} public String getString2() {
return string2;
} public void setString2(String string2) {
this.string2 = string2;
} @Override
public String toString() {
return "[C: string1 = " + string1 + ",string2 = " + string2
+ ",hashCode = " + hashCode() + "]";
}
}

java中复制对象通过反射或序列化的更多相关文章

  1. 【译】Java中的对象序列化

    前言 好久没翻译simple java了,睡前来一篇. 译文链接: http://www.programcreek.com/2014/01/java-serialization/ 什么是对象序列化 在 ...

  2. JAVA中JavaBean对象之间属性拷贝的方法

    JAVA中JavaBean对象之间的拷贝通常是用get/set方法,但如果你有两个属性相同的JavaBean或有大部分属性相同的JavaBean,对于这种情况,可以采用以下几个简便方法处理. 下面对这 ...

  3. 【学习笔记】Java中生成对象的5中方法

    概述:本文介绍以下java五种创建对象的方式: 1.用new语句创建对象,这是最常用的创建对象的方式. 2.使用Class类的newInstance方法 3.运用反射手段,调用java.lang.re ...

  4. JAVA中JavaBean对象之间拷贝的方法

    JAVA中JavaBean对象之间的拷贝通常是用get/set方法,但如果你有两个属性相同的JavaBean或有大部分属性相同的JavaBean,有个更简便的方法,他们之间的拷贝可以通过copyPro ...

  5. Java I/O - 对象的输入输出与序列化

    先说概念: 一.相关概念 序列化是Java提供的一种将对象写入到输出流.并在之后将其读回的机制. 序列化:把内存中的java对象转换成与平台无关的二进制字节序列,以便永久保存在磁盘上或通过网络进行传输 ...

  6. 【性能优化】面试官:Java中的对象都是在堆上分配的吗?

    写在前面 从开始学习Java的时候,我们就接触了这样一种观点:Java中的对象是在堆上创建的,对象的引用是放在栈里的,那这个观点就真的是正确的吗?如果是正确的,那么,面试官为啥会问:"Jav ...

  7. 【Java基础】Java中new对象的过程

    序言 联系我上次写的关于Java内存的文章,对象访问在 Java 语言中无处不在,是最普通的程序行为,但即使是最简单的访问,也会却涉及 Java 栈.Java 堆.方法区这三个最重要内存区域之间的关联 ...

  8. 浅谈Java中的对象和引用

    浅谈Java中的对象和对象引用 在Java中,有一组名词经常一起出现,它们就是“对象和对象引用”,很多朋友在初学Java的时候可能经常会混淆这2个概念,觉得它们是一回事,事实上则不然.今天我们就来一起 ...

  9. java中直接打印对象

    java中直接打印对象,会调用对象.toString()方法.如果没有重写toString()方法会输出"类名+@+hasCode"值,hasCode是一个十六进制数 //没有重写 ...

随机推荐

  1. 实现IBatisNet的Dialect分页

    Hibernate有其独有的Dialect,对不同的数据库实现sql的分页. 用过MyBatis for Java,它可以拦截SQL语句,通过Interceptor对原始的sql语句进行修改,也就是可 ...

  2. Java Spring AOP用法

    Java Spring AOP用法 Spring AOP Java web 环境搭建 Java web 项目搭建 Java Spring IOC用法 spring提供了两个核心功能,一个是IoC(控制 ...

  3. 我的ORM之三 -- 更新

    我的ORM索引 更新语法 var 影响行数 = dbr.表.Update(实体).Where(条件).Execute(); 实体类型: 更新的实体类型和添加的实体类型一样,有三类: 1. 任何C#类. ...

  4. nginx(4、缓存)

    nginx提供内置的缓存功能,对静态文件,如html\css\js等能够缓存在本地,即nginx服务器的某个目录下. 其配置主要是两部分: 1.在http下配置一个缓存路径: proxy_cache_ ...

  5. Github注册账户

    这是注册页面: 注册完后应该会受到邮件,但我一直没有收到,换了邮箱也没有用 ± 账户可以登上去却没办法创建仓库.

  6. java那些小事---用偶数做判断,不要用基数做判断

          今天做项目遇到这样一个奇葩问题:我们先看如下代码: int ftcs = dealFtcs(ftcs); if(ftcs % 2 == 1){ //奇数 /* * 处理..... */ } ...

  7. 根据BOM和已存在的文件生成文件列表

    在BOM中记录中有物料编码,物料名称,物料规格等,而且依据BOM已经生成了相应的文件,如采购规格书,检验规格书等,这个时候需要获得这些文件的标题,并且生成一个列表,可以使用下面的VBA代码,具体代码如 ...

  8. 为 Exchange 服务器编写自定义的反垃圾插件

    Exchange 2010 的 Edge Transport 包含了一些 Anti-spam 的 Feature,如图: 都开启了,但是呢,还是会有漏网之鱼,而且把这些邮件自己列为 Junk 也起不了 ...

  9. python:how does subclass call baseclass's __init__()

    First, use baseclass's name to call __init__() I wrote code like this: and we can use 'super' too.

  10. C-Lodop 非典型应用

    Lodop是什么? 有人说她是报表打印工具,因为那个add_print_table语句把报表统计的那点事弄了个明明白白: 有人说她是条码打印工具,因为用了她再也不用后台生成条码图片了,前端一行指令就动 ...