final的各种用法与意义
final 是 Java 中的一个关键字,简而言之,final 的作用意味着“这是无法改变的”。
不过由于 final 关键字一共有三种用法,它可以用来修饰变量、方法或者类,而且在修饰不同的地方时,效果、含义和侧重点也会有所不同,所以我们需要把这三种情况分开介绍。
final 修饰变量
关键字 final 修饰变量的作用是很明确的,那就是意味着这个变量一旦被赋值就不能被修改了,也就是说只能被赋值一次,直到天涯海角也不会“变心”。如果我们尝试对一个已经赋值过 final 的变量再次赋值,就会报编译错误。
public class FinalVariable {
public final int objVar = 0;
public static final int clsVar = 0;
public static void main(String[] args) {
FinalVariable finalVariable = new FinalVariable();
finalVariable.objVar = 1;
clsVar = 1;
}
}
在这个例子中,我们有两个 final 修饰的 in,然后在 main 函数中尝试去修改它的值,此时会报编译错误,所以这体现了 final 修饰变量的一个最主要的作用:一旦被赋值就不能被修改了。
目的
看完了它的作用之后,我们就来看一下使用 final 的目的,也就是为什么要对某个变量去加 final 关键字呢?主要有以下两点目的。
- 第一个目的是出于设计角度去考虑的,比如我们希望创建一个一旦被赋值就不能改变的量,那么就可以使用 final 关键字。比如声明常量的时候,通常都是带 final 的
- 第二个目的是从线程安全的角度去考虑的。不可变的对象天生就是线程安全的,所以不需要我们额外进行同步等处理,这些开销是没有的。如果 final 修饰的是基本数据类型,那么它自然就具备了不可变这个性质,所以自动保证了线程安全,这样的话,我们未来去使用它也就非常放心了。
赋值时机
下面我们就来看一下被 final 修饰的变量的赋值时机,变量可以分为以下三种:
- 成员变量,类中的非 static 修饰的属性;
- 静态变量,类中的被 static 修饰的属性;
- 局部变量,方法中的变量。
成员变量
成员变量指的是一个类中的非 static 属性,对于这种成员变量而言,被 final 修饰后,它有三种赋值时机(或者叫作赋值途径)。
- 第一种是在声明变量的等号右边直接赋值,例如:
public class FinalFieldAssignment1 {
private final int finalVar = 0;
}
- 第二种是在构造函数中赋值,例如:
class FinalFieldAssignment2 {
private final int finalVar;
public FinalFieldAssignment2() {
finalVar = 0;
}
}
- 第三种就是在类的构造代码块中赋
class FinalFieldAssignment3 {
private final int finalVar;
{
finalVar = 0;
}
}
需要注意的是,这里讲了三种赋值时机,我们必须从中挑一种来完成对 final 变量的赋值。如果不是 final 的普通变量,当然可以不用在这三种情况下赋值,完全可以在其他的时机赋值;或者如果你不准备使用这个变量,那么自始至终不赋值甚至也是可以的。但是对于 final 修饰的成员变量而言,必须在三种情况中任选一种来进行赋值,而不能一种都不挑、完全不赋值,那是不行的,这是 final 语法所规定的。
下面讲解一种概念:“空白 final”。如果我们声明了 final 变量之后,并没有立刻在等号右侧对它赋值,这种情况就被称为“空白 final”。这样做的好处在于增加了 final 变量的灵活性,比如可以在构造函数中根据不同的情况,对 final 变量进行不同的赋值,这样的话,被 final 修饰的变量就不会变得死板,同时又能保证在赋值后保持不变。
/**
* 描述: 空白final提供了灵活性
*/
public class BlankFinal {
//空白final
private final int a;
//不传参则把a赋值为默认值0
public BlankFinal() {
this.a = 0;
}
//传参则把a赋值为传入的参数
public BlankFinal(int a) {
this.a = a;
}
}
在这个代码中,我们有一个 private final 的 int 变量叫作 a,该类有两个构造函数,第一个构造函数是把 a 赋值为 0,第二个构造函数是把 a 赋值为传进来的参数,所以你调用不同的构造函数,就会有不同的赋值情况。
这样一来,利用这个规则,我们就可以根据业务去给 final 变量设计更灵活的赋值逻辑。所以利用空白 final 的一大好处,就是可以让这个 final 变量的值并不是说非常死板,不是绝对固定的,而是可以根据情况进行灵活的赋值,只不过一旦赋值后,就不能再更改了。
静态变量
静态变量是类中的 static 属性,它被 final 修饰后,只有两种赋值时机。
第一种同样是在声明变量的等号右