final關鍵字的字面意思是最終的, 不可修改的. 這彷佛是一個看見名字就大概能知道怎麼用的語法, 但你是否有深究過final在各個場景中的具體使用方法, 注意事項, 以及背後涉及的Java設計思想呢?html
// 代碼示例
public static void main(String[] args) {
final Person p = new Person(20, "炭燒生蠔");
p.setAge(18); //能夠修改p對象的數據
System.out.println(p.getAge()); //輸出18
Person pp = new Person(30, "蠔生燒炭");
p = pp; //這行代碼會報錯, 不能經過編譯, 由於p經final修飾永遠指向上面定義的p對象, 不能指向pp對象.
}
複製代碼
public static void main(String[] args) {
int n1 = 2019; //普通變量
final int n2 = 2019; //final修飾的變量
String s = "20190522";
String s1 = n1 + "0522"; //拼接字符串"20190512"
String s2 = n2 + "0522";
System.out.println(s == s1); //false
System.out.println(s == s2); //true
}
複製代碼
首先要介紹一點: 整數-127-128是默認加載到常量池裏的, 也就是說若是涉及到-127-128的整數操做, 默認在編譯期就能肯定整數的值. 因此這裏我故意選用數字2019(大於128), 避免數字默認就存在常量池中.java
總結: 這個例子想說明的是: 因爲被final修飾的常量會在編譯期進入常量池, 若是有涉及到該常量的操做, 頗有可能在編譯期就已經完成.git
提示: 在JDK1.8之後, 經過內部類訪問外部局部變量時, 無需顯式把外部局部變量聲明爲final. 不是說不須要聲明爲final了, 而是這件事情在編譯期間系統幫咱們作了. 可是咱們仍是有必要了解爲何要用final修飾外部局部變量.編程
public class Outter {
public static void main(String[] args) {
final int a = 10;
new Thread(){
@Override
public void run() {
System.out.println(a);
}
}.start();
}
}
複製代碼
javap -c .class文件的絕對路徑
, 就能查看.class文件的反編譯代碼. 以上的Outter類通過編譯產生兩個.class文件, 分別是Outter.class和Outter$1.class
, 也就是說內部類會單獨編譯成一個.class文件. 下面給出Outter$1.class
的反編譯代碼.Compiled from "Outter.java"
final class forTest.Outter$1 extends java.lang.Thread {
forTest.Outter$1();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Thread."<init>":()V
4: return
public void run();
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: bipush 10
5: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
8: return
}
複製代碼
run()
方法反編譯代碼中的第3行:3: bipush 10
run()
方法執行過程當中是以壓棧的形式存儲到本地變量表中的, 也就是說在內部類打印變量a的值時, 這個變量a不是外部的局部變量a, 由於若是是外部局部變量的話, 應該會使用load
指令加載變量的值. 也就是說系統以拷貝的形式把外部局部變量a複製了一個副本到內部類中, 內部類有一個變量指向外部變量a所指向的值.
//原代碼
public static void test(){
String s1 = "包夾方法a";
a();
String s2 = "包夾方法a";
}
public static final void a(){
System.out.println("我是方法a中的代碼");
System.out.println("我是方法a中的代碼");
}
//通過編譯後
public static void test(){
String s1 = "包夾方法a";
System.out.println("我是方法a中的代碼");
System.out.println("我是方法a中的代碼");
String s2 = "包夾方法a";
}
複製代碼