1.finla变量关键字可用于变量声明,一旦该变量被设定,就不可以再改变该变量的值,通常,有final定义的变量为常量
final关键字定义的变量必须在声明时对其进行赋值定义,final除了可以修饰基本数据类型的常量,还可以修饰对象引用,由于数组也可以被看成一个对象的引用,所以final可以修饰数组,一旦一个对象引用被修饰成final后,它只能恒定指向一个对象,无法将其改变指定另一个对象,一个既是static又是final的字段只占据一段不能改变的存储空间,以下面的例子深入了解final:
例:
import static java.lang.System.*; import java.util.*; class Test { int i = 0; } public class FinalData { static Random rand = new Random(); private final int VALUE_1 = 9; // 声明一个final常量 private static final int VALUE_2 = 10; // 声明一个final、static常量 private final Test test = new Test(); // 声明一个final引用 private Test test2 = new Test(); // 声明一个不是final的引用 private final int[] a = { 1, 2, 3, 4, 5, 6 }; // 声明一个定义为final的数组 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) { FinalData data = new FinalData(); // data.test=new Test(); //可以对指定为final的引用中的成员变量赋值 //但不能将定义为final的引用指向其他引用 // data.value2++; //不能改变定义为final的常量值 data.test2 = new Test(); // 可以将没有定义为final的引用指向其他引用 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); } }
运行结果:
8 3
data2
6 3
分析:
在本实例子中,被定义成final的常量定义时需要使用大写字母命名,并且中间使用下划线进行连接,这是Java中的编码规则,同时,定义为final的数据无论是常量,对象,还是数组,在主函数中都不可以被修改。一个被定义为final的对象引用只能指向唯一一个对象,不可以将它再指向其他对象,但是一个对象本身的值却是可以改变的,那么为了使一个常量真正做到不可以更改,可以将常量声明为staticfinal。为了验证这个理论,看以下实例子:
import static java.lang.System.*; import java.util.*; public class FinalStaticData { private static Random rand = new Random(); // 实例化一个Random类对象 // 随机产生0~10之间的随机数赋予定义为final的a1 private final int a1 = rand.nextInt(10); // 随机产生0~10之间的随机数赋予定义为static final的a2 private static final int a2 = rand.nextInt(10); public static void main(String[] args) { FinalStaticData fdata = new FinalStaticData(); // 实例化一个对象 // 调用定义为final的a1 out.println("重新实例化对象调用a1的值:" + fdata.a1); // 调用定义为static final的a2 out.println("重新实例化对象调用a1的值:" + fdata.a2); // 实例化另外一个对象 FinalStaticData fdata2 = new FinalStaticData(); out.println("重新实例化对象调用a1的值:" + fdata2.a1); out.println("重新实例化对象调用a2的值:" + fdata2.a2); } }
运行结果:
重新实例化对象调用a1的值:4
重新实例化对象调用a1的值:1
重新实例化对象调用a1的值:5
重新实例化对象调用a2的值:1
从实例的结果可以看出,定义为final的常量是恒定不变的,将随机数赋值定义为final的常量,可以做到每次运行程序时改变a1的值,但a1与a2不同,由于他被声明为static final的形式,所以在内存中为a2开辟了一个恒定不变的区域,当再次实例化一个finalstaticdata对象时,仍然指向a2这块内存区域,所以a2的值保持不变,a2是在装载时被初始化,而不是每次创建新对象时度被初始化,而a1会在重新实例化对象时被更改。
2.final方法
首先说明一点,f定义为inal的的方法不能被重写
例:
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() { // 在子类中定义一个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) { Sub s = new Sub(); // 实例化 s.doit(); // 调用doit()方法 Parents p = s; // 执行向上转型操作 // p.doit(); //不能调用private方法 p.doit2(); p.doit3(); } }
运行结果:
子类.doit()
父类.doit2()
子类.doit3()
分析:
从上例子中可以看出,final方法不能被覆盖。例如doit2()方法不能再子类中被重写,但是在父类中定义了一个private final的doit()方法,同时在子类中也定义了一个doit()方法,从表面上看,子类中的doit()方法覆盖了父类的doit()方法,但必须满足一个对象向上转型为它的基本类型并调用相同方法这样一个条件,在例子中,对象p不能调用doit()方法,可见,子类中的doit()方法并不是正常覆盖,而是生成一个新的方法。
3.final类
在这只说明一点:如果将某个类设置为final类,则类中的所有方法都被隐式设置为final形式,但是final类中的成员变量可以被定义为final或非final形式,并且,其值可以被改变。