Java关键字final
根据程序上下文环境,Java关键字final有“这是无法改变的”或者“终态的”含义,它可以修饰非抽象类、非抽象类成员方法和变量。你可能出于两种理解而需要阻止改变:设计或效率。
- final类不能被继承,没有子类,final类中的方法默认是final的。
- final方法不能被子类的方法覆盖,但可以被继承。
- final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
- final不能用于修饰构造方法。
注意:父类的private成员方法是不能被子类方法覆盖的,因此private类型的方法默认是final类型的。
1. final类
final类不能被继承,因此final类的成员方法没有机会被覆盖,默认都是final的。在设计类时候,如果这个类不需要有子类,类的实现细节不允许改变,并且确信这个类不会载被扩展,那么就设计为final类。换言之,出于这样或那样的原因,我们的类肯定不需要进行任何改变;或者出于安全方面的理由,我们不希望进行子类化(子类处理)。
除此以外,我们或许还考虑到执行效率的问题,并想确保涉及这个类各对象的所有行动都要尽可能地有效。如下所示:
public class SmallBrain { } public final class Dinosaur { int i = 7; int j = 1; SmallBrain x = new SmallBrain(); void f() { } @Override public String toString() { return "Dinosaur [i=" + i + ", j=" + j + ", x=" + x + "]"; } } public class Jurassic { public static void main(String[] args) { Dinosaur n = new Dinosaur(); n.f(); n.i = 40; n.j++; System.out.println(n); } }
输出结果
Dinosaur [i=40, j=2, x=com.david.javaTest.SmallBrain@17b68215]
注意:数据成员既可以是final,也可以不是,取决于我们具体选择。应用于final的规则同样适用于数据成员,无论类是否被定义成final。将类定义成final后,结果只是禁止进行继承——没有更多的限制。然而,由于它禁止了继承,所以一个final类中的所有方法都默认为final。因为此时再也无法覆盖它们。所以与我们将一个方法明确声明为final一样,编译器此时有相同的效率选择。
2.final方法
如果一个类不允许其子类覆盖某个方法,则可以把这个方法声明为final方法。使用final方法的原因有二:
第一、把方法锁定,防止任何继承类修改它的意义和实现。
第二、高效。编译器在遇到调用final方法时候会转入内嵌机制,大大提高执行效率。
例如:
public class FinalMethod { public void nonFinalMethod() { System.out.println("nonFinalMethod"); } public final void finalMethod() { System.out.println("finalMethod"); } }
public class TestFinalMethod extends FinalMethod{ public void nonFinalMethod() { System.out.println("test nonFinalMethod"); } public static void main(String[] args) { TestFinalMethod t1 = new TestFinalMethod(); t1.nonFinalMethod(); t1.finalMethod(); } }
结果:
test nonFinalMethod
finalMethod
3.final变量(常量)
用final修饰的成员变量表示常量,值一旦给定就无法改变!final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。
从下面的例子中可以看出,一旦给final变量初值后,值就不能再改变了。
另外,final变量定义的时候,可以先声明,而不给初值,这中变量也称为final空白,无论什么情况,编译器都确保空白final在使用之前必须被初 始化。但是,final空白在final关键字final的使用上提供了更大的灵活性,为此,一个类中的final数据成员就可以实现依对象而有所不同, 却有保持其恒定不变的特征.
特别说明:如果final修饰的是一个基本类型,就表示这个变量被赋予的值是不可变的,即它是个常量;如果final修饰的是一个对象,就表示这个变量被赋予的引用是不可变的,这里需要提醒大家注意的是,不可改变的只是这个变量所保存的引用,并不是这个引用所指向的对象。在第二种情况下,final的含义与第一种情况相同。实际上对于前两种情况,有一种更贴切的表述final的含义的描述,那就是,如果一个变量或方法参数被final修饰,就表示它只能被赋值一次, 但是JAVA虚拟机为变量设定的默认值不记作一次赋值。
package com.david.javaTest; public class FinalTest { private final String S = "final实例变量S"; private final int A = 100; public final int B = 90; public static final int C = 80; private static final int D = 70; public final int E; // final空白,必须在初始化对象的时候赋初值 public FinalTest(int x) { E = x; } /** * @param args */ public static void main(String[] args) { new FinalTest(2).f1(2); FinalTest t = new FinalTest(2); // 使用final关键字修饰一个变量是指引用变量不能变,引用变量所指向的对象中的内容还是可以被修改。 final StringBuffer stringBuffer = new StringBuffer("hello stringBuffer"); System.out.println(stringBuffer); stringBuffer.append(" append "); System.out.println(stringBuffer); // t.A=101; //出错,final变量的值一旦给定就无法改变 // t.B=91; //出错,final变量的值一旦给定就无法改变 // t.C=81; //出错,final变量的值一旦给定就无法改变 // t.D=71; //出错,final变量的值一旦给定就无法改变 System.out.println(t.A); System.out.println(t.B); System.out.println(t.C); // 不推荐用对象方式访问静态字段 System.out.println(t.D); // 不推荐用对象方式访问静态字段 System.out.println(FinalTest.C); System.out.println(FinalTest.D); // System.out.println(FinalTest.E); //出错,因为E为final空白,依据不同对象值有所不同. System.out.println(t.E); FinalTest t1 = new FinalTest(3); System.out.println(t1.E); // final空白变量E依据对象的不同而不同 } private void test() { System.out.println(new FinalTest(1).A); System.out.println(FinalTest.C); System.out.println(FinalTest.D); } public void test2() { final int a; // final空白,在需要的时候才赋值 final int b = 4; // 局部常量--final用于局部变量的情形 final int c; // final空白,一直没有给赋值. a = 3; // a=4; 出错,已经给赋过值了. // b=2; 出错,已经给赋过值了. } public void f1(final int i) { // i++; //i是final类型的,值不允许改变的. System.out.println(i); } }
4、final参数
public static void main(String[] args) { new Test4().f1(2); } public void f1(final int i) { //i++; //i是final类型的,值不允许改变的. System.out.print(i); } }
5.被final修饰的变量必须被初始化。初始化的方式有以下几种:
- 在定义的时候初始化。
- final变量可以在初始化块中初始化,不可以在静态初始化块中初始化。
- 静态final变量可以在静态初始化块中初始化,不可以在初始化块中初始化。
- final变量还可以在类的构造器中初始化,但是静态final变量不可以。
public class FinalTest { final String teString ; public static final String hello;//如果不在静态代码块中初始化则The blank final field hello may not have been initialized static{ final String staticFinalVar ="staticFinalVar"; hello="hello has been initialized in static block"; //hello="hh"; //teString = "this is initial block";//Cannot make a static reference to the non-static field teString System.out.println(staticFinalVar); System.out.println(hello); } { teString = "this is initial block"; System.out.println(teString); } public static void main(String[] args) { new FinalTest(2); } }
6.面试题
使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的。例如,对于如下语句: final StringBuffer a=new StringBuffer("immutable");执行如下语句将报告编译期错误: a=new StringBuffer("");但是,执行如下语句则可以通过编译: a.append(" broken!");