你真的了解final吗?
final关键字可以修饰类,方法,变量
1、用final修饰变量
(1)final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
final修饰一个成员变量(属性),必须要显示初始化。这里有两种初始化方式,一种是在变量声明的时候初始化;第二种方法是在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。
当函数的参数类型声明为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修饰的方法不能被重写
使用final方法的原因有两个:
(1)把方法锁定,以防任何继承类修改它的含义。这是出于设计的考虑:想要确保在继承中使方法行为保持不变,并且不会被覆盖。
(2)效率,在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。
final和private:类中所有的private方法都隐式地指定为final的。由于无法取用private方法,所以也就无法覆盖它。可以对private方法添加final修饰词,但这并不能给该方法增加任何额外的意义。
3、用final修饰类
用final修饰的类无法被继承,即它不能有子类。请注意: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是不可变类的代表。不可变类有很多好处,譬如它们的对象是只读的,可以在多线程环境下安全的共享,不用额外的同步开销等等。