Java基础(十一):关键字final详解 - 详解

Java基础系列文章

Java基础(一):初识Java——发展历程、技术体系与JDK环境搭建

Java基础(二):八种基本数据类型详解

Java基础(三):逻辑运算符详解

Java基础(四):位运算符详解

Java基础(五):流程控制全解析——分支(if/switch)和循环(for/while)的深度指南

Java基础(六):数组全面解析

Java基础(七): 面向过程与面向对象、类与对象、成员变量与局部变量、值传递与引用传递、方法重载与方法重写

Java基础(八):封装、继承、多态与关键字this、super详解

Java基础(九):Object核心类深度剖析

Java基础(十):关键字static详解

Java基础(十一):关键字final详解

一、final概述

  • final是Java中的一个修饰符,可以用来修饰方法变量。它的核心含义是"不可改变的"
    • final变量:一旦初始化就不能重新赋值
    • final方法:不能被子类重写
    • final类:不能被继承

二、final修饰变量

1、final基本类型变量

  • 当final修饰基本类型变量时,该变量的值一旦被初始化后就不能再改变
final int MAX_VALUE = 100;
// MAX_VALUE = 200; // 编译错误,不能修改final变量的值

特点:

  • 必须在声明时或构造方法/静态块中初始化
  • 一旦赋值后不能再修改
  • 对于基本类型,值本身不可变

2、final引用类型变量

  • 当final修饰引用类型变量时,引用本身不可变(即不能指向其他对象),但对象的内容可以改变
final List<
String> names = new ArrayList<
>();
names.add("Alice");
// 允许,修改对象内容
// names = new ArrayList<>(); // 编译错误,不能改变引用

特点:

  • 引用不可变 ≠ 对象不可变
  • final只保证引用不变,不保证被引用对象的内容不变

3、final变量的初始化时机

  • 对于实例变量:可以在声明时构造代码块构造方法中初始化
  • 对于静态变量:可以在声明时静态初始化块中初始化
public class Example
{
// 实例变量
final int instanceVar1 = 10;
// 声明时初始化
final int instanceVar2;
final int instanceVar3;
{
instanceVar2 = 20;
// 实例初始化块(构造代码块)初始化
}
Example() {
instanceVar3 = 30;
// 构造方法中初始化
}
// 静态变量
static final int STATIC_VAR1 = 10;
// 声明时初始化
static final int STATIC_VAR2;
static {
STATIC_VAR2 = 20;
// 静态初始化块中初始化
}
}

三、final修饰方法

  • 当final修饰方法时,该方法不能被子类重写(override)
class Parent
{
public final void forbiddenMethod() {
System.out.println("我不能被重写");
}
public void normalMethod() {
System.out.println("我可以被重写");
}
}
class Child
extends Parent {
// @Override
// public void forbiddenMethod() { } // 编译错误:不能重写final方法
@Override
public void normalMethod() {
System.out.println("父类方法被重写了");
}
}

设计考虑:

  • 防止子类修改关键方法​:确保方法的行为不会被子类改变
    • 常将框架中的关键方法声明为final
    • 当不希望子类改变特定行为时使用
  • JVM可以对final方法进行内联优化:直接将方法体插入调用处,减少方法调用的开销

四、final修饰类

  • 当final修饰类时,该类不能被继承
final class StringUtils
{
public static boolean isEmpty(String str) {
return str == null || str.trim().isEmpty();
}
}
// class ExtendedUtils extends StringUtils { } // 编译错误:不能继承final类

常见的final类:

  • String
  • Integer
  • Double
  • Math
  • System

设计考虑:

  1. 不可变类​:如String类,确保对象状态不会被改变
  2. 安全性​:防止通过继承破坏类的安全性或不变性
  3. 设计意图​:明确表示该类不应有子类
  4. 工具类​:如Math,只包含静态方法,不需要实例化或继承

五、final修饰参数

public void process(final int param1, final List<
String> param2) {
// param1 = 10; // 编译错误
// param2 = new ArrayList<>(); // 编译错误
param2.add("value");
// 允许,修改对象内容
}

设计考虑:

  1. 防止方法内部意外修改参数引用
  2. 提高代码可读性,明确参数不应被重新赋值

六、final在并发编程中的应用

  • final字段在并发编程中非常重要,因为JVM保证final字段的初始化安全
class SafePublication
{
final int safeValue;
int unsafeValue;
public SafePublication(int value) {
this.safeValue = value;
// 正确初始化
this.unsafeValue = value;
// 可能发生重排序
// 在构造函数返回后,safeValue对所有线程立即可见
}
public int getSafeValue() {
return safeValue;
// 总是能看到正确初始化的值
}
public int getUnsafeValue() {
return unsafeValue;
// 可能看到默认值0
}
}

Java内存模型对final字段有特殊规定:

  • 在构造函数中对final字段的写入,与随后将被构造对象的引用赋值给一个引用变量,这两个操作不能重排序
  • 初次读包含final字段的对象的引用,与随后初次读这个final字段,这两个操作不能重排序

这意味着​:正确构造的对象的final字段,对于所有线程都是可见的,无需额外的同步措施

posted @ 2025-09-09 22:25  yfceshi  阅读(6)  评论(0)    收藏  举报