對內部類引用外部方法變量需用final修飾的理解


內部類之所以可以訪問外部類的成員變量,是因爲內部類中引用了外部類的成員變量

參考:http://blog.csdn.net/blumamy/article/details/43303963


參考上面的文章之後,匿名內部類之所以可以引用局部變量,自然是因爲持有局部變量的引用,也是通過類似於構造函數的方式將此變量傳遞到內部類裏面,在內部類裏面會重新有一個變量來引用局部變量所引用的對象,如果局部變量沒有用final修飾,那麼當改變的時候,內部類所引用的變量不會因爲他的改變而改變,所以局部變量必須用final修飾。

測試如下: 

public class JavaDemo {

private String name;
private int num;



void testInner(){
final String picture="戴爾";
new Inner(){
public void showName(){
System.out.println(picture);
}
};
}

class Inner{

}

在內部類Inner中會有一個變量持有傳遞過來的final修飾的picture變量,類似於

String innerPicture = picture;

在JavaDemo$1字節碼中是如下格式

  Last modified 2015-12-14; size 569 bytes
  MD5 checksum d52c1f747d7b708cb19cd659e3d5d9ac
  Compiled from "JavaDemo.java"
class JavaDemo$1 extends JavaDemo$Inner
  SourceFile: "JavaDemo.java"
  EnclosingMethod: #19.#20                // JavaDemo.testInner
  InnerClasses:
       #6; //class JavaDemo$1
       #32= #7 of #19; //Inner=class JavaDemo$Inner of class JavaDemo
  minor version: 0
  major version: 51
  flags: ACC_SUPER
Constant pool:
   #1 = Fieldref           #6.#21         //  JavaDemo$1.this$0:LJavaDemo;
   #2 = Methodref          #7.#22         //  JavaDemo$Inner."<init>":(LJavaDemo;)V
   #3 = Fieldref           #23.#24        //  java/lang/System.out:Ljava/io/PrintStream;
   #4 = String             #25            //  戴爾
   #5 = Methodref          #26.#27        //  java/io/PrintStream.println:(Ljava/lang/String;)V
   #6 = Class              #28            //  JavaDemo$1
   #7 = Class              #31            //  JavaDemo$Inner
   #8 = Utf8               this$0
   #9 = Utf8               LJavaDemo;
  #10 = Utf8               <init>
  #11 = Utf8               (LJavaDemo;)V
  #12 = Utf8               Code
  #13 = Utf8               LineNumberTable
  #14 = Utf8               showName
  #15 = Utf8               ()V
  #16 = Utf8               SourceFile
  #17 = Utf8               JavaDemo.java
  #18 = Utf8               EnclosingMethod
  #19 = Class              #33            //  JavaDemo
  #20 = NameAndType        #34:#15        //  testInner:()V
  #21 = NameAndType        #8:#9          //  this$0:LJavaDemo;
  #22 = NameAndType        #10:#11        //  "<init>":(LJavaDemo;)V
  #23 = Class              #35            //  java/lang/System
  #24 = NameAndType        #36:#37        //  out:Ljava/io/PrintStream;
  #25 = Utf8               戴爾
  #26 = Class              #38            //  java/io/PrintStream
  #27 = NameAndType        #39:#40        //  println:(Ljava/lang/String;)V
  #28 = Utf8               JavaDemo$1
  #29 = Utf8
  #30 = Utf8               InnerClasses
  #31 = Utf8               JavaDemo$Inner
  #32 = Utf8               Inner
  #33 = Utf8               JavaDemo
  #34 = Utf8               testInner
  #35 = Utf8               java/lang/System
  #36 = Utf8               out
  #37 = Utf8               Ljava/io/PrintStream;
  #38 = Utf8               java/io/PrintStream
  #39 = Utf8               println
  #40 = Utf8               (Ljava/lang/String;)V
{
  final JavaDemo this$0;
    flags: ACC_FINAL, ACC_SYNTHETIC


  JavaDemo$1(JavaDemo);
    flags:
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: putfield      #1                  // Field this$0:LJavaDemo;
         5: aload_0
         6: aload_1
         7: invokespecial #2                  // Method JavaDemo$Inner."<init>":(LJavaDemo;)V
        10: return
      LineNumberTable:
        line 12: 0


  public void showName();
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #4                  // String 戴爾
         5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 14: 0
        line 15: 8
}


其他:

當內部類引用成員變量的時候,是將外部類生成的對象傳遞進入內部類,而這個對象相對於內部類對象是唯一不變的,他們是共存的,有內部類對象的時候,外部類對象是必然存在的,而且是唯一的,所以內部類都有一個外部類對象的引用,當外部類的變量發生變化,內部類在調用外部類變量的時候,會先尋找外部類對象地址,再調用該對象的變量。

如上面的 final JavaDemo this$0;


附上JavaDemo.class 和JavaDemo$Inner.class


Classfile /C:/Users/lizheng-ds3/Desktop/test/JavaDemo.class
  Last modified 2015-12-14; size 424 bytes
  MD5 checksum e046fc0b074cc03c114a7052e5c2328b
  Compiled from "JavaDemo.java"
public class JavaDemo
  SourceFile: "JavaDemo.java"
  InnerClasses:
       #7= #6 of #4; //Inner=class JavaDemo$Inner of class JavaDemo
       #2; //class JavaDemo$1
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#21         //  java/lang/Object."<init>":()V
   #2 = Class              #22            //  JavaDemo$1
   #3 = Methodref          #2.#23         //  JavaDemo$1."<init>":(LJavaDemo;)V
   #4 = Class              #24            //  JavaDemo
   #5 = Class              #25            //  java/lang/Object
   #6 = Class              #26            //  JavaDemo$Inner
   #7 = Utf8               Inner
   #8 = Utf8               InnerClasses
   #9 = Utf8
  #10 = Utf8               name
  #11 = Utf8               Ljava/lang/String;
  #12 = Utf8               num
  #13 = Utf8               I
  #14 = Utf8               <init>
  #15 = Utf8               ()V
  #16 = Utf8               Code
  #17 = Utf8               LineNumberTable
  #18 = Utf8               testInner
  #19 = Utf8               SourceFile
  #20 = Utf8               JavaDemo.java
  #21 = NameAndType        #14:#15        //  "<init>":()V
  #22 = Utf8               JavaDemo$1
  #23 = NameAndType        #14:#27        //  "<init>":(LJavaDemo;)V
  #24 = Utf8               JavaDemo
  #25 = Utf8               java/lang/Object
  #26 = Utf8               JavaDemo$Inner
  #27 = Utf8               (LJavaDemo;)V
{
  public JavaDemo();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 4: 0
        line 19: 4


  void testInner();
    flags:
    Code:
      stack=3, locals=2, args_size=1
         0: new           #2                  // class JavaDemo$1
         3: dup
         4: aload_0
         5: invokespecial #3                  // Method JavaDemo$1."<init>":(LJavaDemo;)V
         8: pop
         9: return
      LineNumberTable:
        line 12: 0
        line 17: 9
}



------------------------


Classfile /C:/Users/lizheng-ds3/Desktop/test/JavaDemo$Inner.class
  Last modified 2015-12-14; size 310 bytes
  MD5 checksum f4de5d1e3475cd1ae4efd3e4a111e068
  Compiled from "JavaDemo.java"
class JavaDemo$Inner
  SourceFile: "JavaDemo.java"
  InnerClasses:
       #17= #3 of #15; //Inner=class JavaDemo$Inner of class JavaDemo
  minor version: 0
  major version: 51
  flags: ACC_SUPER
Constant pool:
   #1 = Fieldref           #3.#13         //  JavaDemo$Inner.this$0:LJavaDemo;
   #2 = Methodref          #4.#14         //  java/lang/Object."<init>":()V
   #3 = Class              #16            //  JavaDemo$Inner
   #4 = Class              #19            //  java/lang/Object
   #5 = Utf8               this$0
   #6 = Utf8               LJavaDemo;
   #7 = Utf8               <init>
   #8 = Utf8               (LJavaDemo;)V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               SourceFile
  #12 = Utf8               JavaDemo.java
  #13 = NameAndType        #5:#6          //  this$0:LJavaDemo;
  #14 = NameAndType        #7:#20         //  "<init>":()V
  #15 = Class              #21            //  JavaDemo
  #16 = Utf8               JavaDemo$Inner
  #17 = Utf8               Inner
  #18 = Utf8               InnerClasses
  #19 = Utf8               java/lang/Object
  #20 = Utf8               ()V
  #21 = Utf8               JavaDemo
{
  final JavaDemo this$0;
    flags: ACC_FINAL, ACC_SYNTHETIC


  JavaDemo$Inner(JavaDemo);
    flags:
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: putfield      #1                  // Field this$0:LJavaDemo;
         5: aload_0
         6: invokespecial #2                  // Method java/lang/Object."<init>":()V
         9: return
      LineNumberTable:
        line 19: 0
}


查看class文件方式

javac -verbose class文件目錄


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章