Java 的 final 关键字,通常指的是“这是无法改变的”。本文介绍 final 关键字的三种用法,即变量、方法和类。
final 变量
用 final 来修饰一个成员变量,就是告诉编译器这个变量是恒定不变的,我们通常希望达到以下效果:
1. 一个永不改变的编译时常量
2. 一个在运行时被初始化的值,并且不希望它被改变
对于编译时常量这种情况,编译器可以将常量值带入任何可能用到的计算式中,可以在编译时执行计算式,减轻运行时的负担。这类常量必须是基本数据类型,并且在定义的时候,要显示地初始化,初始化可以直接赋值,也可以在代码块或者构造器中来指定初始值。
当 final 修饰的成员变量是对象引用时,引用是不变的,一旦这个引用被初始化指向一个对象,就无法再将其指向另一个对象,但是对象其自身是可以被修改的。
public class FinalTest {
//基本类型
private final int num_1 = new Random(12).nextInt();
public static final int NUM_2;
//字符串
private final String str_1;
private static final String STR_2 = "str_2";
//对象
private final Value value_1;
private static final Value VALUE_2 = new Value(22);
//数组
private final int[] a = {1, 2, 3};
/**
* 有参构造器
* @param str
*/
public FinalTest(String str) {
this.str_1 = str;
}
//代码块
{
value_1 = new Value(12);
}
//静态代码块
static {
NUM_2 = 2;
}
public static void main(String[] args) {
//final 修饰的对象,可以改变自身
VALUE_2.i = 3;
VALUE_2.i = 4;
}
}
class Value {
int i;
public Value(int i) {
this.i = i;
}
}
我们可以看到 final 修饰的变量,可以在定义的时候初始化,也可以在构造器或者代码块中初始化。一个即是 static 又是 final 的域只占据一段不能改变的存储空间。NUM_2 就是一个典型的常量的定义,定义为 static,表示只有一份,定义为 final,则表示它是一个常量。
带有恒定初始值(即:编译期常量)的 final static 的成员全部用大写字母,并且单词之间用下划线隔开。
不能因为某个数据是 final 的就认为在编译期可以知道它的值,比如上面代码中的 num_1,它是在运行时使用随机数来初始化的。
不能因为某个对象是 final 的,就认为无法改变它的值,比如上面代码中的 VALUE_2,final 表示它无法指向另一个对象,但是可以改变自身,这对数组具有同样的意义,数组也不过另一种引用。
final 还可以用来修饰参数,如下:
/**
* final 修饰参数
* @param num
*/
public static void test(final int num) {
System.out.println(num + 1);
//无法改变
//System.out.println(++num);
}
当用 final 修饰参数的时候,就意味着这个参数只能读取,不能改变。这个特性主要用于向匿名内部类传递参数。
final 方法
当我们使用 final 来修饰方法的时候,就是想确保这个方法的行为保持不变,并且不会因为继承而被覆盖。类中所有的 private 方法都隐式地指定为 final 的,由于外部的类无法取用 private 方法,所以也就无法覆盖它。可以使用 final 关键字来修饰 private 方法,只是显得有点多余就是了。
“覆盖”只有在某方法是父类的接口的一部分时才会出现,比如使用 public, default, protected 来修饰的方法,如果某方法为 private,它就不是父类的接口的一部分,所以除了把它看成是作为归属类的组织结构的原因而存在外,其他的任何原因都不需要考虑它。
final 类
当使用 final 来修饰类的时候,就表明你对这个类的内容很自信,不需要做任何变动,或者出于安全的考虑,不打算继承该类,而且也不允许别人这样做。最常见的就是 java.lang.String 这个类了,它的定义如下:
package java.lang;
//省略导包语句和注释
public final class String
implements java.io.Serializable, Comparable, CharSequence {
由于 final 类禁止继承,所以 final 类中的所有的方法都隐式地指定为 final 的,无法被覆盖,可以给 final 类的方法添加 final 关键字,当然,这也是多余的。
也正是由于 final 类被禁止继承,所以如果将一个方法指定为 final 的,这可能就会妨碍你的队友通过继承来复用你的类,而这只是因为你没有想到它会以那种方式被运用。所以,使用 final,还需慎重。