final关键字用在三种情况下,数据、方法和类。
1.final数据,如果是基本数据类型,那么表明,这个基本数据必须在定义的时候初始化或者使用前进行初始化。如果是对象引用的话,那么表明它不能再指向其他的其他的对象,但是引用的对象本身是可以改变的。
FinalData.java
import java.util.*;
class Value{
int i;
public Value(int i){this.i=i;}
}
public class FinalData{
private static Random rand =new Random();
private String id;
public FinalData(String id){this.id=id;}
private final int VAL_ONE=9;
private static final int VAL_TWO=99;//final的基本常量采用全部字母大写,并且字与字时间用下划线的形式
private final int i4=rand.nextInt(20);
static final int i5= rand.nextInt(20);
private Value v1=new Value(11);
private final Value v2=new Value(22);
private static final Value v3=new Value(33);
public String toString(){
return id+": "+"i4= "+i4+", i5="+i5+" VAL_ONE= "+VAL_ONE;
}
public static void main(String[] args){
FinalData fd1=new FinalData("fd1");
//fd1.VAL_ONE++; //不能修改final的基本数据类型
//fd1.v2=new Value(22);//不能修改引用对象
fd1.v2.i++;
FinalData fd2=new FinalData("fd2");
System.out.println(fd1);
System.out.println(fd2);
}
}
打印结果:
fd1: i4= 10, i5=12 VAL_ONE= 9
fd2: i4= 13, i5=12 VAL_ONE= 9
结果表明,编译时常量(在编译时就能确定的final数据)和static final对象是不会改变的。
Java还允许在参数列表中以声明的形式将参数指明为final的,如下
void with (final String s){
……
}
说明在with函数中你可以读s这个参数,但是不能改变它。
2.final函数
FinalMethod.java
class WithFinal{
public final void f(){
System.out.println("WithFinal.f()");
}
}
public class FinalMethod extends WithFinal {
public void f(){
System.out.println("FinalMethod.f()");
}
public static void main(String[] args){
FinalMethod fm=new FinalMethod();
fm.f();
WithFinal wf=new WithFinal();
wf.f();
}
}
上面的代码是不能通过编译的,因为如果一个方法申明为final,那么继承类就不能将其覆盖。如果将继承类的f()函数删除,那么打印输入如下:
WithFinal.f()
WithFinal.f()
即final想要确保在继承中使方法行为保持不变,不会覆盖。如果将final除掉,那么输出为:
FinalMethod.f()
WithFinal.f()
即基类的方法被覆盖,但基类的对象调用f()还是调用基类本身的函数,而不是继承类的函数,这点要注意。
现在将final前面public改成private看看会发生什么情况:
wf.f()行报错,说引用了private函数(说明wf引用对象调用的是基类的函数,而不是子类的f()),我们将此行注释掉,结果通过了编译,运行结果如下:
FinalMethod.f()
这是为什么呢?不是继承类不能覆盖基类的方法吗?
答案:“覆盖”只有在某方法使在基类的接口的一部分时才会出现,也就是说基类中的private方法并不是基类的接口,只是一些隐藏在类中的程序代码而已。所以此时并没有“覆盖”f()方法,而是在子类中生成了一个新的方法而已。
3.final类
final类容易理解,当某个类定义为final时,则不能继承此类。