`
米奈希尔
  • 浏览: 268685 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Java对象复制--慎用clone

阅读更多

什么是拷贝、影子拷贝、深度拷贝,不是本文要讨论的。如需了解,以下两个连接还是不错滴。

http://liran-email.iteye.com/blog/550249

http://www.ibm.com/developerworks/cn/java/l-jpointer/index.html

 

1.clone的优点

  a. 获得一个对象的拷贝(此处指深层拷贝)使用赋值操作符“=”是不能完成的;

  b. 无需调用构造函数即可获得对象的拷贝(当然,拷贝对象和被克隆对象之间是否影响取决于深克隆还是浅克隆),一定程度上可以提高执行效率。

 

2.clone的缺点

  以下将根据一个具体的例子来说明这个问题,当然这里指的是深层拷贝。

Car.java -- 父类,没有公开clone方法

package com.clonedemo.test;

public class Car {
	private String type;  // 型号
	private String manufacturer;  //制造商
	private Engine engine;  // 引擎

	public Car(String type, String manufacturer, Engine engine) {
		super();
		this.type = type;
		this.manufacturer = manufacturer;
		this.engine = engine;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public String getManufacturer() {
		return manufacturer;
	}

	public void setManufacturer(String manufacturer) {
		this.manufacturer = manufacturer;
	}

	public Engine getEngine() {
		return engine;
	}

	public void setEngine(Engine engine) {
		this.engine = engine;
	}

	// car common methods such as drive,start,stop definition, omitted
	// ...
}

 

RaceCar.java -- 继承自Car类,为其子类,提供公开的clone方法

package com.clonedemo.test;

public class RaceCar extends Car implements Cloneable {
	private String speeder;
	// other variables, omitted
	// ...

	public RaceCar(String type, String manufacturer, Engine engine,
			String speeder) {
		super(type, manufacturer, engine);
		this.speeder = speeder;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		RaceCar racecar = (RaceCar) super.clone();
		racecar.speeder = speeder;
		
		return racecar;
	}

	public String getSpeeder() {
		return speeder;
	}

	public void setSpeeder(String speeder) {
		this.speeder = speeder;
	}

	// race car methods definition, omitted
	// ...

}

 

Engine.java -- 引擎类,没有公开clone方法

package com.clonedemo.test;

public class Engine {
	private String type; // 引擎型号
	private Integer power; // 马力

	public Engine(String type, Integer power) {
		super();
		this.type = type;
		this.power = power;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public Integer getPower() {
		return power;
	}

	public void setPower(Integer power) {
		this.power = power;
	}

}

 

 CloneTester.java -- 测试类

package com.clonedemo.test;

public class CloneTester {

	/**
	 * @param args
	 * @throws CloneNotSupportedException
	 */
	public static void main(String[] args) throws CloneNotSupportedException {
		RaceCar r1 = new RaceCar("BMW-X7", "BMW", new Engine("RR-X7", 500),
				"speeder-001");
		RaceCar r2 = (RaceCar) r1.clone();

		r2.setManufacturer("GE");
		System.out.println("R1 Manufacturer: " + r1.getManufacturer());
		System.out.println("R2 Manufacturer: " + r2.getManufacturer());

		r2.getEngine().setType("RR-X8");
		System.out.println("R1 Engine Type: " + r1.getEngine().getType());
		System.out.println("R2 Engine Type: " + r2.getEngine().getType());

	}

}

 

输出结果

R1 Engine Type: RR-X8
R2 Engine Type: RR-X8
R1 Manufacturer: BMW
R2 Manufacturer: GE

 

   观察输出结果可以发现,R1和R2的引擎引用的是同一个对象,原因是我们没有为RaceCar的父类实现公开的clone。

   但为什么同是对象类型(String)的Manufacturer却不是指向同一个对象呢? 事实上,你会发现基本类型int,double等对应的Integer,Double等对象在这种情况下和String类型一样,实现了深度克隆的效果。原因在于,这些对象被设计为不可更改的类(immutable class),即一旦这个类初始化,那么类中的函数都不能改变自身的值,而是返回修改后的对象。当执行r2 = r1.clone()后,r1的manufacturer和r2的manufacturer指向的是同一个String对象,这可以通过如下代码证实:

RaceCar r1 = new RaceCar("BMW-X7", "BMW", new Engine("RR-X7",  500), "speeder-001");
RaceCar r2 = (RaceCar) r1.clone();
System.out.println(r1.getManufacturer() == r2.getManufacturer());
输出: true

   当执行r2.setManufacturer("GE");时,r2的manufacturer指向新的字符串对象"GE",所以我们看到以上的结果。

 

   好了,问题来了。当存在继承关系时,父类没有公开的clone方法,而子类需要深层拷贝时,子类的clone方法是否安全呢?

   显而易见,如果子类的clone方法依赖父类的clone就会出问题,除非保证父类公开了clone方法并正确的实现了它,否则就会出现示例的情况;

   此外,当我们为Car类实现clone方法时,是否要依赖Engine类提供公开的且正确的clone呢?如果Engine类是一个final类呢?对于前一个问题,如果依赖,那么Engine不能是一个final类,因为如果是final类,就没法提供公开的clone方法(无法实现Cloneable接口);如果Engine类是final,则Car类的clone就无法依赖Engine的clone,原因同上。

   如果不为Car类提供行为良好的clone,那么子类RaceCar就不能依赖于父类的clone,而要自己实现行为正确的clone,就本例而言,可以这样:

@Override
protected Object clone() throws CloneNotSupportedException {
	RaceCar racecar = (RaceCar) super.clone();
	racecar.setEngine(new Engine(getEngine().getType(), getEngine().getPower()));
	racecar.speeder = speeder;

	return racecar;
}

 输出结果

R1 Engine Type: RR-X7
R2 Engine Type: RR-X8
R1 Manufacturer: BMW
R2 Manufacturer: GE

总结如下:

java中的clone约束是很弱的,因为没有规定一定要实现,但全部都实现又没有必要,因此在使用clone方法进行深层复制时,应该慎重,尤其当存在继承关系时。一个不错的做法是,先调用super.clone,然后对结果对象(super.clone返回对象)的所有域重新赋值(内容为原对象副本),像这样:

@Override
protected Object clone() throws CloneNotSupportedException {
	RaceCar racecar = (RaceCar) super.clone();
	racecar.setType(getType());
	racecar.setManufacturer(getManufacturer());
	racecar.setEngine(new Engine(getEngine().getType(), getEngine().getPower()));
	racecar.speeder = speeder;
	return racecar;
}

 

3.替代方案

(1)提供一个拷贝构造函数(如果你用过C++就不会陌生)

public RaceCar(RaceCar raceCar);

    

    (2)提供一个静态工厂方法,当然名字可以改变,比如deepCopy等

public RaceCar newInstance(RaceCar raceCar);

 

   (3)使用序列化

   如何实现此处不再赘述,资料有很多,本文提供的连接也提及,可以参考。

 

ps:示例代码的clone是protected的,因为文件都放在同一包中,所以访问没问题,实际中也许要改为public

分享到:
评论

相关推荐

    darts-clone-java:用Java编写的DARTS(Double-ARray Trie System)克隆

    darts-clone-java 用Java编写的Double-ARray Trie System克隆。 该库基于称为“快速高效”库的 。入门设置要使用Maven添加依赖项,请使用以下命令: < dependency> < groupId>...

    java不同对象及集合的多次复制

    - **浅拷贝**:创建一个新的对象,只复制对象的引用,不复制内部引用的对象。这意味着改变其中一个对象的内部引用对象,会影响另一个对象。 - **深拷贝**:创建一个全新的对象,不仅复制对象本身,还递归复制其...

    java对象复制克隆

    **浅拷贝**:在浅拷贝中,新创建的对象与原对象共享同一块内存空间,只复制对象的引用,不复制对象的内容。因此,如果对象包含对其他对象的引用,修改复制对象的这些引用会影响到原始对象。 **深拷贝**:与浅拷贝...

    编程语言java对象复制.pdf

    浅复制仅仅复制对象中的基本类型数据和引用其他对象的引用,而不会复制引用的对象本身;而深复制则会复制对象本身以及所引用的所有对象,从而创建一个完全独立的对象副本。 知识点二:Cloneable接口的作用 在Java中...

    Java对象的复制克隆

    Java中的对象复制与克隆是程序开发中常见的需求,主要用于创建一个对象的副本,而不会影响原始对象的状态。这里主要讨论两种类型的对象拷贝:浅拷贝和深拷贝。 浅拷贝,也称为表面拷贝,是创建一个新的对象,然后将...

    PyPI 官网下载 | django-clone-1.1.9.tar.gz

    标题"PyPI 官网下载 | django-clone-1.1.9.tar.gz"表明这是从Python Package Index (PyPI) 官网上下载的一个压缩包,包含了一个名为`django-clone`的软件包的版本1.1.9。PyPI是Python开发者发布自己开发的Python模块...

    java_clone用法

    在Java中,`clone`方法提供了一种快速复制对象的方式。它属于`Object`类的一部分,但需要显式地在子类中声明并实现`Cloneable`接口才能正常使用。本文将详细介绍`clone`的基本概念、工作原理以及如何实现浅拷贝和深...

    java 对象克隆

    总结,Java中的对象克隆是一项重要的功能,用于复制和独立化对象。通过实现`Cloneable`接口和覆盖`clone()`方法,我们可以创建浅克隆对象。对于更复杂的场景,可以自定义克隆逻辑或利用序列化来实现深克隆。理解并...

    java clone的小例子

    在Java编程语言中,`clone()`方法是一个非常重要的概念,特别是在对象复制和克隆方面。这个小例子将帮助初学者理解如何在Java中使用`clone()`来创建对象的副本。让我们深入探讨`clone()`方法以及它在实际编程中的...

    java对象复制.pdf

    Java提供了多种复制对象的方式,其中最常见的就是通过`clone()`方法。 `clone()`方法是Java `Object`类中的一个`native`方法,它能够创建当前对象的一个副本。但是,直接调用`clone()`可能会抛出`...

    MyBatisDemo && JAVA把一个对象的全部属性复制到另一个相同的对象

    深拷贝不仅复制对象,还复制对象中引用的对象。在描述中提到的方法,很可能是关于深拷贝的实现。 在Java中,我们可以通过以下几种方法实现对象的深拷贝: 1. **实现Serializable接口**:利用序列化和反序列化来...

    java对象复制[参考].pdf

    然而,`clone()`方法仅执行浅复制,即复制对象的引用属性,而不是引用指向的实体。如果对象的属性包含其他对象,这些内部对象并不会被复制,而是共享相同的引用。如果需要复制这些内部对象,就需要实现深复制,这...

    java clone

    在实际开发中,`clone`方法通常用于创建对象的临时副本,例如在算法中进行数据备份或在并发环境下复制对象以避免数据竞争。然而,由于`clone`的复杂性和容易出错,许多开发者更倾向于使用`copy-constructor`或者`...

    flow-clone-源码.rar

    解压"flow-clone-源码.zip"后,我们可以看到项目的组织结构。源码文件通常包含以下几个部分:主入口文件(如index.js)、核心功能实现(如lib/clone.js)、辅助函数、测试用例(test目录)以及可能的配置文件(如...

    android-java-organizze-clone

    【压缩包子文件的文件名称列表】"android-java-organizze-clone-master" 暗示了这是一个GitHub项目的主分支(master)的下载包。通常,这个压缩包会包含项目的所有源代码文件、资源文件、配置文件等。开发者可能需要...

    java中的指针,引用及对象的clone

    Java中的对象克隆可以通过实现`Cloneable`接口并覆盖`clone()`方法来完成。对象的克隆分为浅拷贝和深拷贝两种形式。 **1. 浅拷贝** 浅拷贝是指创建一个新的对象,然后将原对象的所有非引用类型的成员变量复制到新...

    前端开源库-better-clone

    浅拷贝仅复制对象的顶层属性,而深拷贝会递归复制整个对象及其嵌套的属性。`better-clone` 库致力于提供深拷贝功能,确保即使面对嵌套对象和数组,也能准确无误地创建副本。 1. **性能优化**: `better-clone` 在...

    Jlink-clone解决办法,替换文件.rar

    当遇到"Jlink-clone"问题时,这通常指的是遇到了非原厂生产的、可能功能受限或者不稳定版本的J-Link设备。这类克隆设备可能会有兼容性问题、性能下降或不支持某些高级功能。本文将深入探讨如何解决Jlink-clone带来的...

    C#中Clone一个对象的值到另一个对象案例 c#经典案例.pdf

    浅复制是 Clone 方法的默认实现,它只是复制对象的引用,而不复制对象的值。例如,下面的代码: ```csharp public class A : ICloneable { public int i; public string str; public object Clone() { return ...

    Java深复制与浅复制&Clone

    在Java编程语言中,对象复制是一个常见的操作,用于创建一个现有对象的新副本。有两种主要的复制方式:浅复制(Shallow Copy)和深复制(Deep Copy)。这两种复制方法在处理对象时有不同的行为和应用场景,特别是在...

Global site tag (gtag.js) - Google Analytics