Java final关键字:阻止继承和多态

    |     2015年3月30日   |   Java面向对象   |     0 条评论   |    1651

声明类、变量和方法时可以用 final 修饰,表示终态:这个东西定下来以后就不能再改继承关系、不能再被覆盖、不能再被重新赋值。

具体规定:final 类不能被继承;final 方法不能被子类重写;final 变量(成员或局部)只能赋值一次,成为常量。

修饰对象 效果 补充
类 不能再派生子类 方法隐式 final;字段不会因此变成常量
方法 不能被覆盖,静态绑定 private / static 方法本身就不能被覆盖
基本类型变量 只能赋值一次 常量名习惯全大写
引用类型变量 引用不能再指向别的对象 对象内部状态仍可改

一、final 变量

final 一般用来保护不该被改掉的通用功能或取值,例如三角函数、幂运算,以及数学常量 Math.PI、Math.E。java.lang.Math 本身也是 final 类,防止被继承后篡改这些实现。

成员变量可以在声明时赋值;若声明时没赋值(空白 final),实例字段必须在每个构造方法里显式赋一次(也可以在实例初始化块里),静态 final 则在静态块里赋。局部 final 可以先声明后一次性赋值。再赋值会编译失败。

public final int MAX_ARRAY_SIZE = 25; // 常量名一般大写
public final class Demo {
    public static final int TOTAL_NUMBER = 5;
    public int id;
    public Demo() {
        // 非法:++TOTAL_NUMBER 相当于 TOTAL_NUMBER = TOTAL_NUMBER + 1
        // id = ++TOTAL_NUMBER;
        id = TOTAL_NUMBER;
    }
    public static void main(String[] args) {
        final Demo t = new Demo();
        final int i = 10;
        final int j;
        j = 20;
        // j = 30;  // 非法,对 final 变量二次赋值
        // t = new Demo();  // 非法,final 引用不能再指向别的对象
        t.id = 8;           // 合法,改的是对象内容,不是引用本身
    }
}

引用被标成 final,只锁住“指向谁”,不锁住对象里面的字段。子类可以继承可见的 final 字段并读取,但不能覆盖或重新赋值。

二、final 类

final 放在 class 前面,阻止再派生。例如 java.lang.String 就是 final 类:拿到一个字符串引用,必须就是 String 本身,而不能是某个恶意子类伪装出来的“字符串”。类一旦是 final,其中方法也隐式按 final 处理,变量不会。

三、final 方法与多态

final 方法不能被覆盖,属于静态绑定,运行时不必再查方法表。被 static 或 private 修饰的方法本来就不能参与多态覆盖,动态绑定没有意义。

有人主张“除非明确要多态,否则方法全写成 final”。这偏激了:JVM 的即时编译器能根据运行时继承关系做优化。方法很短且没有被覆盖时,编译器可以内联(inlining),例如把 e.getName() 替换成直接读 e.name。CPU 处理调用时的分支会打乱预取,内联能避开这一点。一旦 getName() 在别的类里被覆盖,编译器就无法预知覆盖代码做什么,也就不能内联。

一句话总结:final 类不能继承,final 方法不能覆盖,final 变量只能赋一次——引用 final 只锁住指向,不锁住对象内容。

转载请注明来源:Java final关键字:阻止继承和多态
本文链接地址:https://ai.zhousir.top/?p=97
回复 取消