不允许扩展的类被称为 final 类。如果在定义类的时候使用了 final 修饰符就表明这个类是 final 类。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {
    ...
}

类中的某个特定方法也可以被声明为 final。如果这样做,子类就不能覆盖这个方法(final 类中的所有方法自动地成为 final 方法)。

public class Employee
{
    ...
    public final String getName()
    {
        return name;
    }
    ...
}

字段也可以被声明为 final。对于 final 字段来说,构造对象之后就不允许改变它们的值了。不过,如果将一个类声明为 final,只有其中的方法自动地成为 final,而不包括字段。

将方法或类声明为 final 主要目的是: 确保它们不会在子类中改变语义。例如:

  • Calendar 类中的 getTime 和 setTime 方法都声明为 final。这表明 Calendar 类的设计者负责实现 Date 类与日历状态之间的转换,而不允许子类来添乱。
  • String 类也是 final 类,这意味着不允许任何人定义 String 的子类。换言之,如果有一个 String 的引用,它引用的一定是一个 String 对象,而不可能是其他类的对象。

有些程序员认为:除非有足够的理由使用多态性,否则应该将所有的方法都声明为 final。事实上,在 C++ 和 C# 中,如果没有特别地说明,所有的方法都不具有多态性。这两种做法可能都有些偏激。我们提倡在设计类层次时,要仔细地思考应该将哪些方法和类声明为 final。

在早期的 Java 中,有些程序员为了避免动态绑定带来的系统开销而使用 final 关键字。如果一个方法没有被覆盖并且很短,编译器就能够对它进行优化处理,这个过程称为内联(inlining)。例如,内联调用 e.getName() 将被替换为访问字段 e.name。这是一项很有意义的改进,CPU 在处理当前指令时,分支会扰乱预取指令的策略,所以,CPU 不喜欢分支。然而,如果 getName 在另外一个类中被覆盖,那么编译器就无法知道覆盖的代码将会做什么操作,因此也就不能对它进行内联处理了。

幸运的是,虚拟机中的即时编译器比传统编译器的处理能力强得多。这种编译器可以准确地知道类之间的继承关系,并能够检测出是否有类确实覆盖了给定的方法。如果方法很简短、被频繁调用而且确实没有被覆盖,那么及时编译器就会将这个方法进行内联处理。如果虚拟机加载了另一个子类,而且这个子类覆盖了一个内联方法,那么将会发生什么情况呢?优化器将取消这个方法的内联。这个过程很慢,不过很少会发生这个情况。