Java关键字之final

简介: Java final 关键字

1 前言

final 的意思是不变的,一般来说可以用于以下三种场景:

  1. 被 final 修饰的类,表明该类是无法继承的;
  2. 被 final 修饰的方法,表明该方法是无法覆写的;
  3. 被 final 修饰的变量,说明该变量在声明的时候,就必须初始化完成,而且以后也不能修改其内存地址。

注意:对于 List、Map 这些集合类来说,被 final 修饰后,是可以修改其内部值的,但却无法修改其初始化时的内存地址。

2 修饰的对象

final 可以用来修饰变量、字段、方法和类。

2.1 变量

可以将变量声明为 final 。final 变量的值一旦分配了就无法再修改了。在一次赋值之前,final 变量必须是明确未赋值的,否则会产生编译错误。如果 final 变量包含对对象的引用,则对象的状态可能会通过对对象的操作进行更改,但变量将始终引用相同的对象。这同样适用于数组,因为数组也是对象;如果一个 final 变量持有一个数组的对象,则可以通过对数组的操作修改数组中的元素,但是该变量始终会引用相同的数组。

注意,如果 final 用于局部变量,那么它可以不用初始化,而如果用于类中的某个字段,那么它必须进行初始化,否则会产生编译错误。

空 final 是指其声明缺少初始化器的 final 变量。

常数变量是一个用常量表达式初始化的基本类型或String类型的 final 变量。 变量是否为常数变量与类的初始化,二进制兼容性和明确赋值有关。

有三种变量会被隐式声明为 final :接口中的字段,try-with-resources 语句中的局部变量以及multi-catch子句中的异常参数。 一个单一的 catch 子句中的 exception 参数永远不会隐式声明为 final ,但是它可以被认为效果等同于 final 。

某些未被声明为 final 变量实际上可以认为效果等同于 final 变量:

  • 声明带有初始化器且满足下列所有条件的局部变量:
  • 永远不会作为赋值操作符的左操作数出现
  • 永远不会作为前缀或后缀递增或递减操作符的操作数出现
  • 声明缺少初始化器且满足下列所有条件的局部变量:
  • 无论何时它作为赋值操作符的左操作数出现,都是明确未赋过值的,并且在本次赋值操作前未被明确赋值。即它是明确未赋过值的,并且在本次赋值操作的右操作数之后未被明确赋值
  • 永远不会作为前缀或后缀递增或递减操作符的操作数出现
  • 方法、构造器、lambda 和异常参数在明确其是否可以认定为等同于 final 时可以被认为是声明带有初始化器的局部变量

如果变量在效果上等同于 final ,那么对其声明添加 final 修饰符并不会引入任何编译错误。相反地,在合法程序中被声明为 final 的局部变量和参数,如果将它们的 final 修饰符移除,那么它们在效果上会等同于 final 。

2.2 类

类可以被声明为 final ,如果一个 final 类的名字出现在另一个类声明的 extends 子句中,会产生编译错误,这意味着 final 类不能有任何子类。

如果一个类同时被声明为 final 和 abstract ,也会产生编译错误,因为这样的类是永远无法实现的。

因为一个 final 类不能有任何子类,所以 final 类的方法永远都不会被覆写。

如果将一个没有被声明的 final 类声明为 final ,那么当这个类的已有子类的二进制文件被加载时,就会抛出一个 VerifyError ,因为 final 类不能有任何子类。因此不推荐在广泛分布的类中进行这种变更。

反之,将一个声明为 final 的类不再声明为 final ,不会破坏与已有二进制文件的兼容性。

2.3 方法

方法可以被声明为 final 以阻止子类覆写或者隐藏它。

如果试图覆写或者隐藏 final 方法,会产生编译错误。

private 方法和所有在 final 类中声明的方法表现就如 final方法一样,因为它们是不可能被覆写的。

在运行时,机器码生成器或优化器可以“内联” final 方法的方法体,将方法的调用替换为其方法体中的代码。内联处理必须保留方法调用的语义。特别是,如果实例方法调用的目标为null,那么即使该方法是内联的,也必须抛出空指针异常。一个Java编译器必须确保异常在正确的位置上被抛出,这样在方法调用之前,传递给方法的实际参数将被按正确的顺序计算过。

2.4 字段

字段可以被声明为 final 。类和实例变量(static 和 非 static 字段)都可以被声明为 final。

空 final 类变量必须在声明它的类的静态初始化器中赋过值,否则会产生编译错误。

空 final 实例变量必须在声明它的类的每一个构造器的末尾赋过值,否则会产生编译错误。

参考资料

目录
相关文章
|
1月前
|
设计模式 安全 Java
Java并发编程实战:使用synchronized关键字实现线程安全
【4月更文挑战第6天】Java中的`synchronized`关键字用于处理多线程并发,确保共享资源的线程安全。它可以修饰方法或代码块,实现互斥访问。当用于方法时,锁定对象实例或类对象;用于代码块时,锁定指定对象。过度使用可能导致性能问题,应注意避免锁持有时间过长、死锁,并考虑使用`java.util.concurrent`包中的高级工具。正确理解和使用`synchronized`是编写线程安全程序的关键。
|
13天前
|
Java API
【JAVA】final、finally、finalize 有什么区别?
【JAVA】final、finally、finalize 有什么区别?
|
10天前
|
Java
final 在 java 中有什么作用?
final 在 java 中有什么作用?
|
11天前
|
Java 编译器
【Java探索之旅】this 关键字 解决你的成员变量困惑
【Java探索之旅】this 关键字 解决你的成员变量困惑
17 0
|
11天前
|
Java
【Java探索之旅】我与Java的初相识(完):注释,标识符,关键字
【Java探索之旅】我与Java的初相识(完):注释,标识符,关键字
5 0
|
11天前
|
Java
Java里的关键字 __final
Java里的关键字 __final
|
13天前
|
Java 编译器
【JAVA】volatile 关键字的作用
【JAVA】volatile 关键字的作用
|
14天前
|
Java
【JAVA面试题】final关键字的作用有哪些
【JAVA面试题】final关键字的作用有哪些
|
16天前
|
安全 Java 编译器
是时候来唠一唠synchronized关键字了,Java多线程的必问考点!
本文简要介绍了Java中的`synchronized`关键字,它是用于保证多线程环境下的同步,解决原子性、可见性和顺序性问题。从JDK1.6开始,synchronized进行了优化,性能得到提升,现在仍可在项目中使用。synchronized有三种用法:修饰实例方法、静态方法和代码块。文章还讨论了synchronized修饰代码块的锁对象、静态与非静态方法调用的互斥性,以及构造方法不能被同步修饰。此外,通过反汇编展示了`synchronized`在方法和代码块上的底层实现,涉及ObjectMonitor和monitorenter/monitorexit指令。
26 0
|
16天前
|
Java
两千字讲明白java中instanceof关键字的使用!
两千字讲明白java中instanceof关键字的使用!
18 0