在Java中,关键字final一般是指“无法改变的”。有三种情况可以使用到final:数据、方法、类。
1.final 数据
被final修饰的数据就是一个不能改变的常量。而一个既是static又是final的域则表示只占据着一段不能改变的存储空间。
对于基本数据类型,final使数值不能改变。比如我定义了如下几个常量:
private final int valueOne = 9;
private static final int VALUE_TWO = 99;
public static final int VALUE_THREE = 39;
对于这几个常量,都不能改变它们的值——比如自加或者想尝试给它们赋值。
valueOne和VALUE_TWO并没有太大的区别,都可以用作编译期常量。而VALUE_THREE则是一种比较经典的常量定义方式,它表示是一个公有的并且只会初始化一次的常量。
注意,编译期常量的static final基本类型全用大写字母命名,并且字与字之间用下划线连接。
对于对象引用,final使引用恒定不变。一旦引用指向一个对象,便不能再指向另外的对象。但对象的值却可以改变。比如:
import java.util.Random;
class Value{
int i;
public Value(int i){
this.i = i;
}
}
public class FinalData {
private Value v1 = new Value(11);
private final Value v2 = new Value(22);
private static final Value VAL_3 = new Value(33);
public static void main(String[] args) {
FinalData fd1 = new FinalData("fd1");
//fd1.v2 = new Value(0); //不能使v2再指向其他对象
fd1.v2.i++; //但可以改变v2所指向的对象中的值。
fd1.v1 = new Value(9);//没有final修饰。
//fd1.VAL_3 = new Value(1); //也不能使VAL_3指向其他对象
}
}
因为数组也是对象,所以数组也符合上述所规定的情况:
private final int[] a = {1,2,3,4,5,6};
for(int i = 0;i<fd1.a.length;i++){
a[i]++; //数组中的对象的值依然可以改变
}
//a = new int[3]; //不能重新给a赋一个数组
空白final
空白final是指被声明为final却又未赋初值的域。但空白final必须在构造器中初始化:
public class BlankFinal {
private final int i ;
public BlankFinal(){
i = 0; //如果没有初始化,则会报错
}
public BlankFinal(int i){
this.i = i; //如果没有初始化,则会报错
}
public static void main(String[] args) {
new BlankFinal();
new BlankFinal(10);
}
}
这样写,就可以使得一个类中的final域,可以根据不同的对象生成不同的值,并且还是不可变的。
final 参数
在一个方法中,可以将参数声明为final。但这样就无法改变参数引用所指向的对象或者参数的数值。
public class FinalArugment {
public void f(final String s){
//s = new String(); //报错
}
public void g(String s){
s = new String(); //不会报错
}
void f(final int i){
//i++; //报错,不能改变其值
}
int g(final int i){
return i+1; //不会报错,可以读取i的值。
}
}
2.final方法
使用final修饰的方法,可以防止任何继承类修改它,即在继承当中,这个方法不会被覆盖。
类中所有private方法都隐式地指定为final。
根据以上两句的总结,看如下一段代码:
class WithFinal{
private final void f(){
System.out.println("WithFinal.f()");
}
private void g(){
System.out.println("WithFinal.g()");
}
}
class OverrdingPrivate extends WithFinal{
private final void f(){
System.out.println("OverrdingPrivate.f()");
}
private void g(){
System.out.println("OverrdingPrivate.g()");
}
}
class OverrdingPrivate2 extends OverrdingPrivate{
public final void f(){
System.out.println("OverrdingPrivate2.f()");
}
public void g(){
System.out.println("OverrdingPrivate2.g()");
}
}
public class FinalOverridingIllusion {
public static void main(String[] args) {
OverrdingPrivate2 op = new OverrdingPrivate2();
op.f();
op.g();
OverrdingPrivate op1 = op;
//op1.f(); //不能调用
//op1.g(); //不能调用
WithFinal wf = op;
//wf.f(); //不能调用
//wf.g(); //不能调用
}
}
以上代码看起来,子类好像覆盖了父类中的私有方法。但实际是却不是这样。
因为父类中的方法是由private修饰,所以其方法不属于基类中的接口的一部分,而覆盖是只有当方法是基类的接口的一部分时才会出现。因此,以上代码中的子类中,只是重新写了一个与父类方法同名的方法而已。
3 final类
当一个类由final修饰时,该类便不能再被继承。因而,这个类中的所有方法都会隐式地指定为final——反正都不能覆盖它们。