友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!阅读过程发现任何错误请告诉我们,谢谢!! 报告错误
一世书城 返回本书目录 我的书架 我的书签 TXT全本下载 进入书吧 加入书签

Java编程思想第4版[中文版](PDF格式)-第123章

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!




它不适合在 Java 中使用,为什么呢?在 Java 中,我们操控的一切东西都是句柄,而在C++中,却可以使用 

类似于句柄的东西,也能直接传递对象。这时便要用到C++的副本构建器:只要想获得一个对象,并按值传 

递它,就可以复制对象。所以它在 C++里能很好地工作,但应注意这套机制在Java 里是很不通的,所以不要 

用它。  



12。4 只读类  



尽管在一些特定的场合,由clone()产生的本地副本能够获得我们希望的结果,但程序员(方法的作者)不 

得不亲自禁止别名处理的副作用。假如想制作一个库,令其具有常规用途,但却不能担保它肯定能在正确的 

类中得以克隆,这时又该怎么办呢?更有可能的一种情况是,假如我们想让别名发挥积极的作用——禁止不 

必要的对象复制——但却不希望看到由此造成的副作用,那么又该如何处理呢?  

一个办法是创建“不变对象”,令其从属于只读类。可定义一个特殊的类,使其中没有任何方法能造成对象 

内部状态的改变。在这样的一个类中,别名处理是没有问题的。因为我们只能读取内部状态,所以当多处代 

码都读取相同的对象时,不会出现任何副作用。  

作为“不变对象”一个简单例子,Java 的标准库包含了“封装器”(wrapper )类,可用于所有基本数据类 

型。大家可能已发现了这一点,如果想在一个象Vector (只采用Object 句柄)这样的集合里保存一个 int 

数值,可以将这个 int 封装到标准库的 Integer类内部。如下所示:  

  

//: ImmutableInteger。java  

// The Integer class cannot be changed  

import java。util。*;  

  

public class ImmutableInteger {  

  public static void main(String'' args) {  

    Vector v = new Vector();  

    for(int i = 0; i 《 10; i++)   

      v。addElement(new Integer(i));  

    // But how do you change the int  



                                                                                 369 


…………………………………………………………Page 371……………………………………………………………

    // inside the Integer?  

  }  

} ///:~  

  

Integer类(以及基本的“封装器”类)用简单的形式实现了“不变性”:它们没有提供可以修改对象的方 

法。  

若确实需要一个容纳了基本数据类型的对象,并想对基本数据类型进行修改,就必须亲自创建它们。幸运的 

是,操作非常简单:  

  

//: MutableInteger。java  

// A changeable wrapper class  

import java。util。*;  

  

class IntValue {   

  int n;  

  IntValue(int x) { n = x; }  

  public String toString() {   

    return Integer。toString(n);  

  }  

}  

  

public class MutableInteger {  

  public static void main(String'' args) {  

    Vector v = new Vector();  

    for(int i = 0; i 《 10; i++)   

      v。addElement(new IntValue(i));  

    System。out。println(v);  

    for(int i = 0; i 《 v。size(); i++)  

      ((IntValue)v。elementAt(i))。n++;  

    System。out。println(v);  

  }  

} ///:~  

  

注意n 在这里简化了我们的编码。  

若默认的初始化为零已经足够(便不需要构建器),而且不用考虑把它打印出来(便不需要 toString ),那 

么 IntValue 甚至还能更加简单。如下所示:  

class IntValue { int n; }  

将元素取出来,再对其进行造型,这多少显得有些笨拙,但那是Vector 的问题,不是IntValue 的错。  



12。4。1 创建只读类  



完全可以创建自己的只读类,下面是个简单的例子:  

  

//: Immutable1。java  

// Objects that cannot be modified  

// are immune to aliasing。  

  

public class Immutable1 {  

  private int data;  

  public Immutable1(int initVal) {  

    data = initVal;  

  }  

  public int read() { return data; }  



                                                                                             370 


…………………………………………………………Page 372……………………………………………………………

  public boolean nonzero() { return data != 0; }  

  public Immutable1 quadruple() {  

    return new Immutable1(data * 4);  

  }  

  static void f(Immutable1 i1) {  

    Immutable1 quad = i1。quadruple();  

    System。out。println(〃i1 = 〃 + i1。read());  

    System。out。println(〃quad = 〃 + quad。read());  

  }  

  public static void main(String'' args)  {  

    Immutable1 x = new Immutable1(47);  

    System。out。println(〃x = 〃 + x。read());  

    f(x);  

    System。out。println(〃x = 〃 + x。read());  

  }  

} ///:~  

  

所有数据都设为private,可以看到没有任何public 方法对数据作出修改。事实上,确实需要修改一个对象 

的方法是quadruple(),但它的作用是新建一个Immutable1 对象,初始对象则是原封未动的。  

方法 f()需要取得一个 Immutable1对象,并对其采取不同的操作,而 main()的输出显示出没有对x 作任何修 

改。因此,x 对象可别名处理许多次,不会造成任何伤害,因为根据 Immutable1类的设计,它能保证对象不 

被改动。  



12。4。2  “一成不变”的弊端  



从表面看,不变类的建立似乎是一个好方案。但是,一旦真的需要那种新类型的一个修改的对象,就必须辛 

苦地进行新对象的创建工作,同时还有可能涉及更频繁的垃圾收集。对有些类来说,这个问题并不是很大。 

但对其他类来说(比如 String 类),这一方案的代价显得太高了。  

为解决这个问题,我们可以创建一个“同志”类,并使其能够修改。以后只要涉及大量的修改工作,就可换 

为使用能修改的同志类。完事以后,再切换回不可变的类。  

因此,上例可改成下面这个样子:  

  

//: Immutable2。java  

// A panion class for making changes  

// to immutable objects。  

  

class Mutable {  

  private int data;  

  public Mutable(int initVal) {  

    data = initVal;  

  }  

  public Mutable add(int x) {   

    data += x;  

    return this;  

  }  

  public Mutable multiply(int x) {  

    data *= x;  

    return this;  

  }  

  public Immutable2 makeImmutable2() {  

    return new Immutable2(data);  

  }  

}  



                                                                                             371 


…………………………………………………………Page 373……………………………………………………………

  

public class Immutable2 {  

  private int data;  

  public Immutable2(int initVal) {  

    data = initVal;  

  }  

  public int read() { return data; }  

  public boolean nonzero() { return data != 0; }  

  public Immutable2 add(int x) {   

    return new Immutable2(data + x);  

  }  

  public Immutable2 multiply(int x) {  

    return new Immutable2(data * x);  

  }  

  public Mutable makeMutable() {  

    return new Mutable(data);  

  }  

  public static Immutable2 modify1(Immutable2 y){  

    Immutable2 val = y。add(12);  

    val = val。multiply(3);  

    val = val。add(11);  

    val = val。multiply(2);  

    return val;  

  }  

  // This produces the same result:  

  public static Immutable2 modify2(Immutable2 y){  

    Mutable m = y。makeMutable();  

    m。add(12)。multiply(3)。add(11)。multiply(2);  

    return m。makeImmutable2();  

  }  

  public static void main(String'' args) {  

    Immutable2 i2 = new Immutable2(47);  

    Immutable2 r1 = modify1(i2);  

    Immutable2 r2 = modify2(i2);  

    System。out。println(〃i2 = 〃 + i2。read());  

    System。out。println(〃r1 = 〃 + r1。read());  

    System。out。println(〃r2 = 〃 + r2。read());  

  }  

} ///:~  

  

和往常一样,Immutable2 包含的方法保留了对象不可变的特征,只要涉及修改,就创建新的对象。完成这些 

操作的是add()和multiply()方法。同志类叫作 Mutable,它也含有 add()和 multiply()方法。但这些方法 

能够修改Mutable 对象,而不是新建一个。除此以外,Mutable 的一个方法可用它的数据产生一个 

Immutable2对象,反之亦然。  

两个静态方法modify1()和 modify2()揭示出获得同样结果的两种不同方法。在 modify1()中,所有工作都是 

在 Immutable2 类中完成的,我们可看到在进程中创建了四个新的 Immutable2 对象(而且每次重新分配了 

val,前一个对象就成为垃圾)。  

在方法modify2()中,可看到它的第一个行动是获取 Immutable2 y,然后从中生成一个Mutable (类似于前 

面对 clone()的调用,但这一次创建了一个不同类型的对象)。随后,用Mutable 对象进行大量修改操作, 

同时用不着新建许多对象。最后,它切换回Immutable2。在这里,我们只创建了两个新对象(Mutable 和 

Immutable2 的结果),而不是四个。  

这一方法特别适合在下述场合应用:  



                                                                                          372 


…………………………………………………………Page 374……………………………………………………………

(1) 需要不可变的对象,而且  

(2) 经常需要进行大量修改,或者  

(3) 创建新的不变对象代价太高  



12。4。3 不变字串  



请观察下述代码:  

  

//: Stringer。java  

  

public class Stringer {  

  static String upcase(String s) {  

    return s。toUpperCase()
返回目录 上一页 下一页 回到顶部 0 0
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!