我们都知道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 @Override 12 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 @Override 25 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 @Override 36 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内存屏障语义。