Java中的final关键字

本文详细介绍了Java中的final关键字,包括final如何用于限制类、方法和变量的可扩展性。final类不能被继承,如String类所示,以保持安全性。final方法不能被重写,如Thread的isAlive()方法。final变量一旦初始化便不可再赋值,但引用变量所指的对象属性仍可修改。讨论了何时应使用final关键字以及其对代码可维护性和可扩展性的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

### Java 中 `final` 关键字的作用和使用场景 #### 1. **`final` 修饰类** 当 `final` 用于修饰一个类时,该类无法被其他类继承。这种设计通常适用于那些不需要扩展或者不应该被子类化的类。通过这种方式可以防止潜在的错误或滥用继承带来的风险。此外,在 `final` 类中,所有的成员方法会被隐式地定义为 `final` 方法[^4]。 ```java // 定义一个 finalfinal class FinalClass { void display() { System.out.println("This is a final class."); } } ``` #### 2. **`final` 修饰方法** 如果一个方法被声明为 `final`,那么这个方法在其派生类中不能被重写(override)。这有助于保持某些核心功能的一致性,避免因子类更改父类行为而导致意外的结果。这种方法常用于框架开发中,以确保一些关键逻辑不会被破坏[^2]。 ```java class BaseClass { final void show() { // 此方法不可被覆盖 System.out.println("Final method cannot be overridden."); } } class DerivedClass extends BaseClass { @Override void show() { // 编译器会报错 System.out.println("Trying to override final method."); } } ``` #### 3. **`final` 修饰变量** 对于基本数据类型的变量来说,一旦赋值完成就不能再改变其值;而对于引用类型的数据而言,则意味着不能再指向另一个对象实例,但是原对象的内容仍然可能发生变化[^5]。 - 基本数据类型: ```java final int number = 10; number = 20; // 错误:试图重新分配给 final 变量 'number' ``` - 引用数据类型: ```java final StringBuilder sb = new StringBuilder("Initial"); sb.append(" Value"); // 合法操作,因为只是修改了原有对象的状态 System.out.println(sb.toString()); // 输出:"Initial Value" sb = new StringBuilder("New Object"); // 非法操作,尝试替换原有的引用地址 ``` 以上特性使得 `final` 成为了构建线程安全代码的重要工具之一,因为它能够减少共享资源之间的竞争条件[^3]。 --- ### 总结 综上所述,`final` 是一种非常有用的机制来提高程序的安全性和性能表现。合理运用它可以有效控制软件系统的灵活性与稳定性之间的平衡点[^1]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值