Skip to content

Commit 88af685

Browse files
authored
"受保护",有歧义,并且不易于理解。原文对protected使用了加粗字体,表明这是个一个关键字,并非字面意思受保护。 (lingcoder#605)
* "受保护",有歧义,并且不易于理解。原文对protected使用了加粗字体,表明这是个一个关键字,并非字面意思受保护。 * Markdown语法错误 * Markdown语法错误 * Markdown语法错误 * 没读懂原来的翻译是什么意思。 * 错句 * 没读懂原来的翻译是什么意思,这样改或许更好。 * 语义错误
1 parent 35a3271 commit 88af685

File tree

1 file changed

+8
-8
lines changed

1 file changed

+8
-8
lines changed

docs/book/08-Reuse.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ Cleanser dilute() apply() scrub()
214214

215215
在这里,`Detergent.main()` 显式地调用 `Cleanser.main()`,从命令行传递相同的参数(当然,你可以传递任何字符串数组)。
216216

217-
**Cleanser** 中的所有方法都是公开的。请记住,如果不使用任何访问修饰符,则成员默认为包访问权限,这只允许包内成员访问。因此,如果没有访问修饰符,那么包内的任何人都可以使用这些方法。例如,**Detergent** 就没有问题。但是,如果其他包中的类继承 **Cleanser**,则该类只能访问 **Cleanser** 的公共成员。因此,为了允许继承,一般规则是所有字段为私有,所有方法为公共。(受保护成员也允许派生类访问;你以后会知道的。)在特定的情况下,你必须进行调整,但这是一个有用的指南。
217+
**Cleanser** 中的所有方法都是公开的。请记住,如果不使用任何访问修饰符,则成员默认为包访问权限,这只允许包内成员访问。因此,如果没有访问修饰符,那么包内的任何人都可以使用这些方法。例如,**Detergent** 就没有问题。但是,如果其他包中的类继承 **Cleanser**,则该类只能访问 **Cleanser** 的公共成员。因此,为了允许继承,一般规则是所有字段为私有,所有方法为公共。(**protected**成员也允许派生类访问;你以后会知道的。)在特定的情况下,你必须进行调整,但这是一个有用的指南。
218218

219219
**Cleanser** 的接口中有一组方法: `append()``dilute()``apply()``scrub()``toString()`。因为 **Detergent** 是从 **Cleanser** 派生的(通过 **extends** 关键字),所以它会在其接口中自动获取所有这些方法,即使你没有在 **Detergent** 中看到所有这些方法的显式定义。那么,可以把继承看作是复用类。如在 `scrub()` 中所见,可以使用基类中定义的方法并修改它。在这里,你可以在新类中调用基类的该方法。但是在 `scrub()` 内部,不能简单地调用 `scrub()`,因为这会产生递归调用。为了解决这个问题,Java的 **super** 关键字引用了当前类继承的“超类”(基类)。因此表达式 `super.scrub()` 调用方法 `scrub()` 的基类版本。
220220

@@ -359,7 +359,7 @@ DerivedSpaceShip extends SpaceShipControls {
359359

360360
```
361361

362-
然而, **DerivedSpaceShip** 并不是真正的“一种” **SpaceShipControls** ,即使你“告诉” **DerivedSpaceShip** 调用 `forward()`。更准确地说,一艘宇宙飞船包含了 **SpaceShipControls **,同时 **SpaceShipControls** 中的所有方法都暴露在宇宙飞船中。委托解决了这个难题:
362+
然而, **DerivedSpaceShip** 并不是真正的“一种” **SpaceShipControls** ,即使你“告诉” **DerivedSpaceShip** 调用 `forward()`。更准确地说,一艘宇宙飞船包含了 **SpaceShipControls**,同时 **SpaceShipControls** 中的所有方法都暴露在宇宙飞船中。委托解决了这个难题:
363363

364364
```java
365365
// reuse/SpaceShipDelegation.java
@@ -507,7 +507,7 @@ PlaceSetting constructor
507507

508508
### 保证适当的清理
509509

510-
Java 没有 C++ 中析构函数的概念,析构函数是在对象被销毁时自动调用的方法。原因可能是,在Java中,通常是忘掉而不是销毁对象,从而允许垃圾收集器根据需要回收内存。通常这是可以的,但是有时你的类可能在其生命周期中执行一些需要清理的活动。初始化和清理章节提到,你无法知道垃圾收集器何时会被调用,甚至它是否会被调用。因此,如果你想为类清理一些东西,必须显式地编写一个特殊的方法来完成它,并确保客户端程序员知道他们必须调用这个方法。最重要的是——正如在"异常"章节中描述的——你必须通过在 **finally **子句中放置此类清理来防止异常。
510+
Java 没有 C++ 中析构函数的概念,析构函数是在对象被销毁时自动调用的方法。原因可能是,在Java中,通常是忘掉而不是销毁对象,从而允许垃圾收集器根据需要回收内存。通常这是可以的,但是有时你的类可能在其生命周期中执行一些需要清理的活动。初始化和清理章节提到,你无法知道垃圾收集器何时会被调用,甚至它是否会被调用。因此,如果你想为类清理一些东西,必须显式地编写一个特殊的方法来完成它,并确保客户端程序员知道他们必须调用这个方法。最重要的是——正如在"异常"章节中描述的——你必须通过在 **finally**子句中放置此类清理来防止异常。
511511

512512
请考虑一个在屏幕上绘制图片的计算机辅助设计系统的例子:
513513

@@ -692,7 +692,7 @@ doh(Milhouse)
692692

693693
**Homer** 的所有重载方法在 **Bart** 中都是可用的,尽管 **Bart** 引入了一种新的重载方法。在下一章中你将看到,使用与基类中完全相同的签名和返回类型覆盖相同名称的方法要常见得多。否则就会令人困惑。
694694

695-
你已经看到了Java 5 **@Override **注释,它不是关键字,但是可以像使用关键字一样使用它。当你打算重写一个方法时,你可以选择添加这个注释,如果你不小心用了重载而不是重写,编译器会产生一个错误消息:
695+
你已经看到了Java 5 **@Override**注释,它不是关键字,但是可以像使用关键字一样使用它。当你打算重写一个方法时,你可以选择添加这个注释,如果你不小心用了重载而不是重写,编译器会产生一个错误消息:
696696

697697
```java
698698
// reuse/Lisa.java
@@ -1161,17 +1161,17 @@ public class Jurassic {
11611161
}
11621162
```
11631163

1164-
**final** 类的属性可以根据个人选择是或不是 **final**这同样适用于不管类是否是 **final** 的内部 **final** 属性。然而,由于 **final** 类禁止继承,类中所有的方法都被隐式地指定为 **final**,所以没有办法覆写它们。你可以在 final 类中的方法加上 **final** 修饰符,但不会增加任何意义。
1164+
**final** 类的属性可以根据个人选择是或不是 **final**同样,非 **final** 类的属性也可以根据个人选择是或不是 **final**。然而,由于 **final** 类禁止继承,类中所有的方法都被隐式地指定为 **final**,所以没有办法覆写它们。你可以在 final 类中的方法加上 **final** 修饰符,但不会增加任何意义。
11651165

11661166
### final 忠告
11671167

11681168
在设计类时将一个方法指明为 final 看上去是明智的。你可能会觉得没人会覆写那个方法。有时这是对的。
11691169

11701170
但请留意你的假设。通常来说,预见一个类如何被复用是很困难的,特别是通用类。如果将一个方法指定为 **final**,可能会防止其他程序员的项目中通过继承来复用你的类,而这仅仅是因为你没有想到它被以那种方式使用。
11711171

1172-
Java 标准类库就是一个很好的例子。尤其是 Java 1.0/1.1 的 **Vector** 类被广泛地使用,而且从效率考虑(这近乎是个幻想),如果它的所有方法没有被指定为 **final**,可能会更加有用。很容易想到,你可能会继承并覆写这么一个基础类,但是设计者们认为这么做不合适。有两个讽刺的原因。第一,**Stack** 继承自 **Vector**,就是说 **Stack** 是个 **Vector**,但从逻辑上来说不对。尽管如此,Java 设计者们仍然这么做,在用这种方式创建 **Stack** 时,他们应该意识到了 **final** 方法过于约束。
1172+
Java 标准类库就是一个很好的例子。尤其是 Java 1.0/1.1 的 **Vector** 类被广泛地使用,如果它的所有方法没有因为从效率考虑(这近乎是个幻想),而被指定为 **final**,可能会更加有用。很容易想到,你可能会继承并覆写这么一个基础类,但是设计者们认为这么做不合适。有两个讽刺的原因。第一,**Stack** 继承自 **Vector**,就是说 **Stack** 是个 **Vector**,但从逻辑上来说不对。尽管如此,Java 设计者们仍然这么做,在用这种方式创建 **Stack** 时,他们应该意识到了 **final** 方法过于约束。
11731173

1174-
第二,**Vector** 中的很多重要方法,比如 `addElement()``elementAt()` 方法都是同步的。在“并发编程”一章中会看同步会导致很大的执行开销,可能会抹煞 **final** 带来的好处。这加强了程序员永远无法正确猜到优化应该发生在何处的观点。如此笨拙的设计却出现在每个人都要使用的标准库中,太糟糕了。庆幸的是,现代 Java 容器用 **ArrayList** 代替了 **Vector**,它的行为要合理得多。不幸的是,仍然有很多新代码使用旧的集合类库,其中就包括 **Vector**
1174+
第二,**Vector** 中的很多重要方法,比如 `addElement()``elementAt()` 方法都是同步的。在“并发编程”一章中会看到同步会导致很大的执行开销,可能会抹煞 **final** 带来的好处。这加强了程序员永远无法正确猜到优化应该发生在何处的观点。如此笨拙的设计却出现在每个人都要使用的标准库中,太糟糕了。庆幸的是,现代 Java 容器用 **ArrayList** 代替了 **Vector**,它的行为要合理得多。不幸的是,仍然有很多新代码使用旧的集合类库,其中就包括 **Vector**
11751175

11761176
Java 1.0/1.1 标准类库中另一个重要的类是 **Hashtable**(后来被 **HashMap** 取代),它不含任何 **final** 方法。本书中其他地方也提到,很明显不同的类是由不同的人设计的。**Hashtable** 就比 **Vector** 中的方法名简洁得多,这又是一条证据。对于类库的使用者来说,这是一个本不应该如此草率的事情。这种不规则的情况造成用户需要做更多的工作——这是对粗糙的设计和代码的又一讽刺。
11771177

@@ -1242,7 +1242,7 @@ j = 39
12421242

12431243
如果基类还存在自身的基类,那么第二个基类也将被加载,以此类推。接下来,根基类(例子中根基类是 **Insect**)的 **static** 的初始化开始执行,接着是派生类,以此类推。这点很重要,因为派生类中 **static** 的初始化可能依赖基类成员是否被正确地初始化。
12441244

1245-
至此,必要的类都加载完毕,可以创建对象了。首先,对象中的所有基本类型变量都被置为默认值,对象引用被设为 **null** —— 这是通过将对象内存设为二进制零值一举生成的。接着会调用基类的构造器。本例中是自动调用的,但是你也可以使用 **super** 调用指定的基类构造器(在 **Beetle** 构造器中的第一步操作)。基类构造器和派生类构造器一样以相同的顺序经历相同的过程。当基类构造器完成后,实例变量按文本顺序初始化。最终,构造器的剩余部分被执行。
1245+
至此,必要的类都加载完毕,对象可以被创建了。首先,对象中的所有基本类型变量都被置为默认值,对象引用被设为 **null** —— 这是通过将对象内存设为二进制零值一举生成的。接着会调用基类的构造器。本例中是自动调用的,但是你也可以使用 **super** 调用指定的基类构造器(在 **Beetle** 构造器中的第一步操作)。基类构造器和派生类构造器一样以相同的顺序经历相同的过程。当基类构造器完成后,实例变量按文本顺序初始化。最终,构造器的剩余部分被执行。
12461246

12471247
<!-- Summary -->
12481248

0 commit comments

Comments
 (0)