按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 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()