前言
本文内容参考自《Thinking in Java》。
final数据
在Java中,编译期常量必须是基本数据类型,并且以关键字final
表示。在对这个常量进行定义的时候,必须对其进行赋值。
对于基本类型,final
使数值恒定不变;而用于对象引用,final
使引用恒定不变。一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象。然而对象其自身却是可以被修改的。
public class FinalData {
private final int valueOne = 9;
private static final VALUE_TWO = 99;
public static final VALUE_THREE = 39;
}
由于valueOne
和VALUE_TWO
都是带有编译时数值的final
基本类型,所以它们二者均可以用作编译期常量,并且没有重大区别。VAL_THREE
是一种更加典型的对常量进行定义的方式:定义为public
,则可以被用于包之外;定义为static
,则强调只有一份;定义为final
,则说明它是一个常量。请注意,带有恒定初始值(即,编译期常量)的static final
基本类型全用大写字母命名,并且单词与单词之间用下划线隔开(这就像C常量一样,C常量是这一命名传统的发源地)。
public class FinalData {
private static Random rand = new Random(47);
private final int i4 = rand.nextInt(20);
static final int INT_5 = rand.nextInt(20);
}
我们不能因为某数据是final
的就认为在编译时可以知道它的值。在运行时使用随机生成的数值来初始化i4
和INT_5
就说明了这一点。
空白final
Java允许生成“空白final
”,所谓空白final
是指被声明为final
但又未给定初值的域。无论什么情况,编译器都确保空白final
在使用前必须被初始化。但是,空白final
在关键字final
的使用上提供了更大的灵活性,为此,一个类中的final
域就可以做到根据对象而有所不同,却又保持其恒定不变的特性。
class Poppet {
private int i;
Poppet(int ii) {
i = ii;
}
}
public class BlankFinal {
private final int i = 0; // Initialized final
private final int j; // Blank final
private final Poppet p; // Blank final reference
public BlankFinal() {
j = 1; // Initialize blank final
p = new Poppet(1); // Initialize blank final reference
}
public BlankFinal(int x) {
j = x; // Initialize blank final
p = new Poppet(x); // Initialize blank final reference
}
public static void main(String[] args) {
new BlankFinal();
new BlankFinal(47);
}
}
必须在域的定义处或者每个构造器中用表达式对final
进行赋值,这正是final
域在使用前总是被初始化的原因所在。
final参数
Java允许在参数列表中以声明的方式将参数指明为final
。这意味着你无法在方法中更改参数引用所指向的对象。
class Gizmo {
public void spin() {}
}
public class FinalArguments {
void with(final Gizmo g) {
//! g = new Gizmo(); // Illegal -- g is final
}
void without(Gizmo g) {
g = new Gizmo(); // OK -- g not final
g.spin();
}
void f(final int i) {
//! i++ // Can't change
}
int g(final int i) {
return i + 1;
}
public static void main(String[] args) {
FinalArguments bf = new FinalArguments();
bf.without(null);
bf.with(null);
}
}
方法f()
和g()
展示了当基本类型的参数被指明为final
所出现的结果:你可以读参数,但却无法修改参数。这一特性主要用来向匿名内部类传递数据。
final方法
使用final
方法的原因有两个。第一个原因是把方法锁定,以防止任何继承类修改它的含义。这是出于设计的考虑:想要确保在继承中使方法行为保持不变,并且不会被覆盖。过去建议使用final
方法的第二个原因是效率。在Java的早期实现中,如果将一个方法指明为final
,就是同意编译器将针对该方法的所有调用都转为内嵌调用。这将消除方法调用的开销。现在已经不再需要使用final
方法来进行优化了。
final和private关键字
类中所有的private
方法都隐式地指定为是final
的。由于无法取用private
方法,所以也就无法覆盖它。可以对private
方法添加final
修饰词,但这并不能给方法增加任何额外的意义。
final类
当用final
修饰⼀个类时,表明这个类不能被继承。final
类中的所有方法都会被隐式地指定为final
方法。在final
类中可以给方法添加final
修饰词,但这不会增添任何意义。