## 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` 修饰且在声明时用常量(字面量)初始化时,`b1` 和 `b2` 成为**编译时常量 (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` 变量。 **赋值通过:** 因为 `4` 在 `byte` 的范围 [-128, 127] 之内,所以编译器认为这个赋值是安全的,不会导致信息丢失,因此编译通过。 本文已收录在[Github](https://github.com/Java-Edge/Java-Interview-Tutorial),**关注我,紧跟本系列专栏文章,咱们下篇再续!** - 🚀 魔都架构师 | 全网30W技术追随者 - 🔧 大厂分布式系统/数据中台实战专家 - 🏆 主导交易系统百万级流量调优 & 车联网平台架构 - 🧠 AIGC应用开发先行者 | 区块链落地实践者 - 🌍 以技术驱动创新,我们的征途是改变世界! - 👉 实战干货:[编程严选网](http://www.javaedge.cn/)