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修饰基本类型变量时,该变量的值一旦被初始化后就不能再改变
java 复制代码
final int MAX_VALUE = 100;
// MAX_VALUE = 200; // 编译错误,不能修改final变量的值

特点:

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

2、final引用类型变量

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

特点:

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

3、final变量的初始化时机

  • 对于实例变量:可以在声明时构造代码块构造方法中初始化
  • 对于静态变量:可以在声明时静态初始化块中初始化
java 复制代码
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)
java 复制代码
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修饰类时,该类不能被继承
java 复制代码
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修饰参数

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

设计考虑:

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

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

  • final字段在并发编程中非常重要,因为JVM保证final字段的初始化安全
java 复制代码
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字段,对于所有线程都是可见的,无需额外的同步措施

相关推荐
Zane199414 小时前
遍历时删元素为什么报错:modCount 这个"计数器"在背后盯着你
java·后端
编码浪子14 小时前
Rust 智能指针深度实战:Box/Rc/Arc/RefCell 选型决策与内存管理避坑
开发语言·后端·rust
yichudu15 小时前
Python 装饰器 decorator
开发语言·python
电商API_1800790524715 小时前
速卖通商品采集API技术文章
java·开发语言·c++·api·跨境电商·商品详情
一晌小贪欢15 小时前
Python办公14:批量解压文件夹内的所有 ZIP/RAR 并自动分类
开发语言·python·excel·数据可视化·python办公
郑州光合科技余经理15 小时前
海外版外卖系统架构:订单怎么流转、权限怎么分
java·开发语言·前端·系统架构·uni-app·php·ai编程
Python 实战手记15 小时前
企业微信主体变更公证书办理全解析:适用场景、材料规范、踩坑点与Python信息校验脚本
开发语言·python·企业微信
longxiaozhang615 小时前
C#异常处理:程序出错了怎么办?
开发语言·数据库·c#
郝学胜-神的一滴15 小时前
Effective Python 条款 1:确认你正在使用的 Python 版本
开发语言·数据结构·python·程序人生·算法
一木 之林15 小时前
五、C++新特性、关键字与编译原理
java·jvm·算法