在我们编写Java程序时总会根据需求将变量、方法、类设置成static(静态)或final(最终),熟练掌握final用法是必须的,现在我们就来详细了解final关键字!
一、final概述
概念:由字面可以了解,final有最终态,无法改变的意思。
使用目的:为了阻止改变与提高效率。
高效原因:Java内嵌机制,final方法会在编译的过程中利用内嵌机制进行inline优化。
inline优化是指:在编译的时候直接调用方法代码替换,也就是内嵌,而不是在运行时调用方法。
inline需要在编译的时候就知道最后要用哪个方法。
显然,非final是不行的。
非final方法可能在子类中被重写,由于可能出现多态的情况,编译器在编译阶段
并不能确定将来调用方法的对象的真正类型,也就无法确定到底调用哪个方法。)
修饰对象:
1、非抽象类,由于被final修饰的类是不能被继承的,而抽象类必须被继承才有意义。
2、非抽象方法,由于被final修饰的方法是不能被重写的,而抽象方法必须被重写才有意义。
3、变量或常量。
注意:
1、final不能修饰构造方法。
2、父类的private方法是不能被子类重写的,因为private方法默认是final的。
二、具体用法
1、修饰类
final类不能被继承,所有其成员与方法自然没有机会被覆盖,默认是final的。所以在将类设计成final类的时候,一定要特别慎重考虑,确定这个类不需要有子类,类实现不能被改变,类不能被扩展的时候才能用final修饰。
注:Java中String类就是一个final类。
2、修饰方法
如果一个类不允许其子类覆盖某个方法,则可以把这个方法用final修饰。
这样做的目的可在《Java编程思想》中查到:
“使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。”
public class Test1 {
public static void main(String[] args) {
// TODO 自动生成方法存根
}
public void f1() {
System.out.println("f1");
}
//无法被子类覆盖的方法
public final void f2() {
System.out.println("f2");
}
public void f3() {
System.out.println("f3");
}
private void f4() {
System.out.println("f4");
}
}
public class Test2 extends Test1 {
public void f1(){
System.out.println("Test1父类方法f1被覆盖!");
}
public static void main(String[] args) {
Test2 t=new Test2();
t.f1();
t.f2(); //调用从父类继承过来的final方法
t.f3(); //调用从父类继承过来的方法
//t.f4(); //调用失败,无法从父类继承获得
}
}
3、修饰变量(重点)
1、final修饰基本数据类型变量,则其数值一旦被初始化就不能被更改。
2、final修饰引用变量(如ObjectName obj = new ObjectName();中obj就是一个引用变量,指向堆内存中对象空间的首地址),则其初始化后就不能修改引用指向另一个对象。
public class Test2 {
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 Test3(int x) {
E = x;
}
/**
* @param args
*/
public static void main(String[] args) {
Test3 t = new Test3(2);
//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(Test3.C);
System.out.println(Test3.D);
//System.out.println(Test3.E); //出错,因为E为final空白,依据不同对象值有所不同.
System.out.println(t.E);
Test3 t1 = new Test3(3);
System.out.println(t1.E); //final空白变量E依据对象的不同而不同
}
private void test() {
System.out.println(new Test3(1).A);
System.out.println(Test3.C);
System.out.println(Test3.D);
}
public void test2() {
final int a; //final空白,在需要的时候才赋值
final int b = 4; //局部常量--final用于局部变量的情形
final int c; //final空白,一直没有给赋值.
a = 3;
//a=4; 出错,已经给赋过值了.
//b=2; 出错,已经给赋过值了.
}
}
四、修饰参数
当函数参数为final类型时,你可以读取使用该参数,但是无法改变该参数的值。
public class Test3 {
public static void main(String[] args) {
new Test4().f1(2);
}
public void f1(final int i) {
//i++; //i是final类型的,值不允许改变的.
System.out.print(i);
}
}