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

相关推荐
该用户已不存在6 小时前
Node.js 做 Web 后端优势为什么这么大?
javascript·后端·node.js
on the way 1237 小时前
Spring WebFlux 流式数据拉取与推送的实现
java·后端·spring
风一样的树懒7 小时前
P0:消息序列化失败,Failed Fast导致无限重启
后端
龙在天7 小时前
分库分表下的分页查询,到底怎么搞?
前端·后端
小蒜学长7 小时前
基于Hadoop的网约车公司数据分析系统设计(代码+数据库+LW)
java·大数据·数据库·hadoop·spring boot·后端
tingyu7 小时前
FastJSON解析异常踩坑记录:一个让人头疼的JSON转换问题
java·后端
Jiezcode7 小时前
Qt QJsonObject
c++·后端·qt
文心快码BaiduComate7 小时前
AI界的“超能力”MCP,到底是个啥?
前端·后端·程序员
bobz9657 小时前
华为防火墙支持配置 IPSec 先分片后加密的功能
后端