java中final和effective final 的区别

94 阅读3分钟

背景

在函数式编程学习中,学习闭包的时候,认识到了一个新词 effective final 之前没有了解过,于是查找,做下记录,具体内容如下

查询结果

在Java中,finaleffective final 是两个相关但不同的概念,它们都与变量的不可变性有关,但适用的场景和含义有所不同。

final 关键字

final 是一个关键字,可以应用于类、方法和变量(包括实例变量、静态变量和局部变量)。对于不同类型的元素,final 的作用也有所不同:

  • :当一个类被声明为 final 时,它不能被继承。这意味着你不能创建该类的子类。
  • 方法:当一个方法被声明为 final 时,它不能被子类重写。这可以用于防止子类改变父类的方法行为。
  • 变量:当一个变量被声明为 final 时,它的值一旦被初始化就不能再被修改。对于基本数据类型,这意味着它的值是固定的;对于对象引用,这意味着引用本身不能指向另一个对象,但是对象内部的状态是可以改变的(除非该对象自身是不可变的)。
final int x = 10; // 基本数据类型
x = 20; // 错误: 不能重新赋值

final StringBuilder sb = new StringBuilder("Hello");
sb.append(" World"); // 正确: 对象状态可以改变
sb = new StringBuilder("Goodbye"); // 错误: 不能重新赋值引用

effective final

effective final 并不是一个关键字,也不是Java语言的正式术语,而是指一种编程实践或特性。一个变量被称为 effective final,如果它虽然没有被显式地声明为 final,但在它的整个生命周期中只被初始化一次,并且之后不再被修改。这样的变量在功能上等同于 final 变量。

重要应用场景

  • Lambda表达式:从Java 8开始,Lambda表达式只能访问局部变量或方法参数,如果这些变量或参数是 final 或者 effective final。这是因为Lambda表达式可能会在不同的线程中执行,而Java需要确保这些变量不会在Lambda表达式的生命周期内发生变化,以避免并发问题。
public class LambdaExample {
    public static void main(String[] args) {
        int num = 1; // effective final
        Runnable r = () -> System.out.println(num); // 正确: 使用了 effective final 变量
        num = 2; // 如果在此处修改 num,则会编译错误
        r.run();
    }
}
  • 匿名内部类:类似地,在匿名内部类中,你只能引用那些是 finaleffective final 的局部变量和方法参数。
public class AnonymousInnerClassExample {
    public static void main(String[] args) {
        int value = 42; // effective final
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(value); // 正确: 使用了 effective final 变量
            }
        }).start();
        // value = 43; // 如果在此处修改 value,则会编译错误
    }
}

总结

  • final 是一个明确的修饰符,用来声明类、方法或变量为不可变的。
  • effective final 是指那些实际上未被重新赋值的局部变量或方法参数,即使它们没有被显式地声明为 final。这种特性使得它们可以在Lambda表达式和匿名内部类中安全使用。

理解这两者的区别有助于编写更清晰、更安全的代码,尤其是在使用Lambda表达式和匿名内部类时。