`
whitesock
  • 浏览: 484473 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

Java Clone

    博客分类:
  • SE
阅读更多

本文部分内容节选自 Effective Java by Joshua Bloch.

 

   Cloneable接口的目的是作为一个mixin接口,表明实现这个接口的类的对象允许克隆。但是Cloneable接口本身并没有包含任何方法,但是它决定了Object的clone方法的行为:如果一个类实现了Cloneable,则Object的clone方法返回该对象的逐域拷贝(通常所说的浅拷贝,而且在这个过程中没有调用构造函数);否则的话,它抛出一个CloneNotSupportedException异常。 在Object类中,clone方法被定义成protected native Object clone() throws CloneNotSupportedException; 实际上,clone方法可以看成是另一个构造函数。Clone方法的通用约定是非常弱的,以下是java.lang.Object规范(javadoc)的内容:      

  1. x.clone() != x will be true
  2. x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements
  3. x.clone().equals(x) will be true, but these are not absolute requirements

   对于规定x.clone().getClass() == x.getClass(),在一个类的继承体系中,超类能提供这种功能的唯一途径是返回一个通过调用super.clone()方法返回的对象。如果一个类的所有超类都遵守这条规则,那么最终会调用到Object的clone方法,从而创建出正确的类的实例。因此,如果你改写了一个非final类的clone方法,则应该返回一个通过调用super.clone而得到的对象。

public class Base implements Cloneable {
    private String x;

    public Object clone() throws CloneNotSupportedException {
	    return super.clone();
    }

    public String getX() {
	    return x;
    }

    public void setX(String x) {
	    this.x = x;
    }
}

public class Derived extends Base implements Cloneable {
	private String y;
	
	public Object clone() throws CloneNotSupportedException {
		return super.clone();
	}

	public String getY() {
		return y;
	}

	public void setY(String y) {
		this.y = y;
	}
	
	public static void main(String args[]) throws CloneNotSupportedException {
		Derived d1 = new Derived();
		d1.setX("x");
		d1.setY("y");
		Derived d2 = (Derived)d1.clone();
		System.out.println("d2.x: " + d2.getX() + ", d2.y: " + d2.getY());
	}
}

    如果一个超类返回了由构造函数创建的对象,它将是具有错误的类。如下所示:

public class Base implements Cloneable {
    private String x;

    public Object clone() throws CloneNotSupportedException {
        Base base = new Base();
        base.x = this.x;
        return base;
    }

    public String getX() {
	    return x;
    }

    public void setX(String x) {
	    this.x = x;
    }
}

    假设你希望在一个类中实现Cloneable接口,并且它的超类都提供了良好的clone方法,那么你从super.clone方法中的到的对象可能会接近于最终想要的返回的对象,也可能相差甚远,这取决于这个类的本质。如果这个类的成员变量是原始类型或者是指向非可变对象的引用,那么返回的对象可能是你期望的对象。否则,可能需要进一步的处理。设想在Base类中声明一个private String[] elements;成员变量,如果不进行进一步的处理,那么克隆后对象的elements引用和被克隆对象的elements引用实际指向相同的String数组。最容易的解决方法是,对于elements数组递归调用clone方法。由于String对象是非可变对象,因此数组元素不需要再进行clone。

public Object clone() throws CloneNotSupportedException{
	Object r = super.clone();
	((Base)r).elements = this.elements.clone();
	return r;
}

    注意,如果elements成员变量是final的,则这种方案不能正常工作。
    如同构造函数一样,clone方法内不应该调用新对象上任何非final方法。如果调用了新对象上的非final方法,而且这个方法被子类改写过,那么这个被子类改写的方法就可能面对一个没有完全克隆完毕的对象,从而造成错误。Object的clone方法声明可以抛出CloneNotSupportedException异常,子类的改写版本可以忽略这个声明。但是对于一个为了继承而设计的类来说,保留这个声明是合适的。这样做可以是子类通过提供下面的方法,选择温和地放弃克隆的能力。

public Object clone() throws CloneNotSupportedException{
	throw new CloneNotSupportedException();
}

    简而言之,所有实现了Cloneable接口的类都应该用一个公共的clone方法改写超类的clone方法。此clone方法首先调用super.clone方法,然后修正任何需要修正的成员变量。通常情况下,这意味这着深拷贝任何可变对象。
    既然Cloneable接口具有以上那么多问题(与final成员变量发生冲突,强制客户捕获CloneNotSupportedException异常等),所以考虑一下实现对象拷贝的其他方法。一种通常的做法是提供一个C++中常见的拷贝构造函数(copy constructor)例如:

public Derived(Derived d);

     或者一个微小的变形,提供一个静态工厂来代替构造函数,例如:

public static Derived newInstance(Derived d);

     还有一种通常的做法是使用commans-beanutils提供的java bean拷贝工具。例如BeanUtils.copyProperties()和BeanUtils.cloneBean()。这两个方法执行的是浅拷贝。需要注意的是,在copyProperties的过程中会调用相应的Converter进行转换,例如BigDecimalConverter的convert方法如下:

public Object convert(Class type, Object value) {

    if (value == null) {
        if (useDefault) {
            return (defaultValue);
        } else {
            throw new ConversionException("No value specified");
        }
    }

    if (value instanceof BigDecimal) {
        return (value);
    }

    try {
        return (new BigDecimal(value.toString()));
    } catch (Exception e) {
        if (useDefault) {
            return (defaultValue);
        } else {
            throw new ConversionException(e);
        }
    }

}

    从以上代码中可以知道,如果被covert的值是null,而且BigDecimalConverter中没有使用缺省值,那么convert方法会抛出ConversionException。解决方法是在convert前调用ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class); 为BigDecimal类注册一个converter,并使用缺省值null,从而避免抛出异常。由于ConvertUtils上注册的converter是全局的,因此注册了这个converter之后,会影响所有对BigDecimal的covert方式。如果不确定这样做的后果,那么可以采用如下的代码,创建一个局部的BeanUtilsBean来负责拷贝。

ConvertUtilsBean cub = new ConvertUtilsBean();
cub.register(new BigDecimalConverter(null), BigDecimal.class);
BeanUtilsBean bub = new BeanUtilsBean(cub, new PropertyUtilsBean());
bub.copyProperties(dst, src);
3
0
分享到:
评论

相关推荐

    java clone的小例子

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

    java clone

    在Java编程语言中,`clone`是一个非常重要的概念,它涉及到对象复制和对象克隆。本文将深入探讨Java中的`clone`方法,包括其工作原理、使用场景、注意事项以及一些个人实践心得。 首先,让我们理解什么是`clone`。...

    Java clone方法使用

    详细的描述了Java中 clone方法使用

    java Clone

    Java中的`clone`方法是Java语言提供的一种复制对象的方式,它允许创建一个对象的副本,这个副本与原对象具有相同的属性值,但它们是两个独立的对象,修改副本不会影响原对象。`clone`方法存在于Java的`java.lang....

    Java Clone深拷贝与浅拷贝的两种实现方法

    Java Clone 深拷贝与浅拷贝的两种实现方法 Java Clone 是 Java 语言中的一种复制对象的机制,它可以将一个对象的所有属性和状态复制到另一个对象中,实现对象的深拷贝和浅拷贝。下面我们将讨论 Java 中的深拷贝和浅...

    Java Clone(类的复制)实例代码

    在Java编程语言中,`Clone`机制是一种对象复制的方式,允许创建一个现有对象的副本。在Java中,对象的默认复制是浅复制(shallow copy),这意味着只复制对象本身,而不复制它引用的对象。要实现深复制(deep copy)...

    java_clone用法

    ### Java中的`clone`方法详解:浅拷贝与深拷贝 #### 一、引言 在Java中,`clone`方法提供了一种快速复制对象的方式。它属于`Object`类的一部分,但需要显式地在子类中声明并实现`Cloneable`接口才能正常使用。本文...

    关于java clone的一些基本的概念

    在Java编程语言中,`clone`是一个非常重要的概念,它涉及到对象复制和对象的深拷贝与浅拷贝。本文将深入探讨Java中的`clone`方法及其相关知识点。 首先,`clone`方法是Java `Object`类的一个成员方法,定义为`...

    Java clone方法详解及简单实例

    Java中的`clone`方法是Java语言提供的一种复制对象的机制,它允许创建一个现有对象的副本,这个副本具有与原始对象相同的状态,但它们是独立的实体,对其中一个对象的修改不会影响另一个。`clone`方法是Java `Object...

    Java中的clone方法详解_动力节点Java学院

    Java中的clone方法详解_动力节点Java学院,动力节点口口相传的Java黄埔军校

    Java中的克隆(Clone)机制

    在Java编程语言中,克隆(Clone)机制是一种创建对象副本的方法,它允许开发者创建一个已有对象的新实例,这个新实例与原对象具有相同的属性值,但却是两个独立的对象,彼此的操作不会互相影响。克隆机制在某些情况...

    clone()示例源码

    在Java编程语言中,`clone()`方法是一个非常重要的概念,特别是在处理对象复制和克隆时。这个方法源自`Object`类,是所有Java类的基类。`clone()`的使用通常涉及到深度复制和浅复制的概念,这两者在数据结构和内存...

    Java中的clone方法详解_动力节点Java学院整理

    Java中的clone方法详解 在Java语言中,clone方法是一个非常重要的概念,它允许对象被复制,从而创造出一个新的对象。下面我们将详细介绍Java中的clone方法,并讨论它的实现机制和应用场景。 什么是clone方法 ...

    java 中clone()的使用方法

    Java 中 clone() 的使用方法 Java 中的 clone() 方法是对象的复制方法,其主要作用是创建一个与原对象相同的新对象。下面将详细介绍 Java 中 clone() 方法的使用方法。 什么是 clone() 方法? clone() 方法是 ...

    Java深浅clone

    在Java编程语言中,`Cloneable`接口和`clone()`方法是两个重要的概念,它们用于对象复制。在本文中,我们将深入探讨Java中的浅克隆(shallow clone)和深克隆(deep clone),并结合测试代码进行分析。 首先,让...

    如何通过JVM角度谈谈Java的clone操作

    Java中的对象创建主要有两种方式,即使用`new`操作符创建新对象以及通过`clone`方法复制已有对象。本文将从JVM的角度深入探讨Java的`clone`操作,揭示其工作原理及其潜在的问题。 首先,让我们理解`clone`方法的...

    java中clone的用法实例

    clone的用法 希望有帮助,仅供参考 通过例子的分析,可以对克隆的方法有所深入了解

    Java clone()方法来由及用法

    并且由于Java不能通过简单的赋值来解决对象复制的问题,在开发过程中,也常常要要应用clone()方法来复制对象。比如函数参数类型是自定义的类时,此时便是引用传递而不是值传递。以下是一个小例子: ...

Global site tag (gtag.js) - Google Analytics