final
final 是 Java 中的一个关键字,简而言之,final 的作用意味着“这是无法改变的”。不过由于 final 关键字一共有三种用法,它可以用来修饰变量、方法或者类,而且在修饰不同的地方时,效果、含义和侧重点也会有所不同。
final 修饰变量
关键字 final 修饰变量的作用是很明确的,那就是意味着这个变量一旦被赋值就不能被修改了,也就是说只能被赋值一次,直到天涯海角也不会“变心”。如果我们尝试对一个已经赋值过 final 的变量再次赋值,就会报编译错误。如果我们尝试对一个已经赋值过 final 的变量再次赋值,就会报编译错误。
代码示例:
/**
* 描述: final变量一旦被赋值就不能被修改
*/
public class FinalVarCantChange {
public final int finalVar = 0;
public static void main(String[] args) {
FinalVarCantChange finalVarCantChange = new FinalVarCantChange();
// finalVarCantChange.finalVar=9; //编译错误,不允许修改final的成员变量
}
}
为什么要对某个变量去加 final 关键字呢?主要有以下两点目的。
第一个目的是出于设计角度去考虑的,比如我们希望创建一个一旦被赋值就不能改变的量,那么就可以使用 final 关键字。比如声明常量的时候,通常都是带 final 的:
public static final int YEAR = 2021;
第二个目的是从线程安全的角度去考虑的。不可变的对象天生就是线程安全的,所以不需要我们额外进行同步等处理,这些开销是没有的。如果 final 修饰的是基本数据类型,那么它自然就具备了不可变这个性质,所以自动保证了线程安全。
赋值时机**
下面我们就来看一下被 final 修饰的变量的赋值时机,变量可以分为以下三种:
- 成员变量,类中的非 static 修饰的属性;
- 静态变量,类中的被 static 修饰的属性;
- 局部变量,方法中的变量。
1)成员变量
第一种是在声明变量的等号右边直接赋值,例如:
public class FinalFieldAssignment1 { private final int finalVar = 0; }
第二种是在构造函数中赋值,例如: ```java class FinalFieldAssignment2 {
private final int finalVar;
public FinalFieldAssignment2() {
finalVar = 0;
}
}
- 第三种就是在类的构造代码块中赋值(不常用),例如:
```java
class FinalFieldAssignment3 {
private final int finalVar;
{
finalVar = 0;
}
}
2)静态变量
静态变量是类中的 static 属性,它被 final 修饰后,只有两种赋值时机。
第一种同样是在声明变量的等号右边直接赋值,例如:
/** * 描述: 演示final的static类变量的赋值时机 */ public class StaticFieldAssignment1 { private static final int a = 0; }
第二种赋值时机就是它可以在一个静态的 static 初始代码块中赋值,这种用法不是很多,例如: ```java class StaticFieldAssignment2 {
private static final int a;
static {
a = 0;
}
}
3)局部变量<br />局部变量指的是方法中的变量,如果你把它修饰为了 final,它的含义依然是**一旦赋值就不能改变**。<br />它的赋值时机和前两种变量是不一样的,因为它是在方法中定义的,所以它没有构造函数,也同样不存在初始代码块,所以对应的这两种赋值时机就都不存在了。实际上,对于 final 的局部变量而言,它是不限定具体赋值时机的,只要求我们**在使用之前必须对它进行赋值**即可。
```java
/**
* 描述: 本地变量的赋值时机:使用前赋值即可
*/
public class LocalVarAssignment1 {
public void foo() {
final int a = 0;//等号右边直接赋值
}
}
class LocalVarAssignment2 {
public void foo() {
final int a;//这是允许的,因为a没有被使用
}
}
class LocalVarAssignment3 {
public void foo() {
final int a;
a = 0;//使用前赋值
System.out.println(a);
}
}
特殊用法:final 修饰参数
关键字 final 还可以用于修饰方法中的参数。在方法的参数列表中是可以把参数声明为 final 的,这意味着我们没有办法在方法内部对这个参数进行修改**。
/**
* 描述: final参数
*/
public class FinalPara {
public void withFinal(final int a) {
System.out.println(a);//可以读取final参数的值
// a = 9; //编译错误,不允许修改final参数的值
}
}
final 修饰方法
我们使用 final 去修饰方法的唯一原因,就是想把这个方法锁定,意味着任何继承类都不能修改这个方法的含义,也就是说,被 final 修饰的方法不可以被重写,不能被 override。
/**
* 描述: final的方法不允许被重写
*/
public class FinalMethod {
public void drink() {
}
public final void eat() {
}
}
class SubClass extends FinalMethod {
@Override
public void drink() {
//非final方法允许被重写
}
// public void eat() {}//编译错误,不允许重写final方法
// public final SubClass() {} //编译错误,构造方法不允许被final修饰
}
final 修饰类
下面我们再来看下 final 修饰类的情况,final 修饰类的含义很明确,就是这个类“不可被继承”。我们举个代码例子:
/**
* 描述: 测试final class的效果
*/
public final class FinalClassDemo {
//code
}
//class A extends FinalClassDemo {}//编译错误,无法继承final的类
不变性
如果对象在被创建之后,其状态就不能修改了,那么它就具备“不变性”。
“被 final 修饰的变量意味着一旦被赋值就不能修改”,而这个规则对于基本类型的变量是没有歧义的,但是对于对象类型而言,final 其实只是保证这个变量的引用不可变,而对象本身依然是可以变化的。这一点同样适用于数组,因为在 Java 中数组也是对象**。那我们就来举个例子,看一看以下 Java 程序的输出:
class Test {
public static void main(String args[]) {
final int arr[] = {1, 2, 3, 4, 5}; // 注意,数组 arr 是 final 的
for (int i = 0; i < arr.length; i++) {
arr[i] = arr[i]*10;
System.out.println(arr[i]);
}
}
}
打印出来结果如下:
10
20
30
40
50
final 修饰一个指向对象的变量的时候,对象本身的内容依然是可以变化的。
**