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字段,对于所有线程都是可见的,无需额外的同步措施

相关推荐
星释3 小时前
Rust 练习册 :Leap与日期计算
开发语言·后端·rust
码事漫谈7 小时前
C++死锁深度解析:从成因到预防与避免
后端
码事漫谈7 小时前
智能体颠覆教育行业:现状、应用与未来展望调研报告
后端
蓝-萧7 小时前
【玩转全栈】----Django基本配置和介绍
java·后端
priority_key7 小时前
排序算法:堆排序、快速排序、归并排序
java·后端·算法·排序算法·归并排序·堆排序·快速排序
韩立学长7 小时前
基于Springboot的旧时月历史论坛4099k6s9(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·后端
汤姆yu8 小时前
基于SpringBoot的动漫周边商场系统的设计与开发
java·spring boot·后端
灰小猿8 小时前
Spring前后端分离项目时间格式转换问题全局配置解决
java·前端·后端·spring·spring cloud
RedJACK~10 小时前
Go Ebiten小游戏开发:扫雷
开发语言·后端·golang
老夫的码又出BUG了10 小时前
分布式Web应用场景下存在的Session问题
前端·分布式·后端