深入理解 Java Final:从基础到线程安全与性能优化

1 修饰属性或变量

无论属性是基本类型、引用类型,都使变量里存放的"值"不可变。

常和static关键字协作,作为常量:

  • 基本类型,变量放的是实实在在的值,如1,"abc"
  • 引用类型,变量放的是个地址,所以final修饰引用类型变量指里面的地址不能变,即它只能指向初始时指向的那个对象,不关心指向的对象内容的变化

所以修饰的变量必须初始化:

java 复制代码
public static final String LOAN = "loan";
LOAN = new String("loan") //invalid compilation error
  • 定义时
  • 初始化块中,但不可在静态初始化块中,静态的final实例变量才可以在静态初始化块中
  • 构造方法中,但静态final实例变量不可以在其中

final变量只读!

2 修饰方法

该方法可被继承,但不许被任何子类重写。

调用final方法时,直接将方法主体插入到调用处,而非进行方法调用,这样能提高程序效率(内联机制)。

如认为一个方法功能够完整,子类中不需要改变,可声明为final。final方法比非final方法快,因为在编译时候已静态绑定,无需在运行时再动态绑定。

java 复制代码
class PersonalLoan{
    public final String getName(){
        return "personal loan";
    }
}
 
class CheapPersonalLoan extends PersonalLoan{
    @Override
    public final String getName(){
        return "cheap personal loan"; //compilation error: overridden method is final
    }
}

3 修饰类

使用final来修饰的类叫作final类。

final类通常功能是完整的,不能被继承。Java中有许多类是final,如String、Interger及其他包装类。类不可被继承,但这并非表示final类的实例变量也不可变,除非给实例变量也增加final修饰。

java 复制代码
final class PersonalLoan{
}
 
class CheapPersonalLoan extends PersonalLoan{  //compilation error: cannot inherit from final class
 
}

一个类不可同时被abstract和final修饰。

4 final关键字的好处

  • 提高性能:JVM和Java应用都会缓存final变量
  • final变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销
  • 使用final关键字,JVM会对方法、变量及类进行优化

5 不可变类

创建不可变类要使用final关键字。不可变类是指它的对象一旦被创建了就不能被更改了。String是不可变类的代表。不可变类有很多好处,譬如它们的对象是只读的,可以在多线程环境下安全的共享,不用额外的同步开销等等。

6 其他重要知识点

  • 🈲对final变量再赋值
  • 本地变量须在声明时赋值
  • 在匿名类中,所有变量都须final
  • 接口中声明的所有变量本身是final
  • final和abstract这两个关键字反相关,final类不能abstract
  • final方法在编译阶段绑定,称为静态绑定(static binding)
  • 没在声明时初始化final变量的称为空白final变量(blank final variable),须在构造器中初始化,或调用this()初始化。不这么做的话,编译器会报错"final变量(变量名)需要进行初始化"

final变量就是常量,常量名通常大写:

java 复制代码
private final int COUNT = 10;

对于集合对象声明为final指的是引用不能被更改,但是你可以向其中增加,删除或者改变内容。譬如:

java 复制代码
private final List Loans = new ArrayList();
list.add("home loan");  //valid
list.add("personal loan"); //valid
loans = new Vector();  //not valid

7 可安全设为 final 的字段

此项检查会报告那些可安全地声明为 final 的字段。所有 final 字段都有一个值,并且这个值在初始化后不会改变,可使代码更易理解和推断。

范围与限制

为了避免过于耗时的分析,此检查仅在以下情况下报告字段: 字段具有 private 修饰符,或字段定义在局部类或匿名类中。 一个字段可以被设为 final,如满足以下条件:

  • 字段是static的。字段在其声明或一个 static 初始化块中被精确地初始化一次
  • 字段是非static的。字段在其声明、一个实例初始化块或类的每一个构造函数 中被精确地初始化一次

且字段在其他任何地方都未被修改。

示例:

java 复制代码
 public class Person {
  private String name;

  Person(String name) {
  this.name = name;
  }

  public String getName() {
  return name;
  }
 }

修复后:

java 复制代码
 public class Person {
  private final String name;

  Person(String name) {
  this.name = name;
  }

  public String getName() {
  return name;
  }
 }

使用 "Annotations" (注解) 按钮来修改注解列表。这些注解会被认为隐含了对字段的写入操作(这会阻止字段被标记为 final)。

8 有趣现象

java 复制代码
byte b1 = 1;
byte b2 = 3;
// 当程序执行到这一行的时候会出错
// 因b1、b2可自动转换成int型变量,运算时JVM对它进行转换,结果导致把一个int赋值给byte
byte b3 = b1 + b2;  // Error: Type mismatch: cannot convert from int to byte
final byte b1=1;
final byte b2=3;

// 不会出错,看了上面的解释就知道原因
byte b3=b1+b2;

8.1 现象一:byte b3 = b1 + b2; 报错

Java 的算术运算类型提升 (Integer Promotion):对小于 int 的整型(即 byte, short, char)进行算术运算(如 +, -, *, /),它们的操作数会先被自动提升(promote)为 int 类型,然后执行运算。

因此,b1 + b2 表达式实际按 (int)b1 + (int)b2 来执行的。两个 int 类型相加,其结果必然是 int 类型

表达式 b1 + b2 的结果是 int 类型(值为 4),而变量 b3 被声明为 byte 类型。将一个 int 类型的值赋给 byte 类型的变量属于窄化原始类型转换 (Narrowing Primitive Conversion)

Java 编译器不允许这种可能导致精度丢失的隐式窄化转换。因为 int 的范围(-2^31 到 2^31-1)远大于 byte 的范围(-128 到 127),直接赋值可能会丢失信息。所以编译器会报错,提示类型不匹配。

如果确实需要将结果赋给 byte,必须进行强制类型转换:

java 复制代码
byte b3 = (byte)(b1 + b2); // 显式强制转换,编译通过

8.2 final byte b3 = b1 + b2不报错

byte 变量被 final 修饰且在声明时用常量(字面量)初始化时,b1b2 成为编译时常量 (Compile-time constants)

常量折叠 (Constant Folding): Java 编译器会对涉及编译时常量的表达式进行常量折叠 优化。即编译器在编译阶段就直接计算出 b1 + b2 的结果。它看到 1 + 3,直接计算得到常量 4

编译时检查常量值: 此时,赋值语句 byte b3 = b1 + b2; 在编译期间实际上被看作是 byte b3 = 4;。Java 编译器特殊规则:如果一个 int 类型的常量值 (字面量或编译时常量表达式的结果)在 byte 类型的表示范围内(-128 到 127),那么编译器允许将这个 int 常量隐式地赋值byte 变量。

赋值通过: 因为 4byte 的范围 [-128, 127] 之内,所以编译器认为这个赋值是安全的,不会导致信息丢失,因此编译通过。

本文已收录在Github关注我,紧跟本系列专栏文章,咱们下篇再续!

  • 🚀 魔都架构师 | 全网30W技术追随者
  • 🔧 大厂分布式系统/数据中台实战专家
  • 🏆 主导交易系统百万级流量调优 & 车联网平台架构
  • 🧠 AIGC应用开发先行者 | 区块链落地实践者
  • 🌍 以技术驱动创新,我们的征途是改变世界!
  • 👉 实战干货:编程严选网

本文由博客一文多发平台 OpenWrite 发布!

相关推荐
kyle~19 分钟前
ROS2---std_msgs基础消息包
开发语言·python·机器人·ros·机器人操作系统
满怀101520 分钟前
【NumPy科学计算引擎:从基础操作到高性能实践】
开发语言·python·numpy
&zzz1 小时前
Python生成exe
开发语言·python
Python×CATIA工业智造1 小时前
基于PySide6与pycatia的CATIA绘图比例智能调节工具开发全解析
python·pycharm·自动化·catia二次开发
vsropy3 小时前
matlab安装python API 出现Invalid version: ‘R2022a‘,
开发语言·python
atec20005 小时前
使用uv管理python项目环境
开发语言·python·uv
zybishe6 小时前
免费送源码:Java+ssm+MySQL 酒店预订管理系统的设计与实现 计算机毕业设计原创定制
java·大数据·python·mysql·微信小程序·php·课程设计
农民小飞侠8 小时前
ubuntu 安装pyllama教程
linux·python·ubuntu
橘猫云计算机设计8 小时前
基于Python电影数据的实时分析可视化系统(源码+lw+部署文档+讲解),源码可白嫖!
数据库·后端·python·信息可视化·小程序·毕业设计
蹦蹦跳跳真可爱5898 小时前
Python----机器学习(基于贝叶斯的鸢尾花分类)
python·机器学习·分类