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 ...
随机推荐
- tomcat端口号修改
修改Tomcat的端口号: 在默认情况下,tomcat的端口是8080,如果出现8080端口号冲突,用如下方法可以修改Tomcat的端口号: 首先: 在Tomcat的根(安装)目录下,有一个conf文 ...
- 进程间通信:命名管道FIFO(2)
一.命名管道 如果我们想在不相关的进程之间交换数据,可以用FIFO文件来完成这项工作,它通常也被称为命名管道.命名管道是一种特殊类型的文件,它在文件系统中以文件名的形式存在,但是它的行为却和我们已经见 ...
- Java容器之List接口
List 接口: 1. List 接口是 Collection 的子接口,实现 List 接口的容器类中的元素是有顺序的,而且可以重复: 2. List 容器中的元素都对应一个整数型的序号记载其在容器 ...
- Swagger字段说明
常用字段说明 字段 说明 schemes 使用协议(如:http.https) host 项目地址,这个地址会作为每个接口的url base,拼接起来一起作为防伪地址 consumes 接口默认接收的 ...
- 【week3】四人小组项目—东师论坛
项目选题:东北师范大学论坛 小组名称:nice! 项目组长:李权 组员:于淼 刘芳芳 杨柳. 本周任务: 1.发布申请 功能列表: 1.注册,登录 2.校内信息公告推送 3.十大热点 (根据搜索量.评 ...
- PAT 甲级 1128 N Queens Puzzle
https://pintia.cn/problem-sets/994805342720868352/problems/994805348915855360 The "eight queens ...
- [计算机网络-应用层] HTTP协议
1.HTTP概况 Web的应用层协议是超文本传输协议(HTTP),它是Web的核心. HTTP由两部分程序实现:一个客户机程序和一个服务器程序,它们运行在不同的端系统中,通过交换HTTP报文进行对话. ...
- [剑指Offer] 44.翻转单词顺序列
题目描述 牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上.同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思.例如,“student ...
- Qt快速入门学习笔记(画图篇)
1.Qt中提供了强大的2D绘图系统,可以使用相同的API在屏幕和绘图设备上进行绘制,它主要基于QPainter.QPaintDevice和QPaintEngine这三个类.其中QPainter用来执行 ...
- bzoj2257[POI2011]Programming Contest
首先可以费用流建图,左边一堆点表示人,右边一堆点表示题,源点向每个人连floor(t/r)条边,费用依次为r,2r,3r….然后写了一个卡不过去,动态加边也卡不过去,然后我想:这里一定有一些不为人知的 ...