final关键字

final关键字总结

final通常是指“这是不可改变的”,不想改变通常有两个原因:设计与效率。
以下谈论fianl的三种情况:数据、方法、类。

final数据

许多编程语言都有这样一种方法,来向编译器告知一块数据是恒定不变的,比如:
- 一个永不改变的编译时常量
- 一个在运行时被初始化的值,而你希望它不被改变

对于编译时常量这种情况,编译时可以将该常量代入任何用到它的计算式中,也就是说,可以在编译时执行该计算式。并且,在Java中,这种常量必须是基本数据类型,并且用关键字fianl表示。还有对这个常量进行定义的时候,必须对其进行赋值。

一个既是static又是final的域只占据一段不可改变的存储空间。

当对对象使用final修饰的时候,它的意思是指引用恒定不变,一但引用被初始化指向一个对象,就无法把他修改为指向另一个对象。然而对象自身确实可以修改的。这一限制同样适用于数组,它也是对象。

根据惯例,既是static又是final的域(即编译期常量)

示例:

class Value {
    int i;
    public Value(int i) {
        this.i = i ;
    }
}
public class FinalTest {


    private static Random rand = new Random();
    private String id;

    public FinalTest(String id) {
        this.id = id;
    }

    //基本数据类型
    private final int valueOne = 9;
    private static final int VALUE_TWO = 99;
    private static final  int VALUE_THREE = 39;
    private final int i4 = rand.nextInt(20);
    static final int INT_5 = rand.nextInt(20);
    //对象
    private Value v1 = new Value(11);
    private final Value v2 = new Value(11);
    private static final Value VAL_3 = new Value(33);
    //数组
    private final int[] a = {1,2,3,4,5,6};
    public String toString() {
        return id+":"+"i4 = "+i4 + ", INT_5 = "+ INT_5;
    }

    public static void main(String[] args) {
        FinalTest ft1 = new FinalTest("ft1");
        // ! ft1,valueOne++;//Error: can not change value
        ft1.v2.i++;//Object is not constant!
        ft1.v1 = new Value(9);//there is ok because it is not final
        for (int i = 0 ; i<ft1.a.length ; i++) {
            ft1.a[i]++;//Object is not constant(不变)
        }
        //! ft1.v2 = new Value(0);//Error : Can't
        //! ft1.VAL_3 = new Value(1); //change reference
        //! ft1.a = new int[2];
        System.out.println(ft1);
        System.out.println("Create new FinalTest");
        FinalTest ft2 = new FinalTest("ft2");
        System.out.println(ft1);
        System.out.println(ft2);
    }
}

结论:
- valueOne和Value_TWO都是带有编译时数值的final基本类型,所以二者局可以用作编译期常量,并没有重大区别。
- VALUE_THREE是一种更加典型的对常量定义的方式,定义为public,则可以被用与包之外;定义为static,则强调只有一份;定义为final,则说明是一个常量。
- 不能因为某数据是final的就认为在编译时可以知道他的值,在运行时使用随机产生的数值来初始化i4和INT_5就说明了这一点。
- v1和VAL_3这些变量说明了final引用的意义。正如在main()中所看到的,final意味着v2无法再次指向新的对象,而不是无法改变它的值,这对数组有同样的意义。

空白final:
Java允许生成“空白final”,所谓空白final是指被声明为final但未给定初值得域。无论什么情况,编译器都确保空白final在使用前必须被初始化。空白final在关键字final的使用上提供了更大的灵活性。

final方法

使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类来修改它的含义。这是出于设计地考虑:想要确保在继承中方法的行为保持不变,并且不会被覆盖。

过去使用final方法的第二个原因是效率。在早期的Java实现中,如果将一个方法指明为final,就是同意编译器将针对该方法的所有调用转化为内嵌调用。当编译器发现一个final方法调用命令时,它会根据自己的谨慎判断,跳过插入程序代码这种正常方式而执行方法调用机制(将参数压栈,跳至方法代码出并执行,然后跳回清理栈中的参数,处理返回值),并且以方法体中的实际代码的副本替代方法调用。这将消除方法调用的开销。当然,如一个方法很大。你的程序代码就会膨胀,因而可能看不到内嵌带来的任何性能提高,因为,所带来的的性能提高会因为花费于方法内的时间量而缩减。
在最近的Java版本中,虚拟机(特别是hotspot技术)可以探测到这些情况,并优化去掉这些效率反而降低的额外的内嵌调用,因此不再需要使用final方法进行优化。事实上,这种方法正在收到劝阻,应该让编译器和JVM去解决这些问题,只有想明确禁止覆盖的时候,才将方法设置成final

final和private关键字

类中的所有private方法都会隐式地指定为final修饰。由于无法使用private方法,所以也就无法覆盖他它,所以对private方法添加final修饰,不会增加额外的意义

fianl类

当一个类被整体定义为final时,就表明了你不打算继承该类,而且也不允许这样做。换句话说,就是这个类不会有子类。

注意:
final类的域可以根据个人的意愿选择是不是final。无论类是否被定义为final,相同的规则都适用于定义为final的域,然而,由于final类禁止继承,所有final的类的方法都会被隐式指定为final,因此无法被覆盖。所以在final类中对final方法添加final修饰,不会有额外意义

思考

在设计类的时候,将方法声明为final的原因,是因为你不想别人覆盖你的方法。但是,如果方法所在的类是一个通用类,如果这个类中有final方法,可能会妨碍其他程序员在项目中通过继承来复用你的类。Vector类就是一个很好的例子,应为这个类中含有一些的final方法,导致Stack继承Vector的时候,麻烦就出现了。

详情请查看thinking in Java P140.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值