1.修飾類
final class Test {}
- 用final修飾一個(gè)類時(shí)载佳,表明這個(gè)類不能被繼承
- final類中的所有成員方法都會(huì)被隱式地指定為final方法(因?yàn)轭惒槐焕^承,那么方法自然就不能被覆蓋了)
- 盡量不要將類設(shè)計(jì)為final幔托,除非以后真的不會(huì)繼承這個(gè)類或出于安全考慮
2.修飾方法
public final void func() {}
- 用final修飾一個(gè)方法,表明這個(gè)方法不能被覆蓋(override)
- 可以重載final修飾的方法
- 所有private方法會(huì)隱式地指定為final(因?yàn)闊o法取用private方法)
3.修飾變量
final int i = 0; //基本數(shù)據(jù)類型
final Object obj = new Object(); //引用類型
//允許'空白final'
final int x;
final Object y;
- 對(duì)于基本數(shù)據(jù)類型的變量仪糖,其值在初始化之后便不能更改柑司,需要保證其在使用之前被初始化賦值
- 對(duì)于引用類型的變量,在初始化之后便不能再指向另一個(gè)對(duì)象(但對(duì)象自身是可以被修改的)锅劝,必須在定義時(shí)或者在構(gòu)造器中進(jìn)行初始化賦值
- 允許'空白final'攒驰,只要保證在第一次訪問數(shù)據(jù)之前已在構(gòu)造函數(shù)或構(gòu)造代碼塊中將其初始化,只能二選一故爵,不能同時(shí)使用構(gòu)造函數(shù)和構(gòu)造代碼塊進(jìn)行初始化
final成員變量
- 如果final修飾的是類的成員變量玻粪,那這個(gè)變量必須在定義時(shí)被初始化隅津,或者在類的構(gòu)造函數(shù)中初始化
- 接口中的變量默認(rèn)是public static final,因?yàn)閟tatic表示只有一個(gè)副本劲室,所以要用final限制實(shí)現(xiàn)接口的類對(duì)該變量進(jìn)行修改(一旦某個(gè)類修改了這個(gè)變量伦仍,其他實(shí)現(xiàn)此接口的類中的這個(gè)變量也會(huì)被修改)
final參數(shù)
在方法范圍內(nèi),final參數(shù)的值或者指向?qū)ο鬅o法被修改很洋。這一特性主要用來向匿名內(nèi)部類傳遞數(shù)據(jù)充蓝,匿名類中所有變量都必須是final變量
編譯時(shí)常量
以下幾種情況的定義會(huì)被當(dāng)作編譯時(shí)常量,即在編譯期間能知道它的確切值(通常在定義時(shí)就被初始化)喉磁,并在編譯時(shí)直接將其替換成字面值
static final int VALUE = 1; // static + final
final int a = 1+2; // final變量是基本數(shù)據(jù)類型或String類型谓苟,且在編譯時(shí)能知道確切值
final String b = "hello";
- 必須是基本數(shù)據(jù)類型或String類型
- 告訴編譯器這塊數(shù)據(jù)是不變的,可在編譯時(shí)執(zhí)行運(yùn)算式协怒,減輕運(yùn)行時(shí)的負(fù)擔(dān)
舉個(gè)例子
String a = "hello2";
final String b = "hello"; //b被當(dāng)成了編譯時(shí)常量涝焙,遇到b會(huì)替換成"Hello"
String c = "hello";
String d = b + 2; //對(duì)編譯器而言,這句和 String d = "hello"+2; 是一樣的
String e = c + 2;
System.out.println((a == d)); //輸出True
System.out.println((a == e)); //輸出False
反編譯字節(jié)碼
編譯后使用反編譯工具javap
孕暇,在命令行輸入javap -v <類名>.class
仑撞。其實(shí)不一定要用-v
,-c
也可以的妖滔,只是展示的信息量不同隧哮,更多用法見javap -help
Constant pool:
#1 = Methodref #18.#45 // java/lang/Object."<init>":()V
#2 = String #46 // hello2
#3 = String #47 // hello
#4 = InvokeDynamic #0:#51 // #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
#5 = Fieldref #52.#53 // java/lang/System.out:Ljava/io/PrintStream;
#6 = Methodref #38.#54 // java/io/PrintStream.println:(Z)V
#38 = Class #68 // java/io/PrintStream
#51 = NameAndType #71:#72 // makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
#52 = Class #73 // java/lang/System
#53 = NameAndType #74:#75 // out:Ljava/io/PrintStream;
-------------------------------------------------------------------------------
Code:
0: ldc #2 // String hello2
2: astore_1 // 存儲(chǔ)變量a
3: ldc #3 // String hello
5: astore_2 // 存儲(chǔ)變量b
6: ldc #3 // String hello
8: astore_3 // 存儲(chǔ)變量c
9: ldc #2 // String hello2
11: astore 4 // 存儲(chǔ)變量d
13: aload_3
14: invokedynamic #4, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
19: astore 5 // 存儲(chǔ)變量e
// ldc:(入棧)常量值從常量池中推送至棧頂
// astore:(出棧)將棧頂引用型數(shù)值存入指定本地變量
// astore_1 等同于 astore 1,astore 4 等同于 astore_4座舍,此處不必糾結(jié)
// invokedynamic:(調(diào)用方法)表明調(diào)用點(diǎn)要實(shí)際執(zhí)行哪個(gè)方法
從例子中可以看到近迁,d的值是定義時(shí)直接以字面值確定的(沒有訪問b),就像a簸州、b鉴竭、c一樣,而e的值是先訪問了c的值岸浑、再進(jìn)行字符串連接才得到的