杰瑞科技汇

final修饰的变量到底能不能变?

final 是 Java 中的一个关键字,它表示“最终的”、“不可变的”,当用它来修饰变量时,意味着这个变量的引用一旦被初始化后,就不能再指向其他对象

final修饰的变量到底能不能变?-图1
(图片来源网络,侵删)

这里有一个非常重要的区别,我们需要根据变量的类型来理解它的具体含义。


final 修饰基本数据类型

final 修饰一个基本数据类型(如 int, double, char, boolean 等)的变量时,它的含义非常直接和明确:

一旦给这个变量赋了初始值,就不能再修改这个值。

示例代码:

final修饰的变量到底能不能变?-图2
(图片来源网络,侵删)
public class FinalPrimitiveExample {
    // 在声明时初始化
    final int a = 10;
    // 在构造方法中初始化
    final int b;
    public FinalPrimitiveExample() {
        this.b = 20; // 合法
        // this.b = 30; // 编译错误!b 已经被初始化,不能再赋值
    }
    public static void main(String[] args) {
        FinalPrimitiveExample example = new FinalPrimitiveExample();
        System.out.println("a = " + example.a); // 输出 a = 10
        System.out.println("b = " + example.b); // 输出 b = 20
        // example.a = 100; // 编译错误!无法为 final 变量 a 指定值
    }
}

关键点:

  • 值不可变a 的值永远是 10,不能被改变。
  • 初始化时机final 基本类型变量必须在使用前被初始化,通常有两个地方:
    1. 声明时:如 final int a = 10;
    2. 构造方法中:如示例中的 b,这允许在对象创建时动态确定其值,但之后不能修改。

final 修饰引用数据类型

final 修饰一个引用数据类型(如自定义的类、数组、String 等)的变量时,它的含义就比较微妙了:

一旦给这个引用变量初始化(即让它指向一个对象),就不能再让它指向其他对象。

它所指向的对象内部的 state(状态,即成员变量)是可以被修改的。

final修饰的变量到底能不能变?-图3
(图片来源网络,侵删)

示例代码:

class Person {
    String name;
    public Person(String name) {
        this.name = name;
    }
}
public class FinalReferenceExample {
    // final 引用,在声明时初始化
    final Person p1 = new Person("Alice");
    // final 引用,在构造方法中初始化
    final Person p2;
    public FinalReferenceExample() {
        this.p2 = new Person("Bob");
    }
    public static void main(String[] args) {
        FinalReferenceExample example = new FinalReferenceExample();
        // --- 不可操作 ---
        // example.p1 = new Person("Carol"); // 编译错误!p1 不能指向新的对象
        // example.p2 = new Person("David"); // 编译错误!p2 不能指向新的对象
        // --- 可以操作 ---
        // 修改 p1 所指向的对象的内部状态
        example.p1.name = "Alice Updated";
        System.out.println("p1's name is now: " + example.p1.name); // 输出 p1's name is now: Alice Updated
        // 修改 p2 所指向的对象的内部状态
        example.p2.name = "Bob Updated";
        System.out.println("p2's name is now: " + example.p2.name); // 输出 p2's name is now: Bob Updated
    }
}

图解说明:

  1. final Person p1 = new Person("Alice");

    • p1 是一个引用,它像一根指向 Person 对象的箭头。
    • final 保证了这根箭头不能再指向别处。
  2. example.p1.name = "Alice Updated";

    • 这句话没有改变 p1 这个引用本身,而是通过 p1 找到它指向的那个 Person 对象,然后修改了该对象内部的 name 字段。
    • 箭头还是指着同一个对象,但对象的内容变了。

final 修饰的特殊成员:static final

finalstatic 一起使用时,它定义的是一个静态常量

  • static:表示这个变量属于,而不是某个对象,所有实例共享这一个变量。
  • final:表示它的值一旦初始化就不能改变。

约定俗成: static final 常量的命名通常使用全大写字母,单词之间用下划线 _ 分隔。

示例代码:

public class MathConstants {
    // PI 是一个静态常量
    public static final double PI = 3.14159;
    // MAX_VALUE 是一个静态常量
    public static final int MAX_VALUE = 100;
    public static void main(String[] args) {
        System.out.println("PI is: " + MathConstants.PI);
        System.out.println("MAX_VALUE is: " + MathConstants.MAX_VALUE);
        // MathConstants.PI = 4.0; // 编译错误!无法为 final 变量 PI 指定值
    }
}

关键点:

  • 初始化时机static final 变量必须在类加载时静态初始化块中进行初始化。
  • 全局唯一:它在整个应用程序的生命周期中都是不变的,是全局共享的。

final 修饰参数

final 也可以用来修饰方法的参数,这表示在方法内部,你不能修改这个参数的引用(对于基本类型,就是不能修改其值)。

示例代码:

public class FinalParameterExample {
    public void processValue(final int value) {
        // value = 20; // 编译错误!不能修改 final 参数的值
        System.out.println("Processed value: " + value);
    }
    public void processObject(final Person person) {
        // person = new Person("Eve"); // 编译错误!不能修改 final 参数的引用
        person.name = "Eve Updated"; // 合法!可以修改对象内部状态
        System.out.println("Processed person's name: " + person.name);
    }
    public static void main(String[] args) {
        FinalParameterExample example = new FinalParameterExample();
        example.processValue(10);
        Person p = new Person("Frank");
        example.processObject(p);
    }
}

用途:

  • 这是一种安全措施,防止方法内部意外地修改传入的参数。
  • 在匿名内部类中,如果需要访问外部方法的局部变量,该局部变量也必须被声明为 final(在 Java 8+ 中,如果变量值未被修改,final 是隐式的)。

总结表格

修饰符 变量类型 含义 初始化时机 示例
final 基本类型 不可变 声明时或构造方法中 final int speed = 60;
final 引用类型 引用不可变(不能指向新对象),但对象可变 声明时或构造方法中 final List<String> names = new ArrayList<>();
static final 任意类型 静态常量,全局共享,值不可变 类加载时或静态初始化块中 public static final String APP_NAME = "MyApp";
final 方法参数 在方法内部,参数的引用/值不可变 调用方法时传入 public void doSomething(final int id)

为什么使用 final

  1. 安全性与不变性:创建不可变对象是编程中一个非常重要的设计原则。final 是实现不可变性的关键。String 类就是 final 的,它的内部 value 数组也是 final 的,保证了 String 对象一旦创建,内容就永远不会变,这极大地增强了代码的安全性,特别是在多线程环境下。
  2. 性能优化:JVM 和编译器可以对 final 变量进行优化。final 变量可以被内联(inline),避免额外的内存访问。
  3. 提高代码可读性:看到 final 关键字,开发者立刻明白这个变量是一个常量,不应该被修改,使代码意图更清晰。
  4. 线程安全:不可变对象天生就是线程安全的,因为它们的状态在创建后就不会改变,不需要额外的同步机制。

希望这个详细的解释能帮助你完全理解 Java 中 final 修饰的变量!

分享:
扫描分享到社交APP
上一篇
下一篇