深入理解 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 发布!

相关推荐
精灵vector1 小时前
构建专家级SQL Agent交互
python·aigc·ai编程
Zonda要好好学习1 小时前
Python入门Day2
开发语言·python
Vertira2 小时前
pdf 合并 python实现(已解决)
前端·python·pdf
太凉2 小时前
Python之 sorted() 函数的基本语法
python
项目題供诗2 小时前
黑马python(二十四)
开发语言·python
晓13132 小时前
OpenCV篇——项目(二)OCR文档扫描
人工智能·python·opencv·pycharm·ocr
是小王同学啊~3 小时前
(LangChain)RAG系统链路向量检索器之Retrievers(五)
python·算法·langchain
AIGC包拥它3 小时前
提示技术系列——链式提示
人工智能·python·langchain·prompt
孟陬3 小时前
Python matplotlib 如何**同时**展示正文和 emoji
python
何双新3 小时前
第 1 课:Flask 简介与环境配置(Markdown 教案)
后端·python·flask