The “final” Keyword in Java – Java中的“final”关键字

最后修改: 2018年 3月 27日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

While inheritance enables us to reuse existing code, sometimes we do need to set limitations on extensibility for various reasons; the final keyword allows us to do exactly that.

虽然继承使我们能够重用现有的代码,但有时我们确实需要为可扩展性设置限制,原因是多方面的;final关键字正是允许我们这样做。

In this tutorial, we’ll take a look at what the final keyword means for classes, methods, and variables.

在本教程中,我们将看看final关键字对类、方法和变量意味着什么。

2. Final Classes

2.最后

Classes marked as final can’t be extended. If we look at the code of Java core libraries, we’ll find many final classes there. One example is the String class.

标记为final的类不能被扩展。如果我们看一下Java核心库的代码,我们会发现那里有许多final类。其中一个例子是String类。

Consider the situation if we can extend the String class, override any of its methods, and substitute all the String instances with the instances of our specific String subclass.

考虑一下如果我们可以扩展String类,覆盖它的任何方法,并用我们特定的String子类的实例替代所有的String实例。

The result of the operations over String objects will then become unpredictable. And given that the String class is used everywhere, it’s unacceptable. That’s why the String class is marked as final.

String对象的操作结果就会变得不可预测了。考虑到String类的使用无处不在,这是不可以接受的。这就是为什么String类被标记为final

Any attempt to inherit from a final class will cause a compiler error. To demonstrate this, let’s create the final class Cat:

任何试图从final类继承的行为都会导致编译器错误。为了证明这一点,我们来创建finalCat

public final class Cat {

    private int weight;

    // standard getter and setter
}

And let’s try to extend it:

并让我们试着扩展它。

public class BlackCat extends Cat {
}

We’ll see the compiler error:

我们会看到编译器错误。

The type BlackCat cannot subclass the final class Cat

Note that the final keyword in a class declaration doesn’t mean that the objects of this class are immutable. We can change the fields of Cat object freely:

注意,类声明中的final关键字并不意味着这个类的对象是不可改变的。我们可以自由改变Cat对象的字段。

Cat cat = new Cat();
cat.setWeight(1);

assertEquals(1, cat.getWeight());

We just can’t extend it.

我们只是不能延长它。

If we follow the rules of good design strictly, we should create and document a class carefully or declare it final for safety reasons. However, we should use caution when creating final classes.

如果我们严格遵循良好设计的规则,我们应该谨慎地创建和记录一个类,或者为了安全起见宣布它是final。然而,在创建final类时,我们应该谨慎行事。

Notice that making a class final means that no other programmer can improve it. Imagine that we’re using a class and don’t have the source code for it, and there’s a problem with one method.

请注意,让一个类成为final意味着其他程序员不能改进它。想象一下,我们正在使用一个类,但没有它的源代码,而且有一个方法出现了问题。

If the class is final, we can’t extend it to override the method and fix the problem. In other words, we lose extensibility, one of the benefits of object-oriented programming.

如果这个类是最终的,我们就不能扩展它来覆盖这个方法并解决这个问题。换句话说,我们失去了可扩展性,这是面向对象编程的好处之一。

3. Final Methods

3.最终方法

Methods marked as final cannot be overridden. When we design a class and feel that a method shouldn’t be overridden, we can make this method final. We can also find many final methods in Java core libraries.

标记为final的方法不能被重写。当我们设计一个类,觉得某个方法不应该被重写时,我们可以把这个方法变成final。我们还可以在Java核心库中找到许多final方法。

Sometimes we don’t need to prohibit a class extension entirely, but only prevent overriding of some methods. A good example of this is the Thread class. It’s legal to extend it and thus create a custom thread class. But its isAlive() methods is final.

有时我们不需要完全禁止一个类的扩展,而只需要阻止对某些方法的重写。一个很好的例子就是Thread类。对它进行扩展是合法的,因此可以创建一个自定义的线程类。但是它的isAlive()方法是final

This method checks if a thread is alive. It’s impossible to override the isAlive() method correctly for many reasons. One of them is that this method is native. Native code is implemented in another programming language and is often specific to the operating system and hardware it’s running on.

这个方法检查一个线程是否活着。由于许多原因,我们不可能正确地覆盖isAlive()方法。其中之一就是这个方法是本地的。原生代码是用另一种编程语言实现的,而且往往是针对它所运行的操作系统和硬件的。

Let’s create a Dog class and make its sound() method final:

让我们创建一个Dog类并使其sound()方法final

public class Dog {
    public final void sound() {
        // ...
    }
}

Now let’s extend the Dog class and try to override its sound() method:

现在让我们扩展Dog类并尝试覆盖其sound()方法。

public class BlackDog extends Dog {
    public void sound() {
    }
}

We’ll see the compiler error:

我们会看到编译器错误。

- overrides
com.baeldung.finalkeyword.Dog.sound
- Cannot override the final method from Dog
sound() method is final and can’t be overridden

If some methods of our class are called by other methods, we should consider making the called methods final. Otherwise, overriding them can affect the work of callers and cause surprising results.

如果我们类的某些方法被其他方法调用,我们应该考虑将被调用的方法变成final。否则,重写它们会影响调用者的工作,并导致令人惊讶的结果。

If our constructor calls other methods, we should generally declare these methods final for the above reason.

如果我们的构造函数调用其他方法,出于上述原因,我们一般应该声明这些方法final

What’s the difference between making all methods of the class final and marking the class itself final? In the first case, we can extend the class and add new methods to it.

使类的所有方法final和标记类本身final之间有什么区别?在第一种情况下,我们可以扩展该类并向其添加新方法。

In the second case, we can’t do this.

在第二种情况下,我们不能这样做。

4. Final Variables

4.最终变量

Variables marked as final can’t be reassigned. Once a final variable is initialized, it can’t be altered.

标记为final的变量不能被重新分配。一旦final变量被初始化,它就不能被改变。

4.1. Final Primitive Variables

4.1.最终原始变量

Let’s declare a primitive final variable i, then assign 1 to it.

让我们声明一个原始的final变量i,然后给它赋值1。

And let’s try to assign a value of 2 to it:

让我们试着给它分配一个2的值。

public void whenFinalVariableAssign_thenOnlyOnce() {
    final int i = 1;
    //...
    i=2;
}

The compiler says:

编译者说。

The final local variable i may already have been assigned

4.2. Final Reference Variables

4.2.最终参考变量

If we have a final reference variable, we can’t reassign it either. But this doesn’t mean that the object it refers to is immutable. We can change the properties of this object freely.

如果我们有一个final引用变量,我们也不能重新分配它。但是这并不意味着它所指的对象是不可改变的。我们可以自由地改变这个对象的属性。

To demonstrate this, let’s declare the final reference variable cat and initialize it:

为了证明这一点,让我们声明final引用变量cat并初始化它。

final Cat cat = new Cat();

If we try to reassign it we’ll see a compiler error:

如果我们试图重新分配它,我们会看到一个编译器错误。

The final local variable cat cannot be assigned. It must be blank and not using a compound assignment

But we can change the properties of Cat instance:

但我们可以改变Cat实例的属性。

cat.setWeight(5);

assertEquals(5, cat.getWeight());

4.3. Final Fields

4.3.最终字段

Final fields can be either constants or write-once fields. To distinguish them, we should ask a question — would we include this field if we were to serialize the object? If no, then it’s not part of the object, but a constant.

最终字段既可以是常量,也可以是一次性写入的字段。为了区分它们,我们应该问一个问题–如果我们对对象进行序列化,我们会不会包含这个字段?如果不会,那么它就不是对象的一部分,而是一个常量。

Note that according to naming conventions, class constants should be uppercase, with components separated by underscore (“_”) characters:

请注意,根据命名惯例,类常量应该是大写的,组件之间用下划线(”_”)字符分隔。

static final int MAX_WIDTH = 999;

Note that any final field must be initialized before the constructor completes.

请注意,任何final字段必须在构造函数完成之前被初始化

For static final fields, this means that we can initialize them:

对于static final字段,这意味着我们可以初始化它们。

  • upon declaration as shown in the above example
  • in the static initializer block

For instance final fields, this means that we can initialize them:

例如final字段,这意味着我们可以初始化它们。

  • upon declaration
  • in the instance initializer block
  • in the constructor

Otherwise, the compiler will give us an error.

否则,编译器会给我们一个错误。

4.4. Final Arguments

4.4.最终论点

The final keyword is also legal to put before method arguments. A final argument can’t be changed inside a method:

final关键字放在方法参数前也是合法的。一个final参数不能在一个方法中被改变

public void methodWithFinalArguments(final int x) {
    x=1;
}

The above assignment causes the compiler error:

上述赋值导致编译器错误。

The final local variable x cannot be assigned. It must be blank and not using a compound assignment

5. Conclusion

5.结论

In this article, we learned what the final keyword means for classes, methods, and variables. Although we may not use the final keyword often in our internal code, it may be a good design solution.

在这篇文章中,我们了解了final关键字对类、方法和变量的意义。尽管我们在内部代码中可能不会经常使用final关键字,但它可能是一个很好的设计方案。

As always, the complete code for this article can be found in the GitHub project.

一如既往,本文的完整代码可以在GitHub项目中找到。