|
1 | 1 | [TOC]
|
2 | 2 |
|
3 | 3 | <!-- Housekeeping -->
|
| 4 | + |
4 | 5 | # 第六章 初始化和清理
|
5 | 6 |
|
| 7 | +"不安全"的编程是造成编程代价昂贵的罪魁祸首之一。有两个安全性问题:初始化和清理。C 语言中很多的 bug 都是因为程序员忘记初始化导致的。尤其是很多类库的使用者不知道如何初始化类库组件,甚至他们必须得去初始化。清理则是另一个特殊的问题,因为当你使用一个元素做完事后就不会去关心这个元素,所以你很容易忘记清理它。这样就造成了元素使用的资源滞留不会被回收,直到程序消耗完所有的资源(特别是内存)。 |
| 8 | + |
| 9 | +C++ 引入了构造器的概念,这是一个特殊的方法,每创建一个对象,这个方法就会被自动调用。Java 采用了构造器的概念,另外还使用了垃圾收集器(Garbage Collector, GC)去自动回收不再被使用的对象所占的资源。这一章将讨论初始化和清理的问题,以及在 Java 中对它们的支持。 |
6 | 10 |
|
7 | 11 | <!-- Guaranteed Initialization with the Constructor -->
|
| 12 | + |
8 | 13 | ## 利用构造器保证初始化
|
9 | 14 |
|
| 15 | +你可能想为每个类创建一个 `initialize()` 方法,该方法名暗示着在使用类之前需要先调用它。不幸的是,用户必须得记得去调用它。在 Java 中,类的设计者通过构造器保证每个对象的初始化。如果一个类有构造器,那么 Java 会在用户使用对象之前(即对象刚创建完成)自动调用对象的构造器方法,从而保证初始化。下个挑战是如何命名构造器方法。存在两个问题:第一个是任何命名都可能与类中其他已有元素的命名冲突;第二个是编译器必须始终知道构造器方法名称,从而调用它。C++ 的解决方法看起来是最简单且最符合逻辑的,所以 Java 中使用了同样的方式:构造器名称与类名相同。在初始化过程中自动调用构造器方法是有意义的。 |
| 16 | + |
| 17 | +以下示例是包含了一个构造器的类: |
| 18 | + |
| 19 | +```java |
| 20 | +// housekeeping/SimpleConstructor.java |
| 21 | +// Demonstration of a simple constructor |
| 22 | + |
| 23 | +class Rock { |
| 24 | + Rock() { // 这是一个构造器 |
| 25 | + System.out.print("Rock "); |
| 26 | + } |
| 27 | +} |
| 28 | + |
| 29 | +public class SimpleConstructor { |
| 30 | + public static void main(String[] args) { |
| 31 | + for (int i = 0; i < 10; i++) { |
| 32 | + new Rock(); |
| 33 | + } |
| 34 | + } |
| 35 | +} |
| 36 | +``` |
| 37 | + |
| 38 | +输出: |
| 39 | + |
| 40 | +````java |
| 41 | +Rock Rock Rock Rock Rock Rock Rock Rock Rock Rock |
| 42 | +```` |
| 43 | + |
| 44 | +现在,当创建一个对象时:`new Rock()` ,内存被分配,构造器被调用。构造器保证了对象在你使用它之前进行了正确的初始化。 |
| 45 | + |
| 46 | +有一点需要注意,构造器方法名与类名相同,不需要符合首字母小写的编程风格。在 C++ 中,无参构造器被称为默认构造器,这个术语在 Java 出现之前使用了很多年。但是,出于一些原因,Java 设计者们决定使用无参构造器这个名称,我(作者)认为这种叫法笨拙而且没有必要,所以我打算继续使用默认构造器。Java 8 引入了 **default** 关键字修饰方法,所以算了,我还是用无参构造器的叫法吧。 |
| 47 | + |
| 48 | +跟其他方法一样,构造器方法也可以传入参数来定义如何创建一个对象。之前的例子稍作修改,使得构造器接收一个参数: |
| 49 | + |
| 50 | +```java |
| 51 | +// housekeeping/SimpleConstructor2.java |
| 52 | +// Constructors can have arguments |
| 53 | + |
| 54 | +class Rock2 { |
| 55 | + Rock2(int i) { |
| 56 | + System.out.print("Rock " + i + " "); |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +public class SimpleConstructor2 { |
| 61 | + public static void main(String[] args) { |
| 62 | + for (int i = 0; i < 8; i++) { |
| 63 | + new Rock2(i); |
| 64 | + } |
| 65 | + } |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +输出: |
| 70 | + |
| 71 | +```java |
| 72 | +Rock 0 Rock 1 Rock 2 Rock 3 Rock 4 Rock 5 Rock 6 Rock 7 |
| 73 | +``` |
| 74 | + |
| 75 | +如果类 **Tree** 有一个构造方法,只接收一个参数用来表示树的高度,那么你可以像下面这样创建一棵树: |
| 76 | + |
| 77 | +```java |
| 78 | +Tree t = new Tree(12); // 12-foot 树 |
| 79 | +``` |
| 80 | + |
| 81 | +如果 **Tree(int)** 是唯一的构造器,那么编译器就不允许你以其他任何方式创建 **Tree** 类型的对象。 |
| 82 | + |
| 83 | +构造器消除了一类重要的问题,使得代码更易读。例如,在上面的代码块中,你看不到对 `initialize()` 方法的显式调用,而从概念上来看,`initialize()` 方法应该与对象的创建分离。在 Java 中,对象的创建与初始化是统一的概念,二者不可分割。 |
| 84 | + |
| 85 | +构造器是一种特殊的方法,因为它没有返回值。这与返回 **void** 值的方法不同,在返回 **void** 值的方法中,方法返回空值,但是你还是有选择返回一些其他值。构造器返回空值,你没有选择(**new** 表达式的确返回了新创建对象的引用,但是构造器自身并没有返回值 )。假如有返回值,而且你可以自由选择,那么编译器得知道如何去处理这个返回值。 |
10 | 86 |
|
11 | 87 | <!-- Method Overloading -->
|
| 88 | + |
12 | 89 | ## 方法重载
|
13 | 90 |
|
| 91 | +任何编程语言中都具备的一项重要特性就是命名。当你创建一个对象时,就会给此对象分配的内存空间命名。方法是行为的命名。你通过名字指代所有的对象,属性和方法。良好命名的系统易于理解和修改。就好比写散文——目的是与读者沟通。 |
| 92 | + |
| 93 | +将人类语言细微的差别映射到编程语言中会产生一个问题。通常,相同的词可以表达多种不同的含义——它们被"重载"了。特别是当含义的差别很小时,这会更加有用。你会说"清洗衬衫"、"清洗车"和"清洗狗"。而如果硬要这么说就会显得很愚蠢:"以洗衬衫的方式洗衬衫"、"以洗车的方式洗车"和"以洗狗的方式洗狗",因为听众根本不需要区分行为的动作。大多数人类语言都具有"冗余"性,所以即使漏掉几个词,你也能明白含义。你不需要对每个概念都使用不同的词汇——可以从上下文推断出含义。 |
| 94 | + |
| 95 | +大多数编程语言(尤其是 C 语言)要求为每个方法(在这些语言中经常称为方法)提供一个独一无二的标识符。 |
14 | 96 |
|
15 | 97 | <!-- No-arg Constructors -->
|
| 98 | + |
16 | 99 | ## 无参构造器
|
17 | 100 |
|
18 | 101 |
|
|
31 | 114 | <!-- Constructor Initialization -->
|
32 | 115 | ## 构造器初始化
|
33 | 116 |
|
34 |
| - |
35 | 117 | <!-- Array Initialization -->
|
| 118 | + |
36 | 119 | ## 数组初始化
|
37 | 120 |
|
38 | 121 |
|
|
43 | 126 | <!-- Summary -->
|
44 | 127 | ## 本章小结
|
45 | 128 |
|
46 |
| - |
47 | 129 | <!-- 分页 -->
|
| 130 | + |
48 | 131 | <div style="page-break-after: always;"></div>
|
0 commit comments