到目前为止,本书已多次使用组合技术。你仅需将对象引用置于新类之中即可。例如,假设
你需要某个对象,它需要具有多个 string 对象、两三个基本类型数据、以及另一个类的对象。
对于非基本类型的对象,你必须将其引用置于新的类中,而现在,你只需直接定义基本类型
数据:
//: c06:SprinklerSystem.java
// Composition for code reuse.
import com.bruceeckel.simpletest.*;
class WaterSource {
private String s;
WaterSource() {
System.out.println("WaterSource()");
s = new String("Constructed");
}
public String toString() { return s; }
}
public class SprinklerSystem {
private static Test monitor = new Test();
private String valve1, valve2, valve3, valve4;
private WaterSource source;
private int i;
private float f;
public String toString() {
return
"valve1 = " + valve1 + "\n" +
"valve2 = " + valve2 + "\n" +
"valve3 = " + valve3 + "\n" +
"valve4 = " + valve4 + "\n" +
"i = " + i + "\n" +
"f = " + f + "\n" +
"source = " + source;
}
public static void main(String[] args) {
SprinklerSystem sprinklers = new SprinklerSystem();
System.out.println(sprinklers);
monitor.expect(new String[] {
"valve1 = null",
"valve2 = null",
"valve3 = null",
"valve4 = null",
"i = 0",
"f = 0.0",
"source = null"
});
}
} ///:~
在上面两个类所定义的方法中,有一个很特殊:toString( )。不久你将会了解到每一个非基
本类型的对象都有一个 toString( )方法,而且当编译器需要一个 string 而你却只有一个
对象时,该方法便会被调用。所以在 sprinklerSystem.toString( )的表达式中:
"source = " + source;
编译器将会得知你想要将一个 string 对象同 watersource 相加。由于你只能将一个
string 和另一个 string 相加,因此编译器会告诉你:“我将调用 toString( ),把 source
转换成为一个 string!”这样做之后,它就能够将两个 string 连接到一起并将结果传递
给 System.out.println( )。每当你想要使你所创建的类具备这样的行为时,你仅需要
编写一个 toString( )方法即可。
正如我们在第 2 章中所提到的,类中的基本类型数据能够自动被初始化为零。但是对象引
用会被初始化为 null,而且如果你试图为它们调用任何方法,都会得到一个异常
(exception)。如果我们可以在不出现异常的前提下将其内容打印出来,将会是件有益并且
有用的事情。
编译器并不是简单地为每一个引用都创建缺省对象,这一点是很有意义的,因为真要是那样
做的话,就会在许多情况下增加不必要的负担。如果你想初始化这些引用,可以在代码中的
下列位置进行:
1. 在定义对象的地方。这意味着它们总是能够在构造器被调用之前被初始化。
2. 在类的构造器中。
3. 就 在 你 确 实 需 要 使 用 这 些 对 象 之 前 。 这 种 方 式 被 称 为 “ 惰 性 初 始 化 (lazy
initialization)”。在不必每次都生成对象的情况下,这种方式可以减少额外的负担。
以下是三种示例:
//: c06:Bath.java
// Constructor initialization with composition.
import com.bruceeckel.simpletest.*;
class Soap {
private String s;
Soap() {
System.out.println("Soap()");
s = new String("Constructed");
}
public String toString() { return s; }
}
public class Bath {
private static Test monitor = new Test();
private String // Initializing at point of definition:
s1 = new String("Happy"),
s2 = "Happy",
s3, s4;
private Soap castille;
private int i;
private float toy;
public Bath() {
System.out.println("Inside Bath()");
s3 = new String("Joy");
i = 47;
toy = 3.14f;
castille = new Soap();
public String toString() {
if(s4 == null) // Delayed initialization:
s4 = new String("Joy");
return
"s1 = " + s1 + "\n" +
"s2 = " + s2 + "\n" +
"s3 = " + s3 + "\n" +
"s4 = " + s4 + "\n" +
"i = " + i + "\n" +
"toy = " + toy + "\n" +
"castille = " + castille;
}
public static void main(String[] args) {
Bath b = new Bath();
System.out.println(b);
monitor.expect(new String[] {
"Inside Bath()",
"Soap()",
"s1 = Happy",
"s2 = Happy",
"s3 = Joy",
"s4 = Joy",
"i = 47",
"toy = 3.14",
"castille = Constructed"
});
}
} ///:~
请注意,在 Bath 的构造器中,有一行语句在所有初始化产生之前就已经执行了。如果你没
有在定义处初始化,那么除非发生了不可避免的运行期异常,否则将不能保证信息在发送给
对象引用之前已经被初始化。
当 toString ( )被调用时,它将填充 s4 的值,以确保所有的数据成员(fields)在被使用之时
你需要某个对象,它需要具有多个 string 对象、两三个基本类型数据、以及另一个类的对象。
对于非基本类型的对象,你必须将其引用置于新的类中,而现在,你只需直接定义基本类型
数据:
//: c06:SprinklerSystem.java
// Composition for code reuse.
import com.bruceeckel.simpletest.*;
class WaterSource {
private String s;
WaterSource() {
System.out.println("WaterSource()");
s = new String("Constructed");
}
public String toString() { return s; }
}
public class SprinklerSystem {
private static Test monitor = new Test();
private String valve1, valve2, valve3, valve4;
private WaterSource source;
private int i;
private float f;
public String toString() {
return
"valve1 = " + valve1 + "\n" +
"valve2 = " + valve2 + "\n" +
"valve3 = " + valve3 + "\n" +
"valve4 = " + valve4 + "\n" +
"i = " + i + "\n" +
"f = " + f + "\n" +
"source = " + source;
}
public static void main(String[] args) {
SprinklerSystem sprinklers = new SprinklerSystem();
System.out.println(sprinklers);
monitor.expect(new String[] {
"valve1 = null",
"valve2 = null",
"valve3 = null",
"valve4 = null",
"i = 0",
"f = 0.0",
"source = null"
});
}
} ///:~
在上面两个类所定义的方法中,有一个很特殊:toString( )。不久你将会了解到每一个非基
本类型的对象都有一个 toString( )方法,而且当编译器需要一个 string 而你却只有一个
对象时,该方法便会被调用。所以在 sprinklerSystem.toString( )的表达式中:
"source = " + source;
编译器将会得知你想要将一个 string 对象同 watersource 相加。由于你只能将一个
string 和另一个 string 相加,因此编译器会告诉你:“我将调用 toString( ),把 source
转换成为一个 string!”这样做之后,它就能够将两个 string 连接到一起并将结果传递
给 System.out.println( )。每当你想要使你所创建的类具备这样的行为时,你仅需要
编写一个 toString( )方法即可。
正如我们在第 2 章中所提到的,类中的基本类型数据能够自动被初始化为零。但是对象引
用会被初始化为 null,而且如果你试图为它们调用任何方法,都会得到一个异常
(exception)。如果我们可以在不出现异常的前提下将其内容打印出来,将会是件有益并且
有用的事情。
编译器并不是简单地为每一个引用都创建缺省对象,这一点是很有意义的,因为真要是那样
做的话,就会在许多情况下增加不必要的负担。如果你想初始化这些引用,可以在代码中的
下列位置进行:
1. 在定义对象的地方。这意味着它们总是能够在构造器被调用之前被初始化。
2. 在类的构造器中。
3. 就 在 你 确 实 需 要 使 用 这 些 对 象 之 前 。 这 种 方 式 被 称 为 “ 惰 性 初 始 化 (lazy
initialization)”。在不必每次都生成对象的情况下,这种方式可以减少额外的负担。
以下是三种示例:
//: c06:Bath.java
// Constructor initialization with composition.
import com.bruceeckel.simpletest.*;
class Soap {
private String s;
Soap() {
System.out.println("Soap()");
s = new String("Constructed");
}
public String toString() { return s; }
}
public class Bath {
private static Test monitor = new Test();
private String // Initializing at point of definition:
s1 = new String("Happy"),
s2 = "Happy",
s3, s4;
private Soap castille;
private int i;
private float toy;
public Bath() {
System.out.println("Inside Bath()");
s3 = new String("Joy");
i = 47;
toy = 3.14f;
castille = new Soap();
public String toString() {
if(s4 == null) // Delayed initialization:
s4 = new String("Joy");
return
"s1 = " + s1 + "\n" +
"s2 = " + s2 + "\n" +
"s3 = " + s3 + "\n" +
"s4 = " + s4 + "\n" +
"i = " + i + "\n" +
"toy = " + toy + "\n" +
"castille = " + castille;
}
public static void main(String[] args) {
Bath b = new Bath();
System.out.println(b);
monitor.expect(new String[] {
"Inside Bath()",
"Soap()",
"s1 = Happy",
"s2 = Happy",
"s3 = Joy",
"s4 = Joy",
"i = 47",
"toy = 3.14",
"castille = Constructed"
});
}
} ///:~
请注意,在 Bath 的构造器中,有一行语句在所有初始化产生之前就已经执行了。如果你没
有在定义处初始化,那么除非发生了不可避免的运行期异常,否则将不能保证信息在发送给
对象引用之前已经被初始化。
当 toString ( )被调用时,它将填充 s4 的值,以确保所有的数据成员(fields)在被使用之时
已被妥善初始化了。