Java之路:你真的了解final吗?

1、用final修饰数据

(1)用final修饰基本数据类型时,即声明了一个常量,必须对其进行赋值。

(2)当对对象的引用使用final时,final使引用恒定不变。一旦引用被初始化指向一个对象,就无法把它改为指向另一个对象。然而对象其自身却是可以被修改的。

class Value {
	int i;
	public Value(int i) { this.i = i; }
}
public FinalData {
    private Value v1 = new Value(11);
    private final Value v2 = new Value(22);
    private final int[] a = {1, 2, 3, 4, 5};
	public static void main(String[] args) {
		FinalData fd1 = new FinalData();
        fd1.v2.i++; // v2是对象,不是常量,v2用final修饰,无法把它改为指向另一对象,但对象自身是可以被修改的
        fd1.v1 = new Value(9);  // v1不是final,可以将它指向另一个对象
//        fd1.v2 = new Value(0);  // v2是final,不可以将它指向另一个对象
        for (int i = 0; i < fd1.a.length; i++){
            fd1.a[i]++; // 数组是对象,对象不是常量,其自身可以改变
        }
//        fd1.a = new int[3];   //错误,数组a是final修饰的引用,无法将它指向另一个对象
	} 
} 

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

(4)Java允许生成“空白final”,所谓空白final是指被声明为final但又未给定初值的域。无论什么情况,编译器都确保空白final在使用前必须被初始化。总之,final是一定要初始化的,如果不在声明处初始化,那就在构造器中初始化。记住一定要初始化,否则编译不通过。

class Poppet {
    private int i;
    Poppet(int i) { this.i = i; }
}
public class BlankFinal {
    private final int i = 0;
    private final int j;    // 空白final
    private final Poppet p; // 空白final引用
    public BlankFinal(){
        j = 1;  // 初始化空白final
        p = new Poppet(1);  // 初始化空白final引用
    }
    public BlankFinal(int x){
        j = x;  // 初始化空白final
        p = new Poppet(x);  // 初始化空白final引用
    }
    public static void main(String[] args){
        new BlankFinal();
        new BlankFinal(47);
    }
}

(5)用final修饰参数,Java允许在参数列表中以声明的方式将参数指明为final。这意味着你无法在方法中更改参数引用所指向的对象。

class Gizmo {
    public void spin(){}
}
public class FinalArguments {
    void with(final Gizmo g){
        //! g = new Gizmo();    // 错误,g是用final修饰的,无法将它指向另一个对象
    }
    void without(Gizmo g){
        g = new Gizmo();    // g不是final类型,可以将它指向新的对象
        g.spin();
    }
    // void f(final int i){ i++; }  // 错误,i是用final修饰的,无法改变它的值。
    int g(final int i){ return i + 1; } // 正确,i的值没有被改变
    public static void main(String[] args){
        FinalArguments bf = new FinalArguments();
        bf.with(null);
        bf.without(null);
    }
}

2、用final修饰方法

使用final方法的原因有两个:
(1)把方法锁定,以防任何继承类修改它的含义。这是出于设计的考虑:想要确保在继承中使方法行为保持不变,并且不会被覆盖。
(2)效率,在Java的早期实现中,如果将一个方法指明为final,就是同意编译器将针对该方法的所有调用都转为内嵌调用。当编译器发现一个final方法调用命令时,它会根据自己的谨慎判断,跳过插入程序代码这种正常方式而执行方法调用机制(将参数压入栈,跳至方法代码处并执行,然后跳回并清理栈中的参数,处理返回值),并且以方法体中的实际代码的副本来替代方法调用。这将消除方法调用的开销。

final和private:类中所有的private方法都隐式地指定为final的。由于无法取用private方法,所以也就无法覆盖它。可以对private方法添加final修饰词,但这并不能给该方法增加任何额外的意义。

3、用final修饰类

用final修饰的类无法被继承,即它不能有子类。请注意:final的域可以根据个人的意愿选择为是或不是final。不论类是否被定义为final,相同的规则都适用于定义为final的域。然而,由于final类禁止继承,所以final类中所有的方法都会隐式指定为final的,因为无法覆盖它们。在final类中可以给方法添加final修饰词,但这不会增添任何意义。

深入理解final的好处

(1)final关键字提高了性能。JVM和Java应用都会缓存final变量。

(2)final变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销。

(3)使用final关键字,JVM会对方法、变量及类进行优化。

(4)创建不可变类要使用final关键字。不可变类是指它的对象一旦被创建了就不能被更改了。String是不可变类的代表。不可变类有很多好处,譬如它们的对象是只读的,可以在多线程环境下安全的共享,不用额外的同步开销等等。

  • 8
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值