final关键字

核心概念

final是Java中的一个重要关键字,可以修饰类、方法和变量,提供了不可变性和性能优化的能力。理解final的使用场景和底层机制,是编写高质量Java代码的关键。


一、final修饰变量

1.1 基本用法

public class FinalDemo {
    // 1. 编译期常量
    public static final int CONSTANT = 100;

    // 2. 运行时常量(必须在构造函数中初始化)
    private final int runtimeConstant;

    public FinalDemo(int value) {
        this.runtimeConstant = value;
    }

    // 3. final局部变量
    public void method() {
        final int localFinal = 200;
        // localFinal = 300;  // ❌ 编译错误
    }
}

1.2 引用类型的final

public class FinalReferenceDemo {
    // final引用:引用不可变,但对象可以修改
    private final List<String> list = new ArrayList<>();

    public void modifyList() {
        // ✅ 可以修改列表内容
        list.add("Hello");
        list.remove(0);

        // ❌ 不能重新赋值引用
        // list = new ArrayList<>();  // 编译错误
    }

    // final参数
    public void processData(final User user) {
        // user = new User();  // ❌ 编译错误
        user.setName("New Name");  // ✅ 可以修改对象内部状态
    }
}

1.3 final的内存语义

public class FinalMemoryDemo {
    // final变量的内存可见性
    private final int x;
    private final int y;

    public FinalMemoryDemo() {
        x = 1;  // 写入final变量
        y = 2;  // happens-before原则
    }

    public void method() {
        // x和y的值对所有线程可见
        System.out.println(x + y);
    }
}

二、final修饰方法

2.1 防止方法重写

public class Parent {
    // 不允许子类重写
    public final void method() {
        System.out.println("Cannot be overridden");
    }
}

class Child extends Parent {
    // ❌ 编译错误
    // @Override
    // public void method() { }
}

2.2 性能优化

public class OptimizationDemo {
    // JVM可以内联final方法,提高性能
    public final int calculate(int a, int b) {
        return a + b;
    }
}

三、final修饰类

3.1 阻止继承

// 不允许被继承
public final class ImmutableClass {
    private final int value;

    public ImmutableClass(int value) {
        this.value = value;
    }
}

// ❌ 编译错误
// class SubClass extends ImmutableClass { }

3.2 不可变类的设计

public final class Money {
    private final int amount;
    private final String currency;

    public Money(int amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }

    // 只读方法
    public int getAmount() {
        return amount;
    }

    public String getCurrency() {
        return currency;
    }

    // 创建新对象,而不是修改
    public Money add(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new IllegalArgumentException("Cannot add different currencies");
        }
        return new Money(this.amount + other.amount, this.currency);
    }
}

四、final的性能优化

4.1 编译器优化

public class OptimizationDemo {
    // 编译器可以进行常量折叠
    public static final int MAGIC_NUMBER = 42;

    public int calculate() {
        // 编译后可能直接替换为 return 84;
        return MAGIC_NUMBER * 2;
    }
}

4.2 JVM内联

public class InlineDemo {
    // final方法更容易被JVM内联
    public final int add(int a, int b) {
        return a + b;
    }

    public void process() {
        int result = add(10, 20);  // 可能被直接替换
    }
}

五、高频面试题

Q1: final和finally有什么区别?

答:

  • final:修饰不可变的变量、方法或类
  • finally:异常处理中的finally块,无论是否异常都会执行

Q2: final变量必须在声明时初始化吗?

答:

  • 静态final变量:必须在声明时或静态块中初始化
  • 实例final变量:可以在声明、构造函数或初始化块中初始化

Q3: final修饰引用类型变量时什么意思?

答:

  • 引用不可变:不能重新赋值
  • 对象内部状态可以修改
  • 类似于引用的"只读"

Q4: final方法的性能优势是什么?

答:

  • JVM可以内联final方法
  • 编译器可以进行更多优化
  • 方法调用开销降低

Q5: 为什么String类是final的?

答:

  • 线程安全
  • 字符串常量池需要不可变性
  • 防止被恶意继承和修改

六、最佳实践

✅ 推荐做法

// 1. 常量使用final
public static final double PI = 3.14159;

// 2. 不可变类设计
public final class ImmutableUser {
    private final String name;
    private final int age;
}

// 3. 方法性能优化
public final int calculate(int a, int b) {
    return a + b;
}

❌ 避免做法

// 1. 避免过度使用final
public final class TooRestrictive {
    // 过度限制,降低灵活性
}

// 2. final不等于完全不可变
public final class WeakImmutability {
    private final List<String> list;
    // list内容仍可以修改
}

相关链接


记忆口诀

final修饰不可变,变量方法类为先
引用不能重新指,对象内部可更新
性能优化靠内联,编译器帮你加速间
不可继承不可改,安全高效是真谛

⬅️ 面向对象设计的SOLID原则 🏠 00-Java ➡️ static关键字