final 关键词总结
final在Java中是一个保留的关键字,可以声明成员变量、方法、类以及本地变量(在方法中的或者代码块中的变量称为本地变量)。一旦你将引用声明作final,你将不能改变这个引用。
- final类
使用final来修饰的类叫作final类。final类通常功能是完整的,它们不能被继承。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。
1 public final class Animal {} 2 public class Dog extends Animal{} // cannot subclass the final class Animal
在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。
- final方法
“使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。“ ---《Java编程思想》
方法前面加上final关键字,代表这个方法不可以被子类的方法重写。final方法比非final方法要快,因为在编译的时候已经静态绑定了,不需要在运行时再动态绑定。
因此,如果只有在想明确禁止该方法在子类中被覆盖的情况下才将方法设置为final的。
注:类的private方法会隐式地被指定为final方法。
1 public class Animal { 2 final void display(){ 3 System.out.println("I am an Animal."); 4 } 5 } 6 7 public class Dog extends Animal{ 8 void display(){ //Cannot override the final method from Animal 9 System.out.println("I am a Dog."); 10 } 11 }
-
final变量
凡是对成员变量或者本地变量(在方法中的或者代码块中的变量称为本地变量)声明为final的都叫作final变量。
对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
1 public class test { 2 private final int i=0; 3 public test(){ 4 i=1; //The final field A.i cannot be assigned 5 System.out.println(i); 6 } 7 }
- final & static
- static final
final变量经常和static关键字一起使用,作为常量。
对于变量,表示一旦给值就不可修改,并且通过类名可以访问。
对于方法,表示不可覆盖,并且可以通过类名直接访问。
1 public class test { 2 public static final String LOAN = "loan"; 3 static final void diskplay(){ 4 System.out.println(LOAN); 5 } 6 7 class test1 extends test{ 8 void diskplay(){ 9 //This instance method cannot override the static method from test 10 System.out.println("loan"); 11 } 12 } 13 14 public static void main(String[] args) { 15 LOAN = new String("Loan"); 16 //invalid.The final field test.LOAN cannot be assigned. 17 System.out.println(LOAN); 18 } 19 }
- final & static区别
static作用于成员变量用来表示只保存一份副本,而final的作用是用来保证变量不可变。
1 public class Test { 2 public static void main(String[] args) { 3 MyClass myClass1 = new MyClass(); 4 MyClass myClass2 = new MyClass(); 5 System.out.println(myClass1.i); 6 System.out.println(myClass1.j); 7 System.out.println(myClass2.i); 8 System.out.println(myClass2.j); 9 10 } 11 } 12 13 class MyClass { 14 public final double i = Math.random(); 15 public static double j = Math.random(); 16 }
运行这段代码就会发现,每次打印的两个j值都是一样的,而i的值却是不同的。
(j是类/静态变量,所有MyClass实例访问的是同一个值;i是实例/非静态变量,随着类的实例被创建而分配内存空间,不同实例有不同值,具体看静态变量与实例变量的区别)
- 对于被static和final修饰过的实例常量,实例本身不能再改变了(即引用不能被更改),但对于一些容器类型(比如,ArrayList、HashMap)的实例变量,不可以改变容器变量本身,但可以修改容器中存放的对象,这一点在编程中用到很多。
1 import java.util.ArrayList; 2 3 public class TestStaticFinal { 4 private static final String strStaticFinalVar = "aaa"; 5 private static String strStaticVar = null; 6 private final String strFinalVar = null; 7 private static final int intStaticFinalVar = 0; 8 private static final Integer integerStaticFinalVar = new Integer(8); 9 private static final ArrayList<String> alStaticFinalVar = new ArrayList<String>(); 10 11 private void test() { 12 System.out.println("-------------值处理前----------\r\n"); 13 System.out.println("strStaticFinalVar=" + strStaticFinalVar + "\r\n"); 14 System.out.println("strStaticVar=" + strStaticVar + "\r\n"); 15 System.out.println("strFinalVar=" + strFinalVar + "\r\n"); 16 System.out.println("intStaticFinalVar=" + intStaticFinalVar + "\r\n"); 17 System.out.println("integerStaticFinalVar=" + integerStaticFinalVar + "\r\n"); 18 System.out.println("alStaticFinalVar=" + alStaticFinalVar + "\r\n"); 19 20 21 //strStaticFinalVar="哈哈哈哈"; //错误,final表示终态,不可以改变变量本身. 22 strStaticVar = "哈哈哈哈"; //正确,static表示类变量,值可以改变. 23 //strFinalVar="呵呵呵呵"; //错误, final表示终态,在定义的时候就要初值(哪怕给个null),一旦给定后就不可再更改。 24 //intStaticFinalVar=2; //错误, final表示终态,在定义的时候就要初值(哪怕给个null),一旦给定后就不可再更改。 25 //integerStaticFinalVar=new Integer(8); //错误, final表示终态,在定义的时候就要初值(哪怕给个null),一旦给定后就不可再更改。 26 alStaticFinalVar.add("aaa"); //正确,容器变量本身没有变化,但存放内容发生了变化。这个规则是非常常用的,有很多用途。 27 alStaticFinalVar.add("bbb"); //正确,容器变量本身没有变化,但存放内容发生了变化。这个规则是非常常用的,有很多用途。 28 29 System.out.println("-------------值处理后----------\r\n"); 30 System.out.println("strStaticFinalVar=" + strStaticFinalVar + "\r\n"); 31 System.out.println("strStaticVar=" + strStaticVar + "\r\n"); 32 System.out.println("strFinalVar=" + strFinalVar + "\r\n"); 33 System.out.println("intStaticFinalVar=" + intStaticFinalVar + "\r\n"); 34 System.out.println("integerStaticFinalVar=" + integerStaticFinalVar + "\r\n"); 35 System.out.println("alStaticFinalVar=" + alStaticFinalVar + "\r\n"); 36 } 37 38 public static void main(String args[]) { 39 new TestStaticFinal().test(); 40 } 41 }
-------------值处理前---------- strStaticFinalVar=aaa strStaticVar=null strFinalVar=null intStaticFinalVar=0 integerStaticFinalVar=8 alStaticFinalVar=[] -------------值处理后---------- strStaticFinalVar=aaa strStaticVar=哈哈哈哈 strFinalVar=null intStaticFinalVar=0 integerStaticFinalVar=8 alStaticFinalVar=[aaa, bbb]
- 关于final的重要知识点
- final关键字可以用于成员变量、本地变量、方法以及类。
- final成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会报编译错误。
- 你不能够对final变量再次赋值。
- 在匿名类中所有变量都必须是final变量。
- final方法不能被重写。
- final类不能被继承。
- final关键字不同于finally关键字,后者用于异常处理。
- final关键字容易与finalize()方法搞混,后者是在Object类中定义的方法,是在垃圾回收之前被JVM调用的方法。
- 接口中声明的所有变量本身是final的。
- final和abstract这两个关键字是反相关的,final类就不可能是abstract的。
- final方法在编译阶段绑定,称为静态绑定(static binding)。
- 没有在声明时初始化final变量的称为空白final变量(blank final variable),它们必须在构造器中初始化,或者调用this()初始化。不这么做的话,编译器会报错“final变量(变量名)需要进行初始化”。
- 将类、方法、变量声明为final能够提高性能,这样JVM就有机会进行估计,然后优化。
- final的成员变量(即外部)在声明时必须赋值(在外部声明的变量在这个类一杯调用就初始化了,如果你没有对final修饰过的变量赋值,编译器初始化这个类的时候发现一个不可被修改的变量没有值,编译器必然报错。)。final局部变量可以先声明后赋值。
class Demo{ final int i; i=7 //编译失败,成员变量应在声明时就赋值 void display(){ final int i; i=1; //局部变量可以先声明后赋值,但使用前必须赋值 System.out.println(this.i); System.out.println(i); }
} - 按照Java代码惯例,final变量就是常量,而且通常常量名要大写:
1 private final int COUNT = 10;
类的final变量和普通变量有什么区别?
当用final作用于类的成员变量时,成员变量(注意是类的成员变量,局部变量只需要保证在使用之前被初始化赋值即可)必须在定义时或者构造器中进行初始化赋值,而且final变量一旦被初始化赋值之后,就不能再被赋值了。
1 public class test { 2 public static void main(String[] args) { 3 String a = "hello2"; 4 final String b = "hello"; 5 String d = "hello"; 6 String c = b + 2; 7 String e = d + 2; 8 System.out.println((a == c)); 9 System.out.println(e); 10 System.out.println((a == e)); 11 } 12 }
true hello2 false
大家可以先想一下这道题的输出结果。为什么第一个比较结果为true,而第二个比较结果为fasle。这里面就是final变量和普通变量的区别了,当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。这种和C语言中的宏替换有点像。因此在上面的一段代码中,由于变量b被final修饰,因此会被当做编译器常量,所以在使用到b的地方会直接将变量b替换为它的值放在常量池中,(对于字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。 )而对于变量d的访问却需要在运行时通过链接来进行。想必其中的区别大家应该明白了,不过要注意,只有在编译期间能确切知道final变量值的情况下,编译器才会进行这样的优化,比如下面的这段代码就不会进行优化:
1 public class Test { 2 public static void main(String[] args) { 3 String a = "hello2"; 4 final String b = getHello(); 5 String c = b + 2; 6 System.out.println((a == c)); 7 8 } 9 10 public static String getHello() { 11 return "hello"; 12 } 13 }
false
参考网站:
http://www.cnblogs.com/dolphin0520/p/3736238.html
http://lavasoft.blog.51cto.com/62575/18771/

浙公网安备 33010602011771号