【Java基础提高】深入分析final关键字(一)
java,final2016-07-28
Java的关键字final通常是指被它修饰的数据是不能被改变的,不想改变可能出于两种理由:设计或效率。
package com.game.lll; import java.util.Random; public class FinalT { private static Random random = new Random(20); private final int VALUE_A = 10; private static final int VALUE_B = 20; public static final int VALUE_C = random.nextInt(10); public final int VALUE_D = random.nextInt(10); public static void main(String[] args) { FinalTest test = new FinalTest(); //test.VALUE_A = 5;//Error:不可以这样做 //test.VALUE_B =21;//Error:不可以这样做 FinalT finalT = new FinalT(); FinalT finalT1 = new FinalT(); System.out.println("VALUE_C:"+VALUE_C); System.out.println("VALUE_C:"+finalT.VALUE_C); System.out.println("VALUE_C:"+finalT1.VALUE_C); System.out.println("---------"); System.out.println("VALUE_D:"+finalT.VALUE_D); System.out.println("VALUE_D:"+finalT1.VALUE_D); } }
运行结果: +------------------第13、15行编译错误-------------------------------+ The final field FinalTest.a cannot be assigned +--------------------static final ---------------------------------+VALUE_C:3
VALUE_C:3
VALUE_C:3
+-----------------------final--------------------------------------+VALUE_D:6
VALUE_D:1
+------------------------------------------------------------------+
以上结果得出一点我们不能因为某数据时final的就认为在编译时可以知道它的值。在运行时使用随机生成的VALUE_C和VALUE_D说明了这一点。示例代码展示了final数值定义为static和非static的区别。此区别只有当数值在运行时内被初始化时才会显现,这是因为编译器对编译时数值一视同仁(并且他们可能因为优化而消失)。使用static final 的数值是不可以通过一个新的对象而改变的。这是因为在装载时已经被初始化,而不是每次创建新对象时初始化。
当使用final用于对对象的引用而不是基本类型时,对于基本类型final使数值恒定不变,而对于对象引用,final使引用恒定不变。一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象。然而,对象自身的数据是可以修改的。
package com.game.lll; public class FinalTest { private final int a = 10; private final Value v1 = new Value(10); public static void main(String[] args) { FinalTest test = new FinalTest(); //test.a = 5;//不可以这样做 test.v1.value++; //test.v1 = new Value(12);//Error:不可以这样做 System.out.println("对象内的数据:"+test.v1.value); } class Value{ int value; public Value(int value) { this.value = value; } } }
运行结果: +------------------------------------------------------------------+ 对象内的数据:11 +------------------------------------------------------------------+
final用于数组和引用时一样的,数组只不过是另一种引用,所以数组里的元素值也是不能被修改的。代码如下:
package com.game.lll; public class FinalTest { private final int a = 10; private final Value v1 = new Value(10); private final int[] values = { 1, 2, 3, 4, 5, 6 }; public static void main(String[] args) { FinalTest test = new FinalTest(); //test.a = 5;//不可以这样做 test.v1.value++; //test.v1 = new Value(12);//Error:不可以这样做 for(int i = 0; i < test.values.length;i++) { System.out.println(test.values[i]++); } //System.out.println("对象内的数据:"+test.v1.value); } class Value{ int value; public Value(int value) { this.value = value; } } }
运行结果: +------------------------------------------------------------------+1 2 3 4 5 6
+------------------------------------------------------------------+
Java允许生成“空白final”,空白final是指被声明为final但又给未定初值的域。代码如下:
package com.game.lll; public class BlankFinal { private final int a; public BlankFinal(int i) { this.a = i; } public static void main(String[] args) { BlankFinal blankFinal = new BlankFinal(5); System.out.println(blankFinal.a); } }虽然未对a直接赋值,但是在构造函数中对a进行了初始化。所以无论什么情况,都要确保final在使用前被初始化。
Java允许在函数参数列表中使用final。不过在使用final后你不能更改参数引用所指向的对象,但是可以修改对象里面的值。代码如下:
package com.game.lll; public class FinalArguments { private Person person = new Person(3); private void run(final Person p) { //p = new Person();//Error 不要这样做 System.out.println("修改前a="+p.a); p.a = 6; System.out.println("修改后a="+p.a); } public static void main(String[] args) { FinalArguments arguments = new FinalArguments(); arguments.run(arguments.person); } class Person{ int a; public Person(int a) { this.a = a; } } }
运行结果: +------------------------------------------------------------------+修改前a=3
修改后a=6
+------------------------------------------------------------------+
使用final方法的原因是把方法锁定,以防任何继承类修改它的含义。同时如果父类中的这个方法使用了private那么在子类也是不能重写这个方法。代码示例如下:
package com.game.lll; public class FinalOverriding { class WithFinals { private final void a() { System.out.println("WithFinals a"); } private void b() { System.out.println("WithFinals b"); } } class OverridingFinal extends WithFinals{ private final void a() { System.out.println("OverridingFinal a"); } private void b() { System.out.println("OverridingFinal b"); } } class OverridingFinal2 extends OverridingFinal{ private final void a() { System.out.println("OverridingFinal2 a"); } private void b() { System.out.println("OverridingFinal2 b"); } } private OverridingFinal2 of2 = new OverridingFinal2(); public static void main(String[] args) { FinalOverriding finalOverriding = new FinalOverriding(); finalOverriding.of2.a(); finalOverriding.of2.b(); OverridingFinal of = finalOverriding.of2; of.a(); of.b(); WithFinals wf = of; wf.a(); wf.b(); } }
运行结果: +------------------------------------------------------------------+OverridingFinal2 a OverridingFinal2 b
OverridingFinal a OverridingFinal b
WithFinals a WithFinals b
+------------------------------------------------------------------+
从结果上来看,子类OverridingFinal和OverridingFinal2都没有重写父类方法。在Java中类中private方法只能在供内部访问,所以子类是无法覆盖它。对private方法添加final和不加结果都是一样并没有任何意义。。下面对它做一些修改
第一步:将WithFinals的方法a()改为public
public final void a() { System.out.println("WithFinals a"); }
第二部:将OverridingFinal的方法a()也改为public
public final void a() { System.out.println("OverridingFinal a"); }
结果是编译错误: +------------------------------------------------------------------+Cannot override the final method from FinalOverriding.WithFinals
+------------------------------------------------------------------+
将private修改为public后,结果证明使用final修饰的方法是不能被重写的。如果我们把OverridingFinal和OverridingFinal2的b()方法改为public呢?那么OverridingFinal2方法b将会覆盖OverridingFinal的方法b。运行结果如下:
运行结果: +------------------------------------------------------------------+ OverridingFinal2 a OverridingFinal2 bOverridingFinal a OverridingFinal2 b
WithFinals a WithFinals b +------------------------------------------------------------------+
当你将某个类定义为final class时,那么子类就无法继承该类。
本章参考文章和书籍
<<Thinking in Java>>