java克隆的控制

为消除克隆能力,大家也许认为只需将clone()方法简单地设为private(私有)即可,但这样是行不通的,因为不能采用一个基础类方法,并使其在衍生类中更“私有”。所以事情并没有这么简单。此外,我们有必要控制一个对象是否能够克隆。对于我们设计的一个类,实际有许多种方案都是可以采取的:
(1) 保持中立,不为克隆做任何事情。也就是说,尽管不可对我们的类克隆,但从它继承的一个类却可根据实际情况决定克隆。只有Object.clone()要对类中的字段进行某些合理的操作时,才可以作这方面的决定。
(2) 支持clone(),采用实现Cloneable(可克隆)能力的标准操作,并覆盖clone()。在被覆盖的clone()中,可调用super.clone(),并捕获所有违例(这样可使clone()不“掷”出任何违例)。
(3) 有条件地支持克隆。若类容纳了其他对象的句柄,而那些对象也许能够克隆(集合类便是这样的一个例子),就可试着克隆拥有对方句柄的所有对象;如果它们“掷”出了违例,只需让这些违例通过即可。举个例子来说,假设有一个特殊的Vector,它试图克隆自己容纳的所有对象。编写这样的一个Vector时,并不知道客户程序员会把什么形式的对象置入这个Vector中,所以并不知道它们是否真的能够克隆。
(4) 不实现Cloneable(),但是将clone()覆盖成protected,使任何字段都具有正确的复制行为。这样一来,从这个类继承的所有东西都能覆盖clone(),并调用super.clone()来产生正确的复制行为。注意在我们实现方案里,可以而且应该调用super.clone()——即使那个方法本来预期的是一个Cloneable对象(否则会掷出一个违例),因为没有人会在我们这种类型的对象上直接调用它。它只有通过一个衍生类调用;对那个衍生类来说,如果要保证它正常工作,需实现Cloneable。
(5) 不实现Cloneable来试着防止克隆,并覆盖clone(),以产生一个违例。为使这一设想顺利实现,只有令从它衍生出来的任何类都调用重新定义后的clone()里的suepr.clone()。
(6) 将类设为final,从而防止克隆。若clone()尚未被我们的任何一个上级类覆盖,这一设想便不会成功。若已被覆盖,那么再一次覆盖它,并“掷”出一个CloneNotSupportedException(克隆不支持)违例。为担保克隆被禁止,将类设为final是唯一的办法。除此以外,一旦涉及保密对象或者遇到想对创建的对象数量进行控制的其他情况,应该将所有构建器都设为private,并提供一个或更多的特殊方法来创建对象。采用这种方式,这些方法就可以限制创建的对象数量以及它们的创建条件——一种特殊情况是第16章要介绍的singleton(独子)方案。

下面这个例子总结了克隆的各种实现方法,然后在层次结构中将其“关闭”:
 

//: CheckCloneable.java
// Checking to see if a handle can be cloned

// Can't clone this because it doesn't
// override clone():
class Ordinary {}

// Overrides clone, but doesn't implement
// Cloneable:
class WrongClone extends Ordinary {
  public Object clone()
      throws CloneNotSupportedException {
    return super.clone(); // Throws exception
  }
}

// Does all the right things for cloning:
class IsCloneable extends Ordinary
    implements Cloneable {
  public Object clone()
      throws CloneNotSupportedException {
    return super.clone();
  }
}

// Turn off cloning by throwing the exception:
class NoMore extends IsCloneable {
  public Object clone()
      throws CloneNotSupportedException {
    throw new CloneNotSupportedException();
  }
}

class TryMore extends NoMore {
  public Object clone()
      throws CloneNotSupportedException {
    // Calls NoMore.clone(), throws exception:
    return super.clone();
  }
}

class BackOn extends NoMore {
  private BackOn duplicate(BackOn b) {
    // Somehow make a copy of b
    // and return that copy. This is a dummy
    // copy, just to make the point:
    return new BackOn();
  }
  public Object clone() {
    // Doesn't call NoMore.clone():
    return duplicate(this);
  }
}

// Can't inherit from this, so can't override
// the clone method like in BackOn:
final class ReallyNoMore extends NoMore {}

public class CheckCloneable {
  static Ordinary tryToClone(Ordinary ord) {
    String id = ord.getClass().getName();
    Ordinary x = null;
    if(ord instanceof Cloneable) {
      try {
        System.out.println("Attempting " + id);
        x = (Ordinary)((IsCloneable)ord).clone();
        System.out.println("Cloned " + id);
      } catch(CloneNotSupportedException e) {
        System.out.println(
          "Could not clone " + id);
      }
    }
    return x;
  }
  public static void main(String[] args) {
    // Upcasting:
    Ordinary[] ord = {
      new IsCloneable(),
      new WrongClone(),
      new NoMore(),
      new TryMore(),
      new BackOn(),
      new ReallyNoMore(),
    };
    Ordinary x = new Ordinary();
    // This won't compile, since clone() is
    // protected in Object:
    //! x = (Ordinary)x.clone();
    // tryToClone() checks first to see if
    // a class implements Cloneable:
    for(int i = 0; i < ord.length; i++)
      tryToClone(ord[i]);
  }
} ///:~

第一个类Ordinary代表着大家在本书各处最常见到的类:不支持克隆,但在它正式应用以后,却也不禁止对其克隆。但假如有一个指向Ordinary对象的句柄,而且那个对象可能是从一个更深的衍生类上溯造型来的,便不能判断它到底能不能克隆。
WrongClone类揭示了实现克隆的一种不正确途径。它确实覆盖了Object.clone(),并将那个方法设为public,但却没有实现Cloneable。所以一旦发出对super.clone()的调用(由于对Object.clone()的一个调用造成的),便会无情地掷出CloneNotSupportedException违例。
在IsCloneable中,大家看到的才是进行克隆的各种正确行动:先覆盖clone(),并实现了Cloneable。但是,这个clone()方法以及本例的另外几个方法并不捕获CloneNotSupportedException违例,而是任由它通过,并传递给调用者。随后,调用者必须用一个try-catch代码块把它包围起来。在我们自己的clone()方法中,通常需要在clone()内部捕获CloneNotSupportedException违例,而不是任由它通过。正如大家以后会理解的那样,对这个例子来说,让它通过是最正确的做法。
类NoMore试图按照Java设计者打算的那样“关闭”克隆:在衍生类clone()中,我们掷出CloneNotSupportedException违例。TryMore类中的clone()方法正确地调用super.clone(),并解析成NoMore.clone(),后者掷出一个违例并禁止克隆。
但在已被覆盖的clone()方法中,假若程序员不遵守调用super.clone()的“正确”方法,又会出现什么情况呢?在BackOn中,大家可看到实际会发生什么。这个类用一个独立的方法duplicate()制作当前对象的一个副本,并在clone()内部调用这个方法,而不是调用super.clone()。违例永远不会产生,而且新类是可以克隆的。因此,我们不能依赖“掷”出一个违例的方法来防止产生一个可克隆的类。唯一安全的方法在ReallyNoMore中得到了演示,它设为final,所以不可继承。这意味着假如clone()在final类中掷出了一个违例,便不能通过继承来进行修改,并可有效地禁止克隆(不能从一个拥有任意继承级数的类中明确调用Object.clone();只能调用super.clone(),它只可访问直接基础类)。因此,只要制作一些涉及安全问题的对象,就最好把那些类设为final。
在类CheckCloneable中,我们看到的第一个类是tryToClone(),它能接纳任何Ordinary对象,并用instanceof检查它是否能够克隆。若答案是肯定的,就将对象造型成为一个IsCloneable,调用clone(),并将结果造型回Ordinary,最后捕获有可能产生的任何违例。请注意用运行期类型鉴定(见第11章)打印出类名,使自己看到发生的一切情况。
在main()中,我们创建了不同类型的Ordinary对象,并在数组定义中上溯造型成为Ordinary。在这之后的头两行代码创建了一个纯粹的Ordinary对象,并试图对其克隆。然而,这些代码不会得到编译,因为clone()是Object中的一个protected(受到保护的)方法。代码剩余的部分将遍历数组,并试着克隆每个对象,分别报告它们的成功或失败。输出如下:
 

Attempting IsCloneable
Cloned IsCloneable
Attempting NoMore
Could not clone NoMore
Attempting TryMore
Could not clone TryMore
Attempting BackOn
Cloned BackOn
Attempting ReallyNoMore
Could not clone ReallyNoMore

总之,如果希望一个类能够克隆,那么:
(1) 实现Cloneable接口
(2) 覆盖clone()
(3) 在自己的clone()中调用super.clone()
(4) 在自己的clone()中捕获违例
这一系列步骤能达到最理想的效果。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索对象
, object
, 方法
, 克隆
, clone
, clone方法
, 一个
, 对象克隆
, 克隆对象
, clone对象
, 克隆方法
, 克隆数组
, 数组克隆
clone()
java对象的克隆、java的克隆、java中的克隆、java中的克隆技术、java 克隆对象,以便于您获取更多的相关知识。

时间: 2024-09-17 04:51:08

java克隆的控制的相关文章

Java的违例控制:解决错误

从最古老的程序设计语言开始,错误控制一直都是设计者们需要解决的一个大问题.由于很难设计出一套完美的错误控制方案,许多语言干脆将问题简单地忽略掉,将其转嫁给库设计人员.对大多数错误控制方案来说,最主要的一个问题是它们严重依赖程序员的警觉性,而不是依赖语言本身的强制标准.如果程序员不够警惕--若比较匆忙,这几乎是肯定会发生的--程序所依赖的错误控制方案便会失效. "违例控制"将错误控制方案内置到程序设计语言中,有时甚至内建到操作系统内.这里的"违例"(Exception

java克隆对象

若需修改一个对象,同时不想改变调用者的对象,就要制作该对象的一个本地副本.这也是本地副本最常见的一种用途.若决定制作一个本地副本,只需简单地使用clone()方法即可.Clone是"克隆"的意思,即制作完全一模一样的副本.这个方法在基础类Object中定义成"protected"(受保护)模式.但在希望克隆的任何衍生类中,必须将其覆盖为"public"模式.例如,标准库类Vector覆盖了clone(),所以能为Vector调用clone(),如

Java 菜单按钮控制绘图程序(求解答,急急急!!!)

问题描述 Java 菜单按钮控制绘图程序(求解答,急急急!!!) 用菜单和按钮来控制绘图 功能要求:实现简单绘图功能,具体要求:画直线,画空心或实心椭圆.画空心或实心矩形.画多边形(图形的位置和大小可在程序中设定). 界面要求:用图形界面实现. 图示一初始界面: 图示二使用菜单绘图: 图示三使用按钮绘图 解决方案 用 myeclipse 10 以上做吧 挺简单的,直接求代码,,不太好吧 解决方案二: http://download.csdn.net/download/wangjinxia00/2

java swing中的弹窗-Java 中如何控制弹出窗口的关闭,类似于QQ的天气的弹窗

问题描述 Java 中如何控制弹出窗口的关闭,类似于QQ的天气的弹窗 本人新手,我想做一个就类似于QQ的天气的弹窗得东西,谁帮我完善一下后面的代码 我的想法是:鼠标移动到label上弹出一个窗口,移出这个label后几秒内判断鼠标移动是否到了这个窗口上,如没有就关闭这个窗口. package com.frame; import java.awt.BorderLayout; import java.awt.Container; import java.awt.GridBagConstraints;

java程序如何控制2台或多台打印机,这些打印机连在同一台计算机上(串口,U口)

问题描述 java程序如何控制2台或多台打印机,这些打印机连在同一台计算机上(串口,U口),求原理,Java代码实现. 解决方案 解决方案二:java...非常难实现,建议用更底层的C解决方案三:这么复杂的问题才20分,楼主小气了.http://wenda.tianya.cn/wenda/thread?tid=6925cb414632d36e解决方案四:只有原理,没有代码.找JNI控制端口的例子,向指定的端口写指定格式的数据流.

Java中log4j控制写入日志开关

我们平时的日志一般使用最多的就是java.util.logger和log4j了. 这里因为要在外部灵活配置,所以我们采用log4j来控制. 首先我们需要log4j.jar和log4j.properties 现在我们在程序中写日志输出 引入import org.apache.log4j.Logger; Logger logger = Logger.getLogger("这里的名字随便取,一般是本类类名,这里是CheckUserServlet"); //Log4j中将要输出的Log信息定义

java布局的控制

在Java里该方法是安一个组件到一个窗体中去,它不同我们使用过的其它GUI系统. 首先,它是全代码的:没有控制安放组件的"资源". 其次,该方法的组件被安放到一个被"布局管理器"控制的窗体中,由"布局管理器"根据我们add()它们的决定来安放组件.大小,形状,组件位置与其它系统的布局管理器显著的不同.另外,布局管理器使我们的程序片或应用程序适合窗口的大小,所以,如果窗口的尺寸改变(例如,在HTML页面的程序片指定的规格),组件的大小,形状和位置都

java序列化的控制

正如大家看到的那样,默认的序列化机制并不难操纵.然而,假若有特殊要求又该怎么办呢?我们可能有特殊的安全问题,不希望对象的某一部分序列化:或者某一个子对象完全不必序列化,因为对象恢复以后,那一部分需要重新创建. 此时,通过实现Externalizable接口,用它代替Serializable接口,便可控制序列化的具体过程.这个Externalizable接口扩展了Serializable,并增添了两个方法:writeExternal()和readExternal().在序列化和重新装配的过程中,会

Java如何从控制台中读取数据

  从控制台中读取数据是一个比较常用的功能,在 JDK 5.0 以前的版本中的实现是比较复杂的,需要手工处理系统的输入流.有意思的是,从 JDK 5.0 版本开始,能从控制台中输入数据的方法每增加一个版本号,就有一种新增的方法,这也增加了选择的种类,可以依据不同的要求来进行选择.下面来看一下,各个版本中如何从控制台中读取数据以及各自的优缺点. 1 JDK 1.4 及以下版本读取的方法 JDK 1.4 及以下的版本中要想从控制台中输入数据只有一种办法,即使用System.in获得系统的输入流,再桥