java內部類引用局部變量與外部類成員變量實例分析

這篇“java內部類引用局部變量與外部類成員變量實例分析”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“java內部類引用局部變量與外部類成員變量實例分析”文章吧。

創(chuàng)新互聯(lián)公司長期為近千家客戶提供的網站建設服務,團隊從業(yè)經驗10年,關注不同地域、不同群體,并針對不同對象提供差異化的產品和服務;打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網生態(tài)環(huán)境。為偏關企業(yè)提供專業(yè)的成都網站設計、成都網站制作、外貿網站建設,偏關網站改版等技術服務。擁有10多年豐富建站經驗和眾多成功案例,為您定制開發(fā)。

假如我們有以下的代碼:

interface Printer {     public void print(); } class MyApplication {     private int field = 10;      public void print(final Integer param) {         final long local = 100;         final long local2 = param.longValue() + 100;         Printer printer = new Printer() {             @Override             public void print() {                 System.out.println("Local value: " + local);                 System.out.println("Local2 value: " + local2);                 System.out.println("Parameter: " + param);                 System.out.println("Field value: " + field);             }         };         printer.print();     } }

這里因為param要在匿名內部類的print()方法中使用,因而它要用final修飾;local/local2是局部變量,因而也需要final修飾;而field是外部類MyApplication的字段,因而不需要final修飾。這種設計是基于什么理由呢?

我想這個問題應該從Java是如何實現(xiàn)匿名內部類的。其中有兩點:

1、匿名內部類可以使用外部類的變量(局部或成員變來那個)。

2、匿名內部類中不同的方法可以共享這些變量。

根據這兩點信息我們就可以分析,可能這些變量會在匿名內部類的字段中保存著,并且在構造的時候將他們的值/引用傳入內部類。這樣就可以保證同時實現(xiàn)上述兩點了。

事實上,Java就是這樣設計的,并且所謂匿名類,其實并不是匿名的,只是編譯器幫我們命名了而已。這點我們可以通過這兩個類編譯出來的字節(jié)碼看出來:

  1. // Compiled from Printer.java (version 1.6 : 50.0, super bit) 

  2. class levin.test.anonymous.MyApplication$1 implements levin.test.anonymous.Printer { 

  3.    

  4.   // Field descriptor #8 Llevin/test/anonymous/MyApplication; 

  5.   final synthetic levin.test.anonymous.MyApplication this$0; 

  6.    

  7.   // Field descriptor #10 J 

  8.   private final synthetic long val$local2; 

  9.    

  10.   // Field descriptor #12 Ljava/lang/Integer; 

  11.   private final synthetic java.lang.Integer val$param; 

  12.    

  13.   // Method descriptor #14 (Llevin/test/anonymous/MyApplication;JLjava/lang/Integer;)V 

  14.   // Stack: 3, Locals: 5 

  15.   MyApplication$1(levin.test.anonymous.MyApplication arg0, long arg1, java.lang.Integer arg2); 

  16.      0  aload_0 [this] 

  17.      1  aload_1 [arg0] 

  18.      2  putfield levin.test.anonymous.MyApplication$1.this$0 : levin.test.anonymous.MyApplication [16] 

  19.      5  aload_0 [this] 

  20.      6  lload_2 [arg1] 

  21.      7  putfield levin.test.anonymous.MyApplication$1.val$local2 : long [18] 

  22.     10  aload_0 [this] 

  23.     11  aload 4 [arg2] 

  24.     13  putfield levin.test.anonymous.MyApplication$1.val$param : java.lang.Integer [20] 

  25.     16  aload_0 [this] 

  26.     17  invokespecial java.lang.Object() [22] 

  27.     20  return 

  28.       Line numbers: 

  29.         [pc: 0, line: 1] 

  30.         [pc: 16, line: 13] 

  31.       Local variable table: 

  32.         [pc: 0, pc: 21] local: this index: 0 type: new levin.test.anonymous.MyApplication(){} 

  33.    

  34.   // Method descriptor #24 ()V 

  35.   // Stack: 4, Locals: 1 

  36.   public void print(); 

  37.      0  getstatic java.lang.System.out : java.io.PrintStream [30] 

  38.      3  ldc <String "Local value: 100"> [36] 

  39.      5  invokevirtual java.io.PrintStream.println(java.lang.String) : void [38] 

  40.      8  getstatic java.lang.System.out : java.io.PrintStream [30] 

  41.     11  new java.lang.StringBuilder [44] 

  42.     14  dup 

  43.     15  ldc <String "Local2 value: "> [46] 

  44.     17  invokespecial java.lang.StringBuilder(java.lang.String) [48] 

  45.     20  aload_0 [this] 

  46.     21  getfield levin.test.anonymous.MyApplication$1.val$local2 : long [18] 

  47.     24  invokevirtual java.lang.StringBuilder.append(long) : java.lang.StringBuilder [50] 

  48.     27  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [54] 

  49.     30  invokevirtual java.io.PrintStream.println(java.lang.String) : void [38] 

  50.     33  getstatic java.lang.System.out : java.io.PrintStream [30] 

  51.     36  new java.lang.StringBuilder [44] 

  52.     39  dup 

  53.     40  ldc <String "Parameter: "> [58] 

  54.     42  invokespecial java.lang.StringBuilder(java.lang.String) [48] 

  55.     45  aload_0 [this] 

  56.     46  getfield levin.test.anonymous.MyApplication$1.val$param : java.lang.Integer [20] 

  57.     49  invokevirtual java.lang.StringBuilder.append(java.lang.Object) : java.lang.StringBuilder [60] 

  58.     52  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [54] 

  59.     55  invokevirtual java.io.PrintStream.println(java.lang.String) : void [38] 

  60.     58  getstatic java.lang.System.out : java.io.PrintStream [30] 

  61.     61  new java.lang.StringBuilder [44] 

  62.     64  dup 

  63.     65  ldc <String "Field value: "> [63] 

  64.     67  invokespecial java.lang.StringBuilder(java.lang.String) [48] 

  65.     70  aload_0 [this] 

  66.     71  getfield levin.test.anonymous.MyApplication$1.this$0 : levin.test.anonymous.MyApplication [16] 

  67.     74  invokestatic levin.test.anonymous.MyApplication.access$0(levin.test.anonymous.MyApplication) : int [65] 

  68.     77  invokevirtual java.lang.StringBuilder.append(int) : java.lang.StringBuilder [71] 

  69.     80  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [54] 

  70.     83  invokevirtual java.io.PrintStream.println(java.lang.String) : void [38] 

  71.     86  return 

  72.       Line numbers: 

  73.         [pc: 0, line: 16] 

  74.         [pc: 8, line: 17] 

  75.         [pc: 33, line: 18] 

  76.         [pc: 58, line: 19] 

  77.         [pc: 86, line: 20] 

  78.       Local variable table: 

  79.         [pc: 0, pc: 87] local: this index: 0 type: new levin.test.anonymous.MyApplication(){} 

  80.  

  81.   Inner classes: 

  82.     [inner class info: #1 levin/test/anonymous/MyApplication$1, outer class info: #0 

  83.      inner name: #0, accessflags: 0 default] 

  84.   Enclosing Method: #66  #77 levin/test/anonymous/MyApplication.print(Ljava/lang/Integer;)V 

  85. }

// Compiled from Printer.java (version 1.6 : 50.0, super bit)  class levin.test.anonymous.MyApplication {        // Field descriptor #6 I    private int field;       // Method descriptor #8 ()V    // Stack: 2, Locals: 1    MyApplication();       0  aload_0 [this]       1  invokespecial java.lang.Object() [10]       4  aload_0 [this]       5  bipush 10       7  putfield levin.test.anonymous.MyApplication.field : int [12]      10  return        Line numbers:          [pc: 0, line: 7]          [pc: 4, line: 8]          [pc: 10, line: 7]        Local variable table:          [pc: 0, pc: 11] local: this index: 0 type: levin.test.anonymous.MyApplication        // Method descriptor #19 (Ljava/lang/Integer;)V    // Stack: 6, Locals: 7    public void print(java.lang.Integer param);       0  ldc2_w <Long 100> [20]       3  lstore_2 [local]       4  aload_1 [param]       5  invokevirtual java.lang.Integer.longValue() : long [22]       8  ldc2_w <Long 100> [20]      11  ladd      12  lstore 4 [local2]      14  new levin.test.anonymous.MyApplication$1 [28]      17  dup      18  aload_0 [this]      19  lload 4 [local2]      21  aload_1 [param]      22  invokespecial levin.test.anonymous.MyApplication$1(levin.test.anonymous.MyApplication, long, java.lang.Integer) [30]      25  astore 6 [printer]      27  aload 6 [printer]      29  invokeinterface levin.test.anonymous.Printer.print() : void [33] [nargs: 1]      34  return        Line numbers:          [pc: 0, line: 11]          [pc: 4, line: 12]          [pc: 14, line: 13]          [pc: 27, line: 22]          [pc: 34, line: 23]        Local variable table:          [pc: 0, pc: 35] local: this index: 0 type: levin.test.anonymous.MyApplication          [pc: 0, pc: 35] local: param index: 1 type: java.lang.Integer          [pc: 4, pc: 35] local: local index: 2 type: long          [pc: 14, pc: 35] local: local2 index: 4 type: long          [pc: 27, pc: 35] local: printer index: 6 type: levin.test.anonymous.Printer    // Method descriptor #45 (Llevin/test/anonymous/MyApplication;)I    // Stack: 1, Locals: 1    static synthetic int access$0(levin.test.anonymous.MyApplication arg0);      0  aload_0 [arg0]      1  getfield levin.test.anonymous.MyApplication.field : int [12]      4  ireturn        Line numbers:          [pc: 0, line: 8]      Inner classes:      [inner class info: #28 levin/test/anonymous/MyApplication$1, outer class info: #0       inner name: #0, accessflags: 0 default]  }

從這兩段字節(jié)碼中可以看出,編譯器為我們的匿名類起了一個叫MyApplication$1的名字,它包含了三個final字段(這里synthetic修飾符是指這些字段是由編譯器生成的,它們并不存在于源代碼中):

MyApplication的應用this$0

long值val$local2

Integer引用val$param

這些字段在構造函數中賦值,而構造函數則是在MyApplication.print()方法中調用。

由此,我們可以得出一個結論:Java對匿名內部類的實現(xiàn)是通過編譯器來支持的,即通過編譯器幫我們產生一個匿名類的類名,將所有在匿名類中用到的局部變量和參數做為內部類的final字段,同是內部類還會引用外部類的實例。其實這里少了local的變量,這是因為local是編譯器常量,編譯器對它做了替換的優(yōu)化。

其實Java中很多語法都是通過編譯器來支持的,而在虛擬機/字節(jié)碼上并沒有什么區(qū)別,比如這里的final關鍵字,其實細心的人會發(fā)現(xiàn)在字節(jié)碼中,param參數并沒有final修飾,而final本身的很多實現(xiàn)就是由編譯器支持的。類似的還有Java中得泛型和逆變、協(xié)變等。這是題外話。

有了這個基礎后,我們就可以來分析為什么有些要用final修飾,有些卻不用的問題。

首先我們來分析local2變量,在”匿名類”中,它是通過構造函數傳入到”匿名類”字段中的,因為它是基本類型,因而在夠著函數中賦值時(撇開對函數參數傳遞不同虛擬機的不同實現(xiàn)而產生的不同效果),它事實上只是值的拷貝;因而加入我們可以在”匿名類”中得print()方法中對它賦值,那么這個賦值對外部類中得local2變量不會有影響,而程序員在讀代碼中,是從上往下讀的,所以很容易誤認為這段代碼賦值會對外部類中得local2變量本身產生影響,何況在源碼中他們的名字都是一樣的,所以我認為了避免這種confuse導致的一些問題,Java設計者才設計出了這樣的語法。

對引用類型,其實也是一樣的,因為引用的傳遞事實上也只是傳遞引用的數值(簡單的可以理解成為地址),因而對param,如果可以在”匿名類”中賦值,也不會在外部類的print()后續(xù)方法產生影響。雖然這樣,我們還是可以在內部類中改變引用內部的值的,如果引用類型不是只讀類型的話;在這里Integer是只讀類型,因而我們沒法這樣做。

以上就是關于“java內部類引用局部變量與外部類成員變量實例分析”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

當前文章:java內部類引用局部變量與外部類成員變量實例分析
網站地址:http://bm7419.com/article38/psdisp.html

成都網站建設公司_創(chuàng)新互聯(lián),為您提供微信公眾號、網站排名、微信小程序、網站建設動態(tài)網站、ChatGPT

廣告

聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)

成都seo排名網站優(yōu)化