final关键字可以用于变量申明,一旦该变量被设定后,就不可以再改变变量的值。通常由final定义的变量为常量。例如在类中定义的PI值,可以使用如下语句。
final double PI=314;
当在程序在使用到PE这个常量时,它的值就是3.14,如果在程序中再次对定义为final的常量赋值,编译器奖不会接受这样一个错误。
final关键字定义的变量必须在声明时对其进行赋值操作。final除了可以修饰基本数据类型的常量之外,还可以修饰对象引用。由于数组也可以被看作一个对象引用,所以final 可修饰数组。一旦一个对象引用被修饰为final,它只能恒定指向一个对象,无法将其改变指向另一个对象。一个既是static又是 final的字段只占据一段不能改变的存储空间。为了深入了解final关键字。来看下面的实例。
package com.wsy;
import static java.lang.System.out;
import java.util.Random;
class Test{
int i=0;
}
public class FinalData {
/**
* @param args
* final数据的验证
*/
static Random rand=new Random();
private final int VALUE_1=9;
private static final int VALUE_2=10;
private final Test test=new Test();
private Test test2=new Test();
private final int[] a={1,2,3,4,5,6};
private final int i4=rand.nextInt(20);
private static final int i5=rand.nextInt(20);
public String toString(){
return i4+" "+i5+" ";
}
public static void main(String[] args) {
// TODO 自动生成方法存根
FinalData data=new FinalData();
//out.print(data.test.i++);
//data.test=new Test();//不能将定义为final引用指向其他对象
//data.value2++;//不能改变定义为final的常量值
data.test2=new Test();
for(int i=0;i<data.a.length;i++){
//a[i]=9;//不能对定义为final的数组赋值
}
out.println(data);
out.println("data2");
out.println(new FinalData());
//out.println(data);
}
}
我们知道一个被定义为final的对象引用只能指向唯一一个对象,不可以将它再指向其他对象,但是一个对象本身的值却可以改变的。那么为了使一个常量真正做不到更改,可以将常量声明为static final。
package com.wsy;
import java.util.Random;
import static java.lang.System.out;
public class FinalStaticData {
/**
* @param args
*/
private static Random rand=new Random();
private final int a1=rand.nextInt(10);
private static final int a2=rand.nextInt(10);
public static void main(String[] args) {
// TODO 自动生成方法存根
FinalStaticData fdata=new FinalStaticData();
out.println("实例化对象调用a1的值:"+fdata.a1);
out.println("实例化对象调用a1的值:"+fdata.a2);
FinalStaticData fdata2=new FinalStaticData();
out.println("重新实例化对象调用a1的值:"+fdata2.a1);
out.println("重新实例化对象调用a2的值:"+fdata2.a2);
}
}
定义为final的常量不是恒定不变的,将随机数辅予定义final的常量中那个,可以做到每次运行程序就改变a1,a2的值。但a2与a1不同,由于它被声明为static final形式,所以在内存中为a2开辟一个恒定不变的区域。当再次实例化一个FinalStaticData对象时,仍然指向a2这块内存区域,所以a2是在装载的时候被初始化,而不是每次创建新对象时候都被初始化;a1会在重新实例化对象时被更改。
final方法
首先读者应该是了解定义为final的方法不能被重写。
将方法定义为final类型可以防止任何子类修改该类的定义与实现方式,同时定义为final的方法等效率要高于非final方法。在修饰权限中曾经提到过程private修饰符,如果一个父类的某个方法被设置为private修饰符,子类将无法访问该方法,自然无法覆盖该方法,所以一个定义为private的方法隐式中被指定为final类型,这样无须将一个定义为private的方法再定义为final类型。
从本实例可以看出,作为final方法不能被覆盖,例如doit2()方法就不能在子类中被重写,但是在父类中定义了一个static final的diot() 方法,同时在子类中也定义了一个doit()方法,从表面上来看,在子类中覆盖了父类的doit()方法,但是覆盖必须可以满足一个对象向上转型为它的基本类型并调用相同方法这样一个条件,例如在主方法中使用Parents p=s;语句执行向上转型操作,对象p只能调用正常覆盖的doit3()方法,却不能调用doit()方法,可见在子类中的doit()方法并不是正常覆盖,此时只是生成一个新的方法而已。
package com.wsy;
class Parents{
private final void doit(){
System.out.println("父类.doit()");
}
final void doit2(){
System.out.println("父类.doit2()");
}
public void doit3(){
System.out.println("父类.doit3()");
}
}
class Sub extends Parents{
public final void doit(){
System.out.println("子类.doit()");
}
// final void doit2(){ //final方法不能覆盖
// System.out.println("子类.doit2()");
// }
public void doit3(){
System.out.println("子类.doit3()");
}
}
public class FinalMethod {
public static void main(String[] args) {
// TODO 自动生成方法存根
Sub s=new Sub();
s.doit();
Parents p=s;
//p.doit();//不能调用private方法
p.doit2();
p.doit3();
}
}