Item 11 谨慎地覆盖Clone
1.进行浅拷贝时,只是复制原始数据类型的值,则可以通过覆盖Clone方法来达到。另外,在进行浅拷贝的时候,还要注意,成员对象中不应该要有引用类型,如果有引用类型,那么,进行了浅拷贝之后,两个对象将会共享成员引用所指向的对象,这会出现问题。所以,在这种情况下,干脆直接使用深拷贝,避免问题出现。2.对于深拷贝,也就是完全将对象的内容复制一份,则使用序列化来实现,也是为了避免覆盖Clone。
浅拷贝的例子:
这个例子,只是复制成员的值,成员的类型都是原始数据类型,不包含引用类型:
public class CloneTest1 { public static void main(String[] args) throws CloneNotSupportedException {
Student student1 = new Student();
student1.setName("ZhangSan");
student1.setAge(20); Student student2 = new Student();
student2 = (Student) student1.clone(); System.out.println("拷贝得到的信息");
System.out.println(student2.getName());
System.out.println(student2.getAge());
System.out.println("-------------"); // 修改第二个对象的信息
student2.setName("LiSi");
student2.setAge(25); System.out.println("修改第二个对象的属性为lisi,25后:");
System.out.println("第一个对象:");
System.out.println(student1.getName());
System.out.println(student1.getAge());
System.out.println("第二个对象:");
System.out.println(student2.getName());
System.out.println(student2.getAge());
System.out.println("-------------"); // 说明两个引用student1和student2指向的是不同的对象 }
} class Student implements Cloneable {
private String name;
private int age; 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;
} @Override
public Student clone() throws CloneNotSupportedException {
// 注意此处要把protected改为public Student student = (Student)super.clone(); return student;
}
}
输出:
拷贝得到的信息
ZhangSan
20
-------------
修改第二个对象的属性为lisi,25后:
第一个对象:
ZhangSan
20
第二个对象:
LiSi
25
-------------
浅拷贝例子2:
这个例子中,成员的类型,有引用类型
public class CloneTest2 {
public static void main(String[] args) throws CloneNotSupportedException {
Teacher teacher = new Teacher();
teacher.setName("Teacher Zhang");
teacher.setAge(40); StudentNew student1 = new StudentNew();
student1.setName("ZhangSan");
student1.setAge(20);
student1.setTeacher(teacher); StudentNew student2 = (StudentNew) student1.clone();
System.out.println("拷贝得到的信息");
System.out.println(student2.getName());
System.out.println(student2.getAge());
System.out.println(student2.getTeacher().getName());
System.out.println(student2.getTeacher().getAge());
System.out.println("-------------"); // 修改老师的信息
teacher.setName("Teacher Zhang has changed");
System.out.println(student1.getTeacher().getName());
System.out.println(student2.getTeacher().getName()); // 两个引用student1和student2指向不同的两个对象
// 但是两个引用student1和student2中的两个teacher引用指向的是同一个对象
// 所以说明是浅拷贝
} } class Teacher {
private String name;
private int age; 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;
} } class StudentNew implements Cloneable {
private String name;
private int age;
private Teacher teacher; 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 Teacher getTeacher() {
return teacher;
} public void setTeacher(Teacher teacher) {
this.teacher = teacher;
} @Override
public StudentNew clone() throws CloneNotSupportedException {
StudentNew object = (StudentNew) super.clone();
return object;
} }
输出结果:
拷贝得到的信息
ZhangSan
20
Teacher Zhang
40
-------------
Teacher Zhang has changed
Teacher Zhang has changed
解析:
Student1和Student2,是两个对象,执行浅拷贝之后,两个的成员的值是相等的。如果里面包含了引用类型的成员,那么,它们是指向同一个对象的。比如,引用类型teacher成员。因为指向同一个对象,所以对teacher的修改,会同时影响到Student1,Student2.这会出现很多意料不到的问题。对于这种情况,我倾向于,使用序列化,做一个深拷贝。
深拷贝,使用序列化实现的深拷贝:
使用序列化实现深拷贝,优点是,不用覆盖Clone(覆盖Clone方法,把握不好,会出现各种问题);缺点是:类成员如果有类类型,则都要实现序列化接口,这样才可以进行序列化,比如下面的例子。
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; public class JavaDeepClone { public static void main(String[] args) {
// (1) create a Person object named Al
Address address = new Address("305 West Fern Avenue", "Palmer", "Alaska");
Person al = new Person("Al", "Alexander", address); // (2) make a deep clone of Al
Person neighbor = (Person) deepClone(al); // (3) modify the neighbor's attributes
neighbor.firstName = "Martha";
neighbor.lastName = "Stewart"; // (4) show that it all worked
System.out.print(neighbor);
System.out.println(al);
} /**
* This method makes a "deep clone" of any object it is given.
*/
public static Object deepClone(Object object) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(object);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
} /**
* These classes implement Serializable so we can write them out and read them back in as a stream
* of bytes.
*/
class Person implements Serializable {
/**
*
*/
private static final long serialVersionUID = 4112183016776552816L;
String firstName, lastName;
Address address; public Person(String firstName, String lastName, Address address) {
this.firstName = firstName;
this.lastName = lastName;
this.address = address;
} public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("First Name: " + firstName + "\n");
sb.append("Last Name: " + lastName + "\n");
sb.append("Street: " + address.street + "\n");
return sb.toString();
}
} class Address implements Serializable {
/**
*
*/
private static final long serialVersionUID = -3711035200324594412L;
String street, city, state; public Address(String street, String city, String state) {
this.street = street;
this.city = city;
this.state = state;
}
}
输出结果:
First Name: Martha
Last Name: Stewart
Street: 305 West Fern Avenue
First Name: Al
Last Name: Alexander
Street: 305 West Fern Avenue
引用:
http://www.cnblogs.com/mengdd/archive/2013/02/20/2917971.html
http://alvinalexander.com/java/java-deep-clone-example-source-code
Item 11 谨慎地覆盖Clone的更多相关文章
- 第11条:谨慎地覆盖clone
Clone提供一种语言之外的机制:无需调用构造器就可以创建对象. 它的通用约定非常弱: 创建和返回该对象的一个拷贝.这个拷贝的精确含义取决于该对象的类.一般含义是,对于任何对象x,表达式x.clone ...
- Effective Java 之-----谨慎的覆盖clone方法
1.概述 如果clone方法返回一个由构造器创建的对象,它就得到有错误的类.因此,如果覆盖了非final类中的clone方法,则应该返回一个通过调用super.clone得到的对象.如果类的所有超类都 ...
- 第十一条:谨慎的覆盖clone()方法
一个类要想实现克隆,需要实现Cloneable接口,表明这个类的对象具有克隆的功能. Cloneable接口是一个mixin接口,它里面并没有任何的抽象方法,类似的接口有Serializable接口, ...
- Java - 谨慎覆盖clone
覆盖clone时需要实现Cloneable接口,Cloneable并没有定义任何方法. 那Cloneable的意义是什么? 如果一个类实现了Clonable,Object的clone方法就可以返回该对 ...
- Effective Java 第三版——13. 谨慎地重写 clone 方法
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- Effective Java —— 谨慎覆盖clone
本文参考 本篇文章参考自<Effective Java>第三版第十三条"Always override toString",在<阿里巴巴Java开发手册>中 ...
- item 11: 比起private undefined function优先使用deleted function
本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 如果你为其他开发者提供代码,并且你想阻止他们调用一个特定的函数,你 ...
- XE3随笔11:Merge、Clone、ForcePath
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, For ...
- 读书笔记 effective c++ Item 11 在operator=中处理自我赋值
1.自我赋值是如何发生的 当一个对象委派给自己的时候,自我赋值就会发生: class Widget { ... }; Widget w; ... w = w; // assignment to sel ...
随机推荐
- js 拼接字符串时,本来想要’#1′ ,返回的却是’#01′
今天在操作一个元素时,id值是拼接的. var index = $(this).attr(‘index’); //0var id = ‘#’ + (index+1); //#01$(id) ...
- VMbox复制虚拟机后网卡问题-bring up interface eth0:Device eth0 does not seem to be present
1.使用 ifconfig -a 查看mac地址 eg:HWaddr:08:00:29:B2:2B 2.vi /etc/sysconfig/network-scripts/ifcfg-eth0 将 ...
- SQL SERVER技术内幕之7 透视与逆透视
1.透视转换 透视数据(pivoting)是一种把数据从行的状态旋转为列的状态的处理,在这个过程中可能须要对值进行聚合. 每个透视转换将涉及三个逻辑处理阶段,每个阶段都有相关的元素:分组阶段处理相关的 ...
- Java多线程同步机制之同步块(方法)——synchronized
在多线程访问的时候,同一时刻只能有一个线程能够用 synchronized 修饰的方法或者代码块,解决了资源共享.下面代码示意三个窗口购5张火车票: package com.jikexueyuan.t ...
- Mysql查询优化从入门到跑路(一)数据库与关系代数
1.怎样才算是数据库? ACID,是指在数据库管理系统中事务所具有的四个特性 1)原子性 2)一致性 3)隔离性 4)持久性 关系数据库,基于关系代 ...
- Delphi Code Editor 之 快捷菜单
Code Editor的快捷菜单分为两个部分:编辑器菜单项和调试器菜单项. 调试器菜单项留作以后讲解调试应用程序时再讲,这里只讲讲Code Editor的编辑器快捷菜单项. 下面列出了全部菜单项及描述 ...
- BZOJ4773 负环(floyd+倍增)
倍增floyd求出经过<=2k条边时两点间最短路,一个点到自身的最短路就是包含该点的最小环.然后倍增找答案即可.注意初始时到自身的最短路设为0,这样求出的最短路就是经过<=2k条边的而不是 ...
- 虚拟机如何设置U盘启动项
开始配置虚拟机时选"自定义"不要选"典型",在“SCSI设配器”选LSI logic ,(不是默认的那个):然后其他正常默认创建,虚拟机建好后,再添加U盘为虚拟 ...
- BZOJ2049:[SDOI2008]洞穴勘测——题解
http://www.lydsy.com/JudgeOnline/problem.php?id=2049 https://www.luogu.org/problemnew/show/P2147 辉辉热 ...
- Static全局变量与普通的全局变量有什么区别?static函数与普通函数有什么区别?
Static全局变量与普通的全局变量有什么区别? 答: 全局变量(外部变量)的说明之前再冠以static就构成了静态的全局变量.全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式. 这两者 ...