Java final关键字详解

java - final关键字

final是java的保留关键字,字面意思是”最终的、不可更改的”,对应到java的使用场景完全适用。

java的final关键字可以修饰类、方法、成员变量、局部变量、方法参数。接下来将会分别说明这些用法。

用法

修饰类

final修饰类,表示该类是不可被继承的,该类已经足够完整了。jdk源码中的StringInteger等包装类都是final的。如String类的声明


public final class String implements java.io.Serializable, Comparable<String>, CharSequence {

}

Integer类的声明

public final class Integer extends Number implements Comparable<Integer> {

}

声明为final的类如果被子类继承,编译期即会报错。

修饰方法

final修饰方法,表示该方法不可被重写。如果确认方法功能已经足够完整,不需要子类重写,可以在方法前加上该修饰符。如下例

public class A {
  //该方法被声明为final
  public final void aMethod() {
  }
}

public class B extends A{
  //该方法试图重写类A的方法,编译期将会报错,提示final方法不可被重写
  public final void Method() {
  }
}
修饰成员变量

final修饰成员变量,表示该变量一旦被赋值就不能改变,final变量也称为java常量。

对于基本数据类型,表示该值本身不可被改变,对于指向对象的引用,表示该引用不可改变,但是引用指向的对象确是可以改变的。

final变量和其他普通的成员变量不太一样,普通成员变量会初始化为该类型的”零”值,例如:

public class Test {
    //int型的"零值"是0
    private int x;
    private long y;
    //对象引用的"零值"是null
    private Long l;
    private Object obj;

    public void print() {
        System.out.println(x);
        System.out.println(y);
        System.out.println(l);
        System.out.println(obj);
    }

    public static void main(String[] args) {
        Test test = new Test();
        test.print();
    }
}

将会打印出:

0
0
null
null

可以看出,int型默认为0,long型默认也是0,对象引用类型的默认为null。

如果成员变量用final来修饰,则必须对其进行初始化,否则编译期即报错,final变量有3种初始化方式:

直接赋值、构造函数、静态初始化块

  • 直接赋值

    public class Test {
    //直接给final成员变量赋值
    private final int x = 0;
    }
  • 构建函数

    public class Test {
    private final int x;
    public Test() {
      //构造函数初始化
      x = 0;
    }
    }
  • 静态初始化块

    public class Test {
    private static final int x;
    static {
      //static final修饰的成员变量只能通过直接赋值或者静态初始化块赋值
      x = 0;
    }
    }

    final修饰的成员变量存储在常量池中

修饰局部变量

final还可以修饰局部变量,final修饰的局部变量在使用前必须显示初始化,如果该变量未被使用则不需要初始化。

同样地,final修饰的局部变量一经赋值,就不能更改。

另外,方法中的局部内部类(包括匿名内部类)访问方法的局部变量必须是final修饰的,例如:

public class Outer {
    public Object method() {
        //被局部内部类访问的局部变量必须是final修饰的
        final int k = 0;

        class Inner {
            void innerMethod() {
                //此处引用了方法的局部变量
                System.out.println(k);
            }
        }
        return new Inner();
    }
}

方法中的普通变量在线程退出该方法之后,局部变量k就消失了,但是方法method的返回值对该变量有引用,可能会访问到不存在的变量。所以该变量在方法退出之后不能消失。那么java是如何解决的呢?

我们知道内部类和外部类是如何通信的,内部类其实是持有外部类的引用Outer.this,通过这个引用可以访问外部类的成员变量。方法中的局部内部类也是如此(方法中的内部类也有自己独立的类文件)。

局部内部类如果想访问方法的局部变量按理来说是没法办到的,但是为了解决这个问题,java通过将方法中的局部变量传递给(通过局部类的构造函数)局部内部类并变成局部内部类的成员变量来实现的,以后局部内部类访问方法的局部变量其实是在访问自己的成员变量,也就是说局部内部类维护了方法局部变量的副本,既然是副本,那就有一致性的问题,如何保证局部变量的值和局部内部类中该变量副本的值是一样的?利用final来保证,将变量用final修饰,禁止修改。

此时方法退出后,局部内部类仍然可以访问到自己的成员变量(该变量是方法局部变量的副本),造成了局部内部类仍然可以访问方法局部变量的假象。

修饰方法参数

方法参数其实也是局部变量,不再说明。

注意事项

java引入final关键字其中一个重要原因是效率,那么为何用final修饰后会提高效率呢?

编译器遇到final方法时会采用内联机制,节省了方法调用的开销,但是如果方法体过大,编译器可能不会采用内联机制。

参考:

  1. Thinking in java
  2. http://blog.csdn.net/zhangjg_blog/article/details/19996629 (这篇分析内部类的文章非常好,分析的很透彻)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值