Java中的final关键字
1.介绍
虽然继承使我们能够重用现有代码,但有时我们确实需要出于各种原因对可扩展性设置限制;final关键字可以让我们做到这一点。
在本教程中,我们将了解final关键字对类、方法和变量的意义。
2.Final类
标记为final的类不能被继承。如果我们看一下Java核心库的代码,我们会在那里找到许多最终的类。一个例子是String类。如果我们可以扩展String类,重写它的任何方法,并用特定String子类的实例替换所有的String实例,对字符串对象的操作结果将变得不可预测。考虑到String类到处都在使用,这是不可接受的。这就是为什么String类被标记为final。
任何从final类继承的尝试都将导致编译器错误。为了演示这一点,让我们创建最后一个类Cat:
public final class Cat {
private int weight;
// standard getter and setter
}
让我们试着继承它:
public class BlackCat extends Cat {
}
我们将看到编译器错误:
The type BlackCat cannot subclass the final class Cat
注意,类声明中的final关键字并不意味着这个类的对象是不可变的。我们可以自由更改Cat对象的字段:
Cat cat = new Cat();
cat.setWeight(1);
assertEquals(1, cat.getWeight());
我们只是不能继承它。如果我们严格遵循好的设计规则,我们应该谨慎地创建和记录一个类,或者出于安全原因将其声明为final。但是,在创建final类时应该谨慎。
注意,使类成为final意味着没有其他程序员可以改进它。假设我们使用的是一个类,而没有它的源代码,并且有一个方法有问题。
如果类是final,我们就不能继承它来重写方法并解决问题。换句话说,我们失去了可扩展性,而这正式面向对象编程的好处之一。
3.Final方法
不能重写标记为final的方法。当我们设计一个类并认为一个方法不应该被重写时,我们可以将这个方法设为final。我们还可以在Java核心库中找到许多final方法。
有时我们不需要完全禁止类继承,只需要防止重写某些方法。Thread类就是一个很好的例子。扩展它从而创建自定义线程类是合法的。但是它的isAlive()方法是最终的。
此方法检查线程是否处于活动状态。由于许多原因,无法正确重写isAlive()方法。其中之一就是这个方法是本地的。本机代码是用另一种编程语言实现的,通常特定于运行本机代码的操作系统和硬件。
让我们创建一个Dog类并将其sound()方法设为final:
public class Dog {
public final void sound() {
// ...
}
}
现在,让我们继承Dog类并尝试重写其sound()方法:
public class BlackDog extends Dog {
public void sound() {
}
}
结果就是编译报错:
- overrides
com.java.finalkeyword.Dog.sound- Cannot override the final method from Dog
sound() method is final and can’t be overridden
如果我们类的某些方法被其他方法调用,我们应该考虑将被调用的方法设置为final。否则,重写它们可能会影响调用者的工作并导致意想不到的结果。
如果我们的构造函数调用其他方法,出于上述原因,我们通常应该将这些方法声明为final。将类的所有方法都标记为final和将类本身标记为final有什么区别?在第一种情况下,我们可以扩展类并向其添加新方法。
4.Final变量
无法重新分配标记为final的变量。一旦最后一个变量被初始化,它就不能被改变。
4.1 Final原始类型变量
让我们声明一个基本的final变量i,然后给它赋值1。我们试着给它赋值2:
public void whenFinalVariableAssign_thenOnlyOnce() {
final int i = 1;
//...
i=2;
}
输出:
The final local variable i may already have been assigned
4.2 Final引用变量
如果我们有一个final引用变量,我们也不能重新分配它。但这并不意味着它所指的对象是不变的。我们可以自由地改变这个对象的属性。为了演示这一点,让我们声明最后一个引用变量cat并初始化它:
final Cat cat = new Cat();
如果我们尝试重新分配它,我们将看到一个编译器错误:
The final local variable cat cannot be assigned. It must be blank and not using a compound assignment
但我们可以更改Cat实例的属性:
cat.setWeight(5);
assertEquals(5, cat.getWeight());
4.3 Final字段
final字段可以是常量或一次写入字段。为了区分它们,我们应该问一个问题-如果我们要序列化对象,我们会包括这个字段吗?如果不是,那么它不是对象的一部分,而是一个常量。
请注意,根据命名约定,类常量应为大写,组件之间用下划线(“_”)字符分隔:
static final int MAX_WIDTH = 999;
请注意,任何final字段都必须在构造函数完成之前初始化。对于静态final字段,这意味着我们可以初始化它们:
- 如上例所示声明时
- 在静态初始化程序块中
4.3 Final参数
final关键字放在方法参数之前也是合法的。但无法在方法内更改final参数:
public void methodWithFinalArguments(final int x) {
x=1;
}
The final local variable x cannot be assigned. It must be blank and not using a compound assignment
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix