我们都知道final修饰的成员是不可变。下面分析final在内部类的语义特征。
首先看一个简单的例子。
1 public class FinalExample { 2 3 4 private String fis = "final-init"; 5 6 public void method1() { 7 8 final String key = "key1"; 9 10 new Thread(new Runnable() {11 @Override12 public void run() {13 System.out.println(key);14 System.out.println(fis);15 }16 });17 18 }19 20 public void method2() {21 final String key = new String("key1");22 23 new Thread(new Runnable() {24 @Override25 public void run() {26 System.out.println(key);27 System.out.println(fis);28 }29 });30 }31 32 public void method3(final String key) {33 34 new Thread(new Runnable() {35 @Override36 public void run() {37 System.out.println(key);38 System.out.println(fis);39 }40 });41 }42 43 }
编译器生成的代码:
FinalExample类:
1 public class FinalExample { 2 private String fis = "final-init"; 3 4 public FinalExample() { 5 } 6 7 public void method1() { 8 String key = "key1"; 9 new Thread(new Runnable() {10 public void run() {11 System.out.println("key1");12 System.out.println(FinalExample.this.fis);13 }14 });15 }16 17 public void method2() {18 final String key = new String("key1");19 new Thread(new Runnable() {20 public void run() {21 System.out.println(key);22 System.out.println(FinalExample.this.fis);23 }24 });25 }26 27 public void method3(final String key) {28 new Thread(new Runnable() {29 public void run() {30 System.out.println(key);31 System.out.println(FinalExample.this.fis);32 }33 });34 }35 }
方法1的内部类:
1 class FinalExample$1 implements Runnable { 2 FinalExample$1(FinalExample var1) { 3 this.this$0 = var1; 4 } 5 6 public void run() { 7 System.out.println("key1"); 8 System.out.println(FinalExample.access$000(this.this$0)); 9 }10 }
方法2的内部类:
1 class FinalExample$2 implements Runnable { 2 FinalExample$2(FinalExample var1, String var2) { 3 this.this$0 = var1; 4 this.val$key = var2; 5 } 6 7 public void run() { 8 System.out.println(this.val$key); 9 System.out.println(FinalExample.access$000(this.this$0));10 }11 }
方法3的内部类:
1 class FinalExample$3 implements Runnable { 2 FinalExample$3(FinalExample var1, String var2) { 3 this.this$0 = var1; 4 this.val$key = var2; 5 } 6 7 public void run() { 8 System.out.println(this.val$key); 9 System.out.println(FinalExample.access$000(this.this$0));10 }11 }
以上可以看出,final修饰的局部变量。如果是引用类型的变量,内部类会生成一个有参构造方法,将此变量传入构造函数。然后通过外部类中的access$*()方法获取到外部类中的值(该方法是通过编译器生成的)。 如果是基本类型,值直接编译到class文件。
通过汇编指令,反编译暂未发现网上说的final内存屏障语义。