Java 有这个关键字——final。它可以应用于类、方法、变量(包括方法参数)。对于一个类,final关键字表示该类不能有子类,即禁止继承……这在创建不可变(unchangeable)对象时很有用。例如,String 类被声明为 final。

    public final class String {
    }
    
    class SubString extends String { // Compilation error
    }
我还应该注意,final 修饰符不能应用于抽象类(具有关键字 abstract 的类),因为它们是相互排斥的概念。对于 final 方法,修饰符意味着该方法不能在子类中被重写。当我们想要防止更改原始实现时,这很有用。

    public class SuperClass {
        public final void printReport() {
            System.out.println("Report");
        }
    }

    class SubClass extends SuperClass { 
        public void printReport() { //Compilation error
            System.out.println("MyReport");
        }
    }
对于基本类型的变量,final 关键字意味着值一旦赋值就不能更改。对于引用变量,就是指一个对象被赋值后,就不能再改变对该对象的引用。这个很重要!不能更改引用,但可以更改对象的状态。Java 8 引入了一个新概念:effectively final。它仅适用于变量(包括方法参数)。最重要的是,尽管明显没有 final 关键字,但变量的值在初始化后不会改变。换句话说,可以将 final 关键字应用于这样的变量而不会出现编译错误。实际上,final 变量可以在本地类(本地内部类)、匿名类(匿名内部类)和流(Stream API)中使用。

        public void someMethod() {
            // In the example below, both a and b are effectively final, since they are assigned values only once:
            int a = 1;
            int b;
            if (a == 2) b = 3;
            else b = 4;
            // c is NOT effectively final since its value changes
            int c = 10;
            c++;
            
            Stream.of(1, 2).forEach(s-> System.out.println(s + a)); // OK
            Stream.of(1, 2).forEach(s-> System.out.println(s + c)); // Compilation error
        }
现在,让我们进行一次小采访。毕竟,完成 CodeGym 课程的目标是成为一名 Java 开发人员并找到一份有趣且收入丰厚的工作。那么,让我们开始吧。
  1. 对于声明为 final 的数组,我们能说些什么呢?

  2. 我们知道 String 类是不可变的:该类被声明为 final。字符串值存储在用关键字 final 标记的 char 数组中。


public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

我们可以替换 String 对象的值(而不更改对对象的引用)吗?这些是真正的面试问题。而实践表明,很多考生都答错了。了解如何使用 final 关键字非常重要,尤其是对于引用变量。在您考虑这一点时,我将向 CodeGym 团队提出一个小请求。请给文本编辑器添加一个块的能力,当你点击它时,它的内容可以显示/隐藏。 答案:
  1. 数组是一个对象,所以 final 关键字意味着一旦分配了对数组的引用,就不能更改该引用。也就是说,您可以更改对象的状态。

    
            final int[] array = {1, 2, 3, 4, 5};
            array[0] = 9;	 // OK, because we're changing the contents of the array: {9, 2, 3, 4, 5}
            array = new int[5]; // Compilation error
    
  2. 我们可以。最主要的是理解棘手的 final 关键字在与对象一起使用时的含义。反射 API 可用于替换值。


import java.lang.reflect.Field;

class B {
    public static void main(String[] args) throws Exception {
        String value = "Old value";
        System.out.println(value);

        // Get the String class's value field
        Field field = value.getClass().getDeclaredField("value");
        // Make it mutable
        field.setAccessible(true);
        // Set a new value
        field.set(value, "CodeGym".toCharArray());

        System.out.println(value);

        /* Output:
         * Old value
         * CodeGym
         */
    }
}
请注意,如果我们尝试以这种方式更改原始类型的最终变量,那么什么也不会发生。我建议您说服自己:例如,创建一个带有 final int 字段的 Java 类,并尝试使用 Reflection API 更改它的值。祝你们好运!