java中复制对象通过反射或序列化
在使用缓存读取数据后修改发现缓存被修改。于是找了下复制对象的方法。
关于对象克隆
按我的理解,对象是包含引用+数据。通常变量复制都是将引用传递过去。比如:
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中复制对象通过反射或序列化的更多相关文章
- 【译】Java中的对象序列化
前言 好久没翻译simple java了,睡前来一篇. 译文链接: http://www.programcreek.com/2014/01/java-serialization/ 什么是对象序列化 在 ...
- JAVA中JavaBean对象之间属性拷贝的方法
JAVA中JavaBean对象之间的拷贝通常是用get/set方法,但如果你有两个属性相同的JavaBean或有大部分属性相同的JavaBean,对于这种情况,可以采用以下几个简便方法处理. 下面对这 ...
- 【学习笔记】Java中生成对象的5中方法
概述:本文介绍以下java五种创建对象的方式: 1.用new语句创建对象,这是最常用的创建对象的方式. 2.使用Class类的newInstance方法 3.运用反射手段,调用java.lang.re ...
- JAVA中JavaBean对象之间拷贝的方法
JAVA中JavaBean对象之间的拷贝通常是用get/set方法,但如果你有两个属性相同的JavaBean或有大部分属性相同的JavaBean,有个更简便的方法,他们之间的拷贝可以通过copyPro ...
- Java I/O - 对象的输入输出与序列化
先说概念: 一.相关概念 序列化是Java提供的一种将对象写入到输出流.并在之后将其读回的机制. 序列化:把内存中的java对象转换成与平台无关的二进制字节序列,以便永久保存在磁盘上或通过网络进行传输 ...
- 【性能优化】面试官:Java中的对象都是在堆上分配的吗?
写在前面 从开始学习Java的时候,我们就接触了这样一种观点:Java中的对象是在堆上创建的,对象的引用是放在栈里的,那这个观点就真的是正确的吗?如果是正确的,那么,面试官为啥会问:"Jav ...
- 【Java基础】Java中new对象的过程
序言 联系我上次写的关于Java内存的文章,对象访问在 Java 语言中无处不在,是最普通的程序行为,但即使是最简单的访问,也会却涉及 Java 栈.Java 堆.方法区这三个最重要内存区域之间的关联 ...
- 浅谈Java中的对象和引用
浅谈Java中的对象和对象引用 在Java中,有一组名词经常一起出现,它们就是“对象和对象引用”,很多朋友在初学Java的时候可能经常会混淆这2个概念,觉得它们是一回事,事实上则不然.今天我们就来一起 ...
- java中直接打印对象
java中直接打印对象,会调用对象.toString()方法.如果没有重写toString()方法会输出"类名+@+hasCode"值,hasCode是一个十六进制数 //没有重写 ...
随机推荐
- Web Essentials之Markdown和自定义编辑器(Web Essentials完结)
返回Web Essentials功能目录 本篇目录 功能 自定义编辑器 开源项目都会在项目的根目录放一个Readme.md文件来告诉读者一些重要的说明,那么就可以在VS中直接编辑Markdown文件. ...
- 设计模式之美:Behavioral Patterns(行为型模式)
行为型模式涉及到算法和对象间职责的分配. 行为模式不仅描述对象或类的模式,还描述它们之间的通信模式. 这些模式刻划了在运行时难以跟踪的复杂的控制流.它们将你的注意力从控制流转移到对象间的联系方式上来. ...
- AlwaysON 故障处理之辅助副本磁盘空间不足
用户反馈AlwaysON辅助副本数据库查询的结果与主库不一致, 远程到服务器后发现数据库的状态显示为“未同步/可疑”, 查看数据库的日志,定位到出现错误的时间点,可以看到提示日志文件所在磁盘的“磁盘空 ...
- Smack Message扩展,添加自定义元素(标签)经验分享
Smack框架对XMPP协议进行了封装,从而方便与Openfire即时通信服务器做交互.说白了,Smack框架可以通过对象构造符合XMPP协议的XML字符串,避免手动拼接字符串. XMPP协议基本XM ...
- 为 WSUS 服务器定期运行清理向导
在 WSUS 的管理界面的 Options 里面,可以找到 Server Cleanup Wizard 然后运行.后来想了一下,为什么不把它弄成定期运行呢! 找了一下,从 Windows Server ...
- redis系列-主从复制
redis自身提供了主从的机制,通过配置可以实现服务的备份(Master->Slave). 配置项 slaveof <masterip> <masterport> mas ...
- swift 创建单例模式
一.意图 保证一个类公有一个实例,并提供一个访问它的全局访问点. 二.使用场景 1.使用场景 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时 当这个唯一实例应该是通过子类化可扩展的,并且 ...
- Java-类与类之间的关系
类与类之间的几种关系 一.继承关系 继承指的是一个类(称为子类.子接口)继承另外的一个类(称为父类.父接口)的功能,并可以增加它自己的新功能的能力.在Java中继承关系通过关键字exte ...
- JDBC操作数据库,第一:jsp插入mysql数据库,坎坷摸索分享
JSP连接数据库,坎坷摸索了好久,现在终于做好了,分享一下,希望对更多热爱编程学习的人有所帮助!!!谢谢 第一:首先准备的就是已经安装好Mysql,这里不做多叙述,百度可以做到. 然后在mysql数据 ...
- git回滚到任意版本
git回滚到任意版本 先显示提交的log $ git log -3 commit 4dc08bb8996a6ee02f Author: Mark <xxx@xx.com> Date: We ...