@@ -970,16 +970,203 @@ fd1: i4 = 15, INT_5 = 18
970970fd2: i4 = 13, INT_5 = 18
971971```
972972
973- 因为 ** valueOne** 和 ** VALUE_TWO** 都是带有编译时值的 ** final** 基本类型,它们都可用作编译时常量,没有多大区别。
973+ 因为 ** valueOne** 和 ** VALUE_TWO** 都是带有编译时值的 ** final** 基本类型,它们都可用作编译时常量,没有多大区别。** VALUE_THREE** 是一种更加典型的常量定义的方式:** public** 意味着可以在包外访问,** static** 强调只有一个,** final** 说明是一个常量。
974+
975+ 按照惯例,带有恒定初始值的 ** final** ** static** 基本变量(即编译时常量)命名全部使用大写,单词之间用下划线分隔。(源于 C 语言中定义常量的方式。)
976+
977+ 我们不能因为某数据被 ** final** 修饰就认为在编译时可以知道它的值。由上例中的 ** i4** 和 ** INT_5** 可以看出,它们在运行时才会赋值随机数。示例部分也展示了将 ** final** 值定义为 ** static** 和非 ** static** 的区别。此区别只有当值在运行时被初始化时才会显现,因为编译器对编译时数值一视同仁。(而且编译时数值可能因优化而消失。)当运行程序时就能看到这个区别。注意到 ** fd1** 和 ** fd2** 的 ** i4** 值不同,但 ** INT_5** 的值并没有因为创建了第二个 ** FinalData** 对象而改变,这是因为它是 ** static** 的,在加载时已经被初始化,并不是每次创建新对象时都初始化。
978+
979+ ** v1** 到 ** VAL_3** 变量说明了 ** final** 引用的意义。正如你在 ` main() ` 中所见,** v2** 是 ** final** 的并不意味着你不能修改它的值。因为它是引用,所以只是说明它不能指向一个新的对象。这对于数组具有同样的意义,数组只不过是另一种引用。(我不知道有什么方法能使数组引用本身成为 ** final** 。)看起来,声明引用为 ** final** 没有声明基本类型 ** final** 有用。
980+
981+ ### 空白 final
982+
983+ 空白 final 指的是没有初始化值的 ** final** 属性。编译器确保空白 final 在使用前必须被初始化。这样既能使一个类的每个对象的 ** final** 属性值不同,也能保持它的不变性。
984+
985+ ``` java
986+ // reuse/BlankFinal.java
987+ // "Blank" final fields
988+ class Poppet {
989+ private int i;
990+
991+ Poppet (int ii ) {
992+ i = ii;
993+ }
994+ }
995+
996+ public class BlankFinal {
997+ private final int i = 0 ; // Initialized final
998+ private final int j; // Blank final
999+ private final Poppet p; // Blank final reference
1000+ // Blank finals MUST be initialized in constructor
1001+ public BlankFinal () {
1002+ j = 1 ; // Initialize blank final
1003+ p = new Poppet (1 ); // Init blank final reference
1004+ }
1005+
1006+ public BlankFinal (int x ) {
1007+ j = x; // Initialize blank final
1008+ p = new Poppet (x); // Init blank final reference
1009+ }
1010+
1011+ public static void main (String [] args ) {
1012+ new BlankFinal ();
1013+ new BlankFinal (47 );
1014+ }
1015+ }
1016+ ```
1017+
1018+ 你必须在定义时或在每个构造器中执行 final 变量的赋值操作。这保证了 final 属性在使用前已经被初始化过。
1019+
1020+ ### final 参数
1021+
1022+ 在参数列表中,将参数声明为 final 意味着在方法中不能改变参数指向的对象或基本变量:
1023+
1024+ ``` java
1025+ // reuse/FinalArguments.java
1026+ // Using "final" with method arguments
1027+ class Gizmo {
1028+ public void spin () {
1029+
1030+ }
1031+ }
1032+
1033+ public class FinalArguments {
1034+ void with (final Gizmo g ) {
1035+ // -g = new Gizmo(); // Illegal -- g is final
1036+ }
1037+
1038+ void without (Gizmo g ) {
1039+ g = new Gizmo (); // OK -- g is not final
1040+ g. spin();
1041+ }
1042+
1043+ // void f(final int i) { i++; } // Can't change
1044+ // You can only read from a final primitive
1045+ int g (final int i ) {
1046+ return i + 1 ;
1047+ }
1048+
1049+ public static void main (String [] args ) {
1050+ FinalArguments bf = new FinalArguments ();
1051+ bf. without(null );
1052+ bf. with(null );
1053+ }
1054+ }
1055+ ```
1056+
1057+ 方法 ` f() ` 和 ` g() ` 展示了 ** final** 基本类型参数的使用情况。你只能读取而不能修改参数。这个特性主要用于传递数据给匿名内部类。这将在”内部类“章节中详解。
9741058
9751059### final 方法
9761060
1061+ 使用 ** final** 方法的原因有两个。第一个原因是给方法上锁,防止子类通过覆写改变方法的行为。这是出于继承的考虑,确保方法的行为不会因继承而改变。
1062+
1063+ 过去建议使用 ** final** 方法的第二个原因是效率。在早期的 Java 实现中,如果将一个方法指明为 ** final** ,就是同意编译器把对该方法的调用转化为内嵌调用。当编译器遇到 ** final** 方法的调用时,就会很小心地跳过普通的插入代码以执行方法的调用机制(将参数压栈,跳至方法代码处执行,然后跳回并清理栈中的参数,最终处理返回值),而用方法体内实际代码的副本替代方法调用。这消除了方法调用的开销。但是如果一个方法很大代码膨胀,你也许就看不到内嵌带来的性能提升,因为内嵌调用带来的性能提高被花费在方法里的时间抵消了。
1064+
1065+ 在最近的 Java 版本中,虚拟机可以探测到这些情况(尤其是 * hotspot* 技术),并优化去掉这些效率反而降低的内嵌调用方法。有很长一段时间,使用 ** final** 来提高效率都被阻止。你应该让编译器和 JVM 处理效率问题,只有在防止方法ß覆写时才使用 ** final** 。
9771066
1067+ ### final 和 private
1068+
1069+ 类中所有的 ** private** 方法都隐式地指定为 ** final** 。因为不能访问 ** private** 方法,所以不能覆写它。可以给 ** private** 方法添加 ** final** 修饰,但是并不能给方法带来额外的含义。
1070+
1071+ 以下情况会令人困惑,当你试图覆写一个 ** private** 方法(隐式是 ** final** 的)时,看上去奏效,而且编译器不会给出错误信息:
1072+
1073+ ``` java
1074+ // reuse/FinalOverridingIllusion.java
1075+ // It only looks like you can override
1076+ // a private or private final method
1077+ class WithFinals {
1078+ // Identical to "private" alone:
1079+ private final void f () {
1080+ System . out. println(" WithFinals.f()" );
1081+ }
1082+ // Also automatically "final":
1083+ private void g () {
1084+ System . out. println(" WithFinals.g()" );
1085+ }
1086+ }
1087+
1088+ class OverridingPrivate extends WithFinals {
1089+ private final void f () {
1090+ System . out. println(" OverridingPrivate.f()" );
1091+ }
1092+
1093+ private void g () {
1094+ System . out. println(" OverridingPrivate.g()" );
1095+ }
1096+ }
1097+
1098+ class OverridingPrivate2 extends OverridingPrivate {
1099+ public final void f () {
1100+ System . out. println(" OverridingPrivate2.f()" );
1101+ }
1102+
1103+ public void g () {
1104+ System . out. println(" OverridingPrivate2.g()" );
1105+ }
1106+ }
1107+
1108+ public class FinalOverridingIllusion {
1109+ public static void main (String [] args ) {
1110+ OverridingPrivate2 op2 = new OverridingPrivate2 ();
1111+ op2. f();
1112+ op2. g();
1113+ // You can upcast:
1114+ OverridingPrivate op = op2;
1115+ // But you can't call the methods:
1116+ // - op.f();
1117+ // - op.g();
1118+ // Same here:
1119+ WithFinals wf = op2;
1120+ // - wf.f();
1121+ // - wf.g();
1122+ }
1123+ }
1124+ ```
1125+
1126+ 输出:
1127+
1128+ ```
1129+ OverridingPrivate2.f()
1130+ OverridingPrivate2.g()
1131+ ```
1132+
1133+ "覆写"只发生在方法是基类的接口时。也就是说,必须能将一个对象向上转型为基类并调用相同的方法(这一点在下一章阐明)。如果一个方法是 ** private** 的,它就不是基类接口的一部分。它只是隐藏在类内部的代码,且恰好有相同的命名而已。但是如果你在派生类中以相同的命名创建了 ** public** ,** protected** 或包访问权限的方法,这些方法与基类中的方法没有联系,你没有覆写方法,只是在创建新的方法而已。由于 ** private** 方法无法触及且能有效隐藏,除了把它看作类中的一部分,其他任何事物都不需要考虑到它。
9781134
9791135### final 类
9801136
1137+ 当说一个类是 ** final** (** final** 关键字在类定义之前),就意味着它不能被继承。之所以这么做,是因为类的设计就是永远不需要改动,或者是出于安全考虑不希望它有子类。
1138+
1139+ ``` java
1140+ // reuse/Jurassic.java
1141+ // Making an entire class final
1142+ class SmallBrain {}
1143+
1144+ final class Dinosaur {
1145+ int i = 7 ;
1146+ int j = 1 ;
1147+ SmallBrain x = new SmallBrain ();
1148+
1149+ void f () {}
1150+ }
1151+
1152+ // - class Further extends Dinosaur {}
1153+ // error: Cannot extend final class 'Dinosaur'
1154+ public class Jurassic {
1155+ public static void main (String [] args ) {
1156+ Dinosaur n = new Dinosaur ();
1157+ n. f();
1158+ n. i = 40 ;
1159+ n. j++ ;
1160+ }
1161+ }
1162+ ```
1163+
1164+ ** final** 类的属性可以根据个人选择是或不是 ** final** 。这同样适用于不管类是否是 ** final** 的内部 ** final** 属性。然而,由于 ** final** 类禁止继承,类中所有的方法都被隐式地指定为 ** final** ,所以没有办法覆写它们。你可以在 final 类中的方法加上 ** final** 修饰符,但不会增加任何意义。
1165+
1166+ ### final 忠告
9811167
9821168<!-- Initialization and Class Loading -->
1169+
9831170## 类初始化和加载
9841171
9851172<!-- Summary -->
0 commit comments