Java中final关键字详解

文章目录

    • final实例字段
    • final类和方法
    • 将方法和类定义为final的主要原因

final实例字段

可以将实例字段定义为final,这样的字段必须在构造对象时初始化。也就是说,必须确保在每一个构造器执行之后,这个字段的值都已经设置,并且以后不能在修改这个字段。
final修饰符对于类型为基本类型或者不可变类的字段尤其有用。(如果类中的所有方法都不会改变其对象,这样的类就是不可变的类。例如,String就是不可变的)
对于可变的类,使用final修饰符可能会造成混乱。例如,考虑以下字段:

private final StringBuilder evaluations;
它在类中的构造器中初始化为
evaluations=new String();
final关键字只是表示储存在evaluations变量中的对象引用不会再指示另
一个不同的StringBuilder对象。不过这个对象可以更改:
public void giveGoldStar(){
evaluation.append(LocalDate.now()+":Gold  star!\n");
}

final类和方法

有时候,我们可能希望阻止人们利用某个类定义子类,即阻止继承。**不允许扩展的类被称为final类。如果在定义类的时候使用了final修饰符就表明这个类时final类。**示例如下:

public final class Executive
{
}

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

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

值得注意的是,如果将一个类声明为final,只有其中的方法自动成为fianl,而不包括字段。

将方法和类定义为final的主要原因

确保它们不会在子类中改变语义。例如 在Galendar类中的getTime和setTime方法都声明为final。这表明Galendar类的设计者负责实现Date类和日历状态的转换,而不允许子类来添乱。同样地,String类也是final类,这意味着不允许任何人定义String的子类。换言之,如果有一个String引用,它引用的一定是一个String对象,而不可能是其他类的对象。
在早期的java中,有些程序员为了避免动态绑定带来的系统开销而使用fianl关键字。如果一个方法没被覆盖并且很短,编译器就能够对它进行优化处理,这个过程称为内联。例如,内联调用e.getName()将被替换为访问字段e.name,这是一项很有意义的改进,CPU在处理当前指令时,分支会扰乱预取指令的策略,所以CPU不喜欢分支。然而,如果getName在另外一个类中被覆盖,那么编译器就无法知道覆盖的代码将会做什么操作,因此也就不能对它进行内联处理了。
幸运的是,虚拟机中的即时编译器比传统编译器的处理能力强得多。这种编译器可以准确的知道类之间的继承关系,并能够检测出是否有类确实覆盖了给定的方法。如果方法很简单、被频繁调用而且确实没有被覆盖,那么即时 编译器就会将这个方法进行内联处理如果虚拟机加载了另外一个子类,而这个子类覆盖了一个内联方法,那么将会发生什么情况呢?优化器将取消对这个方法的内联。这个过程很慢,不过很少会发生这种情况。

你可能感兴趣的:(java学习,java,编程语言,类,设计模式)