Final 关键字的用法
一、final 在 Java中的用法
1、final类
final类不能被继承,因此final类的成员方法没有机会被覆盖,默认都是final的。在设计类时候,如果这个类不需要有子类,类的实现细节不允许改变,并且确信这个类不会再被扩展,那么就设计为final类。 final方法不能被子类的方法覆盖,但可以被继承。
2、final方法
如果一个类不允许其子类覆盖某个方法,则可以把这个方法声明为final方法。
使用final方法的原因有二:
第一、把方法锁定,防止任何继承类修改它的意义和实现。
第二、高效。编译器在遇到调用final方法时候会转入内嵌机制,大大提高执行效率。
3、final变量(常量)
用final修饰的成员变量表示常量,只能被赋值一次,赋值后值无法改变!
final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。
另外,final变量定义的时候,可以先声明,而不给初值,这种变量也称为final空白,无论什么情况,编译器都确保空白final在使用之前必须被初始化。但是,final空白在final关键字final的使用上提供了更大的灵活性,为此,一个类中的final数据成员就可以实现依对象而有所不同,却有保持其恒定不变的特征。
4、final参数
当函数参数为final类型时,你可以读取使用该参数,但是无法改变该参数的值。
final不能用于修饰构造方法。
注:父类的private成员方法是不能被子类方法覆盖的,因此private类型的方法默认是final类型的。
二、final 数据
下面例子展示了 final 属性的使用:
class Value {
int i; // package access
Value(int i) {
this.i = i;
}
}
public class FinalData {
private static Random rand = new Random(47);
private String id;
public FinalData(String id) {
this.id = id;
}
// 带有编译时值的final基本类型,它们都可用作编译时常量
private final int valueOne = 9;
private static final int VALUE_TWO = 99;
// public意味着可以在包外访问,static强调只有一个,final说明是一个常量。
public static final int VALUE_THREE = 39;
// 不是编译时常量,因为i4 和 INT_5 都是在运行时才能被赋值
//不同对象的 i4 值可能是不同的
private final int i4 = rand.nextInt(20);
//不同对象的 INT_5 值是同一个
static final int INT_5 = rand.nextInt(20);
private Value v1 = new Value(11);
//对于被final修饰的引用,引用指向的对象不能进行修改,但是对象的属性或者值可以进行修改
private final Value v2 = new Value(22);
private static final Value VAL_3 = new Value(33);
// 数组也是对象,同理如上
private final int[] a = {1, 2, 3, 4, 5, 6};
@Override
public String toString() {
return id + ": " + "i4 = " + i4 + ", INT_5 = " + INT_5;
}
public static void main(String[] args) {
FinalData fd1 = new FinalData("fd1");
//- fd1.valueOne++; // 不能修改被final修饰的基本数据类型
fd1.v2.i++; // 可以修改被final修饰的引用指向的对象的属性
fd1.v1 = new Value(9); // 可以修改引用指向的对象,因为没有被final修饰
for (int i = 0; i < fd1.a.length; i++) {
fd1.a[i]++; // 原理如 fd1.v2.i++
}
//- fd1.v2 = new Value(0); // 不能修改v2引用指向的对象
//- fd1.VAL_3 = new Value(1); // 如上
//- fd1.a = new int[3]; // 如上
System.out.println(fd1);
System.out.println("Creating new FinalData");
FinalData fd2 = new FinalData("fd2");
System.out.println(fd1);
System.out.println(fd2);
}
}
运行结果:
fd1: i4 = 15, INT_5 = 18
Creating new FinalData
fd1: i4 = 15, INT_5 = 18
fd2: i4 = 13, INT_5 = 18
对于基本类型,final 使数值恒定不变,而对于对象引用,final 使引用恒定不变。一旦引用被初始化指向了某个对象,它就不能改为指向其他对象。但是,对象本身是可以修改的,Java 没有提供将任意对象设为常量的方法。(你可以自己编写类达到使对象恒定不变的效果)这一限制同样适用数组,数组也是对象。
三、空final
空白 final 指的是没有初始化值的 final 属性。编译器确保空白 final 在使用前必须被初始化。这样既能使一个类的每个对象的 final 属性值不同,也能保持它的不变性。
class Poppet {
private int i;
Poppet(int ii) {
i = ii;
}
}
public class BlankFinal {
private final int i = 0; // 正常定义的final常量
private final int j; // 空final
private final Poppet p; // Blank final reference
// 空final必须在构造方法中进行初始化,如果未进行初始化,则编译器会报错
public BlankFinal() {
j = 1;
p = new Poppet(1);
}
public BlankFinal(int x) {
j = x;
p = new Poppet(x);
}
public static void main(String[] args) {
new BlankFinal();
new BlankFinal(47);
}
}
说白了,如果用final修饰数据,咱们就必须在你使用之前进行初始化,可以直接在构造函数中或者在属性定义时进行初始化。
四、final方法和private
使用 final 方法的原因有两个。第一个原因是给方法上锁,防止子类通过覆写改变方法的行为。这是出于继承的考虑,确保方法的行为不会因继承而改变。
class WithFinals {
// 使用private和final共同修饰,和只使用private修饰效果一样
private final void f() {
System.out.println("WithFinals.f()");
}
// 自动添加了 final
private void g() {
System.out.println("WithFinals.g()");
}
}
class OverridingPrivate extends WithFinals {
private final void f() {
System.out.println("OverridingPrivate.f()");
}
private void g() {
System.out.println("OverridingPrivate.g()");
}
}
class OverridingPrivate2 extends OverridingPrivate {
public final void f() {
System.out.println("OverridingPrivate2.f()");
}
public void g() {
System.out.println("OverridingPrivate2.g()");
}
}
public class FinalOverridingIllusion {
public static void main(String[] args) {
OverridingPrivate2 op2 = new OverridingPrivate2();
op2.f();
op2.g();
// 可以向上转型,但是不能调用op.f()或者op.g()
OverridingPrivate op = op2;
//- op.f();
//- op.g();
// Same here:
WithFinals wf = op2;
//- wf.f();
//- wf.g();
}
}
如果放开 op2.f() 的注释,编译器会报红,如果进行编译,会提示 java: f() 在 reuse.OverridingPrivate 中是 private 访问控制
,因为如果是使用 private 或者 final 修饰的方法,它只是隐藏在类内部的代码。在 WithFinals 、 OverridingPrivate、 OverridingPrivate2中, **f() **和 g() 都是属于各自的方法,他们之间不存在任何联系,细心的小伙伴可能会发现在 OverridingPrivate 和 **OverridingPrivate2 **没有 @override 注解,如果强行加上 @override 注解,编译器会报错,由此看来,它们之间并没有重写父类的 private 方法,只是恰好有相同的命名而已。这也不难解释为什么调用 op.f() 编译器会报错。
五、final类
当说一个类是 final (final 关键字在类定义之前),就意味着它不能被继承。之所以这么做,是因为类的设计就是永远不需要改动,或者是出于安全考虑不希望它有子类。
class SmallBrain {}
final class Dinosaur {
int i = 7;
int j = 1;
SmallBrain x = new SmallBrain();
void f() {}
}
//- class Further extends Dinosaur {}
public class Jurassic {
public static void main(String[] args) {
Dinosaur n = new Dinosaur();
n.f();
n.i = 40;
n.j++;
}
}
如果放开注释,显而易见的会出现error: Cannot extend final class ‘Dinosaur‘
错误,因为 Dinosaur 是不能被继承的。当然,被 final 修饰的类,其中包含的方法都被隐式的定义为 final 。