Java 中的 final 修饰符

Java 中 final 修饰符的使用。

final 修饰变量

我们知道,final 修饰符可用来修饰变量。

final 修饰变量(基本数据类型,或引用数据类型)时,三大原则

  1. 当定义的 final 变量为成员变量(类或结构中的属性)时,必须在定义时就明确设定它的初始值,否者直接会报错无法通过编译,并且如果再次对其进行赋值将会提示编译错误。
  2. 当定义的 final 变量为 方法中(方法内,或方法块内)的局部变量 时,可以直接定义时赋值,也可以由开发者在声明后,第一次使用该变量之前,显式的赋初始值,实例化该变量;
  3. 当定义的 final 变量为 方法上的局部变量(参数) 时,是为了防止数据在方法体中被修改,这时参数的值(或引用)在方法体内是不能不能被重新赋值的。

根据 Java 中数据类型的分类,这里 final 修饰符修饰的变量的使用场景我们需要注意以下两种,使用上是有细微差异的(两个注意):

  • final 修饰符修饰基本数据类型的变量
  • final 修饰符修饰引用数据类型的变量

1 –> 修饰基本数据类型的变量时

final 修饰基本数据类型的变量时,基本类型的值是不能够改变的。

2 –> 修饰引用数据类型的变量时

final 修饰引用数据类型的变量时,引用类型变量所指的引用是不能够改变的(地址不变),但是引用类型变量的值是可以改变的(地址所指向存储空间值可变)。

详细使用样例差异,可见下文原则 1~3 中样例。


原则一

当定义的 final 变量为成员变量(类或结构中的属性)时,必须在定义时就明确设定它的初始值,否者直接会报错无法通过编译,并且如果再次对其进行赋值将会提示编译错误。

1 –> 基本数据类型

1
2
3
4
5
6
7
8
9
10
public class HelloJava {

static final int i = 3; // 声明时,显式初始化

public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(i);
}

}

关于 final 前面的 staitc 修饰符,这里可以不用深究,你就当没看见。

输入:

1
2
0
3

2 –> 引用数据类型

1
2
3
4
5
6
7
8
9
10
public class HelloJava {

static final String str1 = "this is a test"; // 声明时,显式初始化

public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(str1);
}

}

输入:

1
2
null
this is a test

下面这样的表达可以么?(先忽略之后的注释)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class HelloJava {

static final int i; // 编译不通过,声明时,未初始化
static final int j = 3;
static final String str1; // 编译不通过,声明时,未初始化
static final String str2 = null;

static final int[] a_test = {2,3,4};

public static void main(String[] args) {
// TODO Auto-generated method stub
j = 1; // 编译错误,再次对 final 修饰成员变量进行赋值操作
System.out.println(j);
System.out.println(str2);

a_test = {1,2}; // 编译错误,引用类型变量所指的引用是不能够改变的
a_test[0] = 2; // 编译可通过,引用类型变量的值是可以改变的
}

}

===========================================

final 修饰的成员变量的另一种赋值方法(构造函数):

1
2
3
4
5
6
7
public class FinalDemo {
final int age;

public FinalDemo(int age){
this.age=age; // 编译通过
}
}

你可以想想上述赋值方法成立的原因….


原则二

当定义的 final 变量为 方法中(方法内,或方法块内)的局部变量 时,可以直接定义时赋值,也可以由开发者在声明后,第一次使用该变量之前,显式的赋初始值,实例化该变量。

1 –> 基本数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
public class HelloJava {

public static void main(String[] args) {

final int i_test = 3; // 变量声明时,进行初始化
System.out.println(i_test);

final int j_test;
j_test = 1; // 变量声明后,使用前,进行初始化
System.out.println(j_test);
}

}

输出:

1
2
3
1

2 –> 引用数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
public class HelloJava {

public static void main(String[] args) {
// TODO Auto-generated method stub
final String i_test = "abc";
System.out.println(i_test);

final String j_test;
j_test = "def";
System.out.println(j_test);
}

}

输出:

1
2
abc
def

下面这样的表达可以么?(先忽略之后的注释)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class HelloJava {

public static void main(String[] args) {
// TODO Auto-generated method stub
int i = 1;
final String str;
if (i > 0) {
str = "this is a test";
str = "test"; // 编译错误,块内使用前多次初始化赋值
str.length(); // 编译可通过,块内使用前已初始化赋值
} else {
str.length(); // 编译错误,块内使用前未初始化赋值
}
}

}

原则三

当定义的 final 变量为 方法上的局部变量(参数) 时,是为了防止数据在方法体中被修改,这时参数的值(或引用)在方法体内是不能被重新赋值的。

但需要谨记上文的 两个注意

1 –> 基本数据类型

1
2
3
4
5
6
7
8
9
10
11
public class HelloJava {

public static void main(String[] args) {
// TODO Auto-generated method stub
}

public static void checkValue(final int a) {
a = 234; // 编译错误,基本类型的值在方法内部是不能够改变的
}

}

2 –> 引用数据类型

1
2
3
4
5
6
7
8
9
10
11
12
public class HelloJava {

public static void main(String[] args) {
// TODO Auto-generated method stub
}

public static void checkValue(final int[] a) {
a = {2,3,4}; // 编译错误,a 变量的引用是不能够改变的
a[0] = 3; // 编译可通过,a 变量所指向的存储中的值是能够修改的
}

}

final 修饰类

final 修饰类时,可以继承其他类,但是不可以被继承的,使用方式跟其它类一样。

1
2
3
class Parent{}
final class Person extends Parent{}
class child extends Person{}

final 修饰方法

final 修饰方法时,不可以被覆盖(不可以重写),但可以继承使用。

1
2
3
4
5
6
7
8
9
class Parent {
// final 修饰的方法,不可以被覆盖,但可以继承使用
public final void method1(){}
public void method2(){} // 普通方法
}
class Child extends Parent {
// 普通 method2 方法,支持重写
public final void method2(){}
}

Author

Waldeinsamkeit

Posted on

2017-10-13

Updated on

2021-01-04

Licensed under

You need to set install_url to use ShareThis. Please set it in _config.yml.

Comments

You forgot to set the shortname for Disqus. Please set it in _config.yml.