基本概念
final是Java中是一个保留的关键字,可以声明变量(包括成员变量[实例变量和静态变量]和局部变量)、方法和类。它通常指“这是无法改变的”。将变量、方法和类声明为final,JVM能够对其进行优化,进而提升性能。
其关键知识点如下:
- final方法是在编译器绑定的,即静态绑定(可参考Java学习笔记之深入理解动态绑定和静态绑定)
- final**成员变量**必须在声明的时候初始化或者在构造器中初始化
- final**局部变量**必须在使用前进行初始化赋值
- 当final变量是基本数据类型时,其数值恒定不变,且不能被更改;当final变量是引用类型时,其引用恒定不变,即不能再指向其它对象,但其引用指向的对象本身却是可以被修改的
- final方法不能被重写
- final类不能被继承
- 类中所有的private方法都隐式地指定为final
final变量
无论是成员变量(包括实例变量和静态变量)还是局部变量,只要声明为final,就必须在使用前进行初始化,否则在编译期时出现错误。值得注意的是,成员变量如果没有声明为final,那么在使用前是可以不用进行初始化的,因为它们有默认值。(可参考Java学习笔记之变量类型)
例1:
import java.util.ArrayList;
public class FinalVarDemo {
int var; // It's OK!默认值为0
//final int var_final; // It's not OK! 必须在声明的时候初始化或者在构造器中初始化
final int var_instance = 0;
final ArrayList<String> arrayList = new ArrayList<>();
void test(){
//final int var_local; // It's not OK!局部变量在使用前必须进行初始化赋值
final int var_local = 1;
//var_local = 2; // It's not OK!final局部变量值不能更改
//var_instance = 1; // It's not OK!final实例变量值不能被更改
//arrayList = new ArrayList<>(); // It's not OK!不能将final引用类型变量指向新的对象
arrayList.add("hello"); // It's OK!可以改变对象本身
arrayList.add("world");
System.out.println("var = "+var);
System.out.println("var_instance = "+var_instance);
System.out.println("var_local = "+var_local);
System.out.println("arrayList = "+arrayList);
}
public static void main(String[] srgs){
FinalVarDemo finalVarDemo = new FinalVarDemo();
finalVarDemo.test();
}
}
输出结果:
var = 0
var_instance = 0
var_local = 1
arrayList = [hello, world]
final参数
Java允许在参数列表中以声明的方法将参数指明为final。
例2:
class Dog{
void bark(){}
}
public class FinalArg {
void withFinal(final Dog dog){
//dog = new Dog(); // Illegal! --dog is final
}
void withoutFinal(Dog dog){
dog = new Dog(); // OK! --dog is not final
}
int f(final int i){
//i++; // Illegal! --i is final,can't change
return i + 1;
}
int g(int i){
i++; // OK! --i is not final
return i;
}
}
分析:将方法中的参数指定为final,意味着无法在方法中更改参数引用所指向的对象,如果是基本数据类型参数,不能改变其值,只能读取。这一特性,一般用来向匿名内部类传递数据。
final方法
方法前面加上final关键字,代表这个方法不可以被子类的方法重写。final方法比非final方法要快,因为在编译的时候已经静态绑定了,不需要在运行时再动态绑定。
例3:
class Animal{
private final void f(){
System.out.println("Animal.f()");
}
private void g(){
System.out.println("Animal.g()");
}
}
class Dog extends Animal{
public final void f(){
System.out.println("Dog.f()");
}
public void g(){
System.out.println("Dog.g()");
}
}
class Teddy extends Dog{
// It's not OK!final方法不能被覆盖
// public final void f(){
// System.out.println("Teddy.f()");
// }
public void g(){
System.out.println("Teddy.g()");
}
}
public class FinalMethodDemo {
public static void main(String[] args){
Teddy teddy = new Teddy();
teddy.f();
teddy.g();
Dog teddy1 = new Teddy();
teddy1.f();
teddy1.g();
Animal teddy2 = new Teddy();
// teddy2.f(); // It's not OK!final方法不能被覆盖
// teddy2.g();
}
}
输出结果:
Dog.f()
Teddy.g()
Dog.f()
Teddy.g()
结果分析:需要指明的是类中所有的private方法都隐式的指定为final。而final方法是不能被覆盖的。但从例3中的代码可以看出,覆盖一个private方法(隐含是final的)似乎是奏效的。类Dog
继承于类Animal
,并且覆盖了两个private方法,而且编译也没有出错。那么问题来了,谁说final方法是不能被覆盖的?一般来讲,重写方法使用的是动态绑定,如果类Dog
真的是重写了类Animal
中的两个private方法,那么输出应该是Animal.f()
而不是Dog.f()
,所以可以肯定的是,类Dog
中的这两个方法并不是继承自类Animal
,它只不过是具有相同的名称,仅此而已!当然,这说的是private方法,对于public final方法,如果你想要覆盖该方法,编译会出现错误提示,该方法不能被覆盖,正如例3中的代码写的一样。
final类
当将某个类定义为final时,表示该类不能被继承。final类通常功能是完整的。Java中有许多这样的类,如String,Interger以及其他包装类。
final class Animal{
}
//compilation error: cannot inherit from final class
class Dog extends Animal{
}
final, finally, finalize的区别
- final 用于声明属性,方法和类, 分别表示属性不可变, 方法不可覆盖, 类不可继承
- finally 是异常处理语句结构的一部分,表示总是执行
- finalize 是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等. JVM不保证此方法总被调用
参考资料
- Java编程思想
- 深入理解Java中的final关键字