Java中的深浅拷贝
Java中对象复制的方式
- 直接赋值
- 浅拷贝
- 深拷贝
概念明确
Java把内存划分成两种:一种是堆内存,一种是栈内存。
堆(heap):主要用于存储实例化的对象,数组。由JVM动态分配内存空间,堆内存还可以用来存放由new创建的对象和数组。一个JVM只有一个堆内存,线程是可以共享数据的。
栈(stack):主要用于存储局部变量和对象的引用变量,每个线程都会有一个独立的栈空间,所以线程之间是不共享数据的。
talk is c heap , show me your code.
直接赋值Demo
@Data
@AllArgsConstructor
public class Person {
private String name;
private int age;
private String email;
private String desc;
}
class PersonApp {
public static void main(String[] args) {
Person person = new Person("张三", 22, "156xxxx2775@163.com", "我是张三");
//复制对象
Person person1 = person;
person1.setName("李四");
System.out.println("person" + person + " " + person.hashCode());
System.out.println("person1" + person1 + " " + person1.hashCode());
Console.log(person == person1);
}
}
输出: personPerson(name=李四, age=20, email=156xxxx2775@163.com, desc=我是张三) 1528617539 person1Person(name=李四, age=20, email=156xxxx2775@163.com, desc=我是张三) 1528617539 true
直接赋值是Java开发中最简单且常用的方式,通过Demo输出可见,原对象person赋值给新对象person1,并给person1对象的某个字段进行重新赋值,其hashCode是一致的,说明在Java的中并没有创建新的内存地址,而是复制了原对象的引用地址而已。
图片源自
浅拷贝Demo
@Data
@AllArgsConstructor
public class Person implements Cloneable {
/**
* 基本类型属性
*/
private String name;
private int age;
private String email;
private PersonDesc personDesc;
public void setDesc(String desc) {
this.personDesc.setDesc(desc);
}
public Person(String name, int age, String email, String desc) {
this.name = name;
this.age = age;
this.email = email;
this.personDesc = new PersonDesc();
this.personDesc.setDesc(desc);
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
/**
* 引用类型属性
*
* @return
* @throws CloneNotSupportedException
*/
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person("张三", 20, "123456@qq.com", "Java开发");
Person person1 = (Person) person.clone();
person1.setName("Clone 张三S");
person1.setAge(23);
person1.setDesc("JavaScript");
System.out.println(person + " " + person.hashCode());
System.out.println(person1 + " " + person1.hashCode());
System.out.println(person == person1);
}
}
输出:
Person(name=张三, age=20, email=123456@qq.com, personDesc=PersonDesc(desc=JavaScript)) 1110312536 Person(name=Clone 张三S, age=23, email=123456@qq.com, personDesc=PersonDesc(desc=JavaScript)) 57334109 false
浅拷贝在原对象中的基本类型拷贝中,会复制一份到克隆对象,并在堆中开辟新的内存空间,对于引用类型,则会拷贝引用对象的内存地址,并不会把引用类型也克隆一份到堆内存中,由于原对象和克隆对象是引用的内存地址,因此如果两对象的任何一方改变这个地址,就会影响到另一个对象。
深拷贝Demo
- 实现Cloneable方式 PersonDesc.class
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PersonDesc implements Cloneable {
private String desc;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Person.class
@Data
@AllArgsConstructor
public class Person implements Cloneable {
/**
* 基本类型属性
*/
private String name;
private int age;
private String email;
private PersonDesc personDesc;
public void setDesc(String desc) {
this.personDesc.setDesc(desc);
}
public Person(String name, int age, String email, String desc) {
this.name = name;
this.age = age;
this.email = email;
this.personDesc = new PersonDesc();
this.personDesc.setDesc(desc);
}
/**
* 深克隆的方式
*
* @return
* @throws CloneNotSupportedException
*/
@Override
public Object clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
//对引用对象进行克隆
person.personDesc = (PersonDesc) personDesc.clone();
return person;
}
/**
* 引用类型属性
*
* @return
* @throws CloneNotSupportedException
*/
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person("张三", 20, "123456@qq.com", "Java开发");
Person person1 = (Person) person.clone();
person1.setName("Clone 张三S");
person1.setAge(23);
person1.setDesc("JavaScript");
System.out.println(person + " " + person.hashCode());
System.out.println(person1 + " " + person1.hashCode());
System.out.println(person == person1);
}
}
输出:
Person(name=张三, age=20, email=123456@qq.com, personDesc=PersonDesc(desc=Java开发)) 2056507198 Person(name=Clone 张三S, age=23, email=123456@qq.com, personDesc=PersonDesc(desc=JavaScript)) 57334109 false
2.实现 Serializable 接口方式
Person.class
@Data
@AllArgsConstructor
public class Person implements Serializable {
private static final long serialVersionUID = 369285298572941L;
private String name;
private int age;
private String email;
private PersonDesc personDesc;
public Person(String name, int age, String email, String desc) {
this.name = name;
this.age = age;
this.email = email;
this.personDesc = new PersonDesc();
this.personDesc.setDesc(desc);
}
@Override
public Person clone() {
Person person = null;
try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
// 将流序列化成对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
person = (Person) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return person;
}
public void setDesc(String desc) {
this.personDesc.setDesc(desc);
}
public static class PersonApp {
public static void main(String[] args) throws Exception {
// 初始化一个对象
Person person = new Person("平头", 20, "123456@qq.com", "技术");
// 复制对象
Person person1 = (Person) person.clone();
// 改变 person1 的属性值
person1.setName("我是平头的克隆对象");
// 修改 person age 的值
person1.setAge(22);
person1.setDesc("我已经关注了技术");
System.out.println("person对象:" + person);
System.out.println();
System.out.println("person1对象:" + person1);
}
}
}
PersonDesc.class
@Data
public class PersonDesc implements Serializable {
private static final long serialVersionUID = 872390113109L;
private String desc;
}
深拷贝是把所有的属性都在内存堆里去创建新的内存地址,克隆对象与原对象是两个不同的对象。
拷贝方式的选择
- 如果对象的属性全是基本类型的,那么可以使用浅拷贝。
- 如果对象有引用属性且引用对象经常改变,那么就选择深拷贝,如果引用对象一成不变,可以选择浅拷贝。
相关文章
- Jease 2.6发布 Java开源内容框架
- JVM调优总结:反思
- JVM调优总结:调优方法
- JVM调优总结:新一代的垃圾回收算法
- JVM调优总结:典型配置举例
- JVM调优总结:分代垃圾回收详述
- JVM调优总结:垃圾回收面临的问题
- JVM调优总结:基本垃圾回收算法
- JVM调优总结:一些概念
- 用Java GUI编写的画板程序
- Java的动态绑定机制
- jOOQ 2.0.2发布 Java的ORM框架
- Java中带复选框的树的实现和应用
- Java网络编程菜鸟进阶:TCP和套接字入门
- 甲骨文与谷歌专利权之争定于今年三月开审
- Java调用C/C++编写的第三方dll动态链接库
- 集成开发环境 NetBeans IDE 7.1正式版发布
- kangle 2.7.5紧急发布 防hash碰撞攻击
- 东方通技术引领模式为国产软件“争权”
- UML中关联,组合与聚合等关系的辨析