Java的关键字final通常是指被它修饰的数据是不能被改变的,不想改变可能出于两种理由:设计或效率。以下是本文主要大纲:
- final数据的使用
- final参数的使用
- final方法的使用
- final类不能被继承
一、final数据
1.1 final修饰变量
- 修饰的变量必须是基本数据类型:告知编译器这一块数据是不变的,这样可以在执行计算时,减少一些运行时的负担。
- 论什么情况,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 的数值是不可以通过一个新的对象而改变的。这是因为在装载时已经被初始化,而不是每次创建新对象时初始化。
1.2 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
+------------------------------------------------------------------+
1.3 final用于数组
final用于数组和引用时一样的,数组只不过是另一种引用,对于这个变量的引用是不能被重新赋值,但是对象本身是可以修改的。代码如下:
package com.game.lll;
public class FinalTest {
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:不可以这样做
test.values[0] = 100;//对象本身是可以修改的
for(int i = 0; i < test.values.length;i++){
System.out.println(test.values[i]++);//对于这个变量的引用是不能被重新赋值
}
}
class Value{
int value;
public Value(int value) {
this.value = value;
}
}
}
运行结果:
+------------------------------------------------------------------+
100
2
3
4
5
6
+------------------------------------------------------------------+
1.4 空白final
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在使用前被初始化。
二、final参数
2.1final参数用在普通类中
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
+------------------------------------------------------------------+
2.2 final参数用在匿名内部类中
如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final的。
package com.game.lll;
/**
* final在匿名内部类的使用
* @author liulongling
*
*/
public class FinalInClass{
public abstract class AbstractFinalClass<Result> {
protected abstract Result onExecute(int a);
public Result execute(int a)
{
return this.onExecute(a);
}
}
private Integer a(final int value)
{
return new AbstractFinalClass<Integer>() {
@Override
protected Integer onExecute(int a) {
a = value;
return a;
}
}.execute(3);
}
public static void main(String[] args) {
FinalInClass inClass = new FinalInClass();
System.out.println(inClass.a(2));
}
}
三、final方法
3.1 final和private关键字
使用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 b
OverridingFinal a
OverridingFinal2 b
WithFinals a
WithFinals b
+------------------------------------------------------------------+
四、final类
当你将某个类定义为final class时,那么子类就无法继承该类。
本章参考文章和书籍
<<Thinking in Java>>