您好,登錄后才能下訂單哦!
這篇“java內部類引用局部變量與外部類成員變量實例分析”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“java內部類引用局部變量與外部類成員變量實例分析”文章吧。
假如我們有以下的代碼:
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是如何實現匿名內部類的。其中有兩點:
1、匿名內部類可以使用外部類的變量(局部或成員變來那個)。
2、匿名內部類中不同的方法可以共享這些變量。
根據這兩點信息我們就可以分析,可能這些變量會在匿名內部類的字段中保存著,并且在構造的時候將他們的值/引用傳入內部類。這樣就可以保證同時實現上述兩點了。
事實上,Java就是這樣設計的,并且所謂匿名類,其實并不是匿名的,只是編譯器幫我們命名了而已。這點我們可以通過這兩個類編譯出來的字節碼看出來:
// Compiled from Printer.java (version 1.6 : 50.0, super bit)
class levin.test.anonymous.MyApplication$1 implements levin.test.anonymous.Printer {
// Field descriptor #8 Llevin/test/anonymous/MyApplication;
final synthetic levin.test.anonymous.MyApplication this$0;
// Field descriptor #10 J
private final synthetic long val$local2;
// Field descriptor #12 Ljava/lang/Integer;
private final synthetic java.lang.Integer val$param;
// Method descriptor #14 (Llevin/test/anonymous/MyApplication;JLjava/lang/Integer;)V
// Stack: 3, Locals: 5
MyApplication$1(levin.test.anonymous.MyApplication arg0, long arg1, java.lang.Integer arg2);
0 aload_0 [this]
1 aload_1 [arg0]
2 putfield levin.test.anonymous.MyApplication$1.this$0 : levin.test.anonymous.MyApplication [16]
5 aload_0 [this]
6 lload_2 [arg1]
7 putfield levin.test.anonymous.MyApplication$1.val$local2 : long [18]
10 aload_0 [this]
11 aload 4 [arg2]
13 putfield levin.test.anonymous.MyApplication$1.val$param : java.lang.Integer [20]
16 aload_0 [this]
17 invokespecial java.lang.Object() [22]
20 return
Line numbers:
[pc: 0, line: 1]
[pc: 16, line: 13]
Local variable table:
[pc: 0, pc: 21] local: this index: 0 type: new levin.test.anonymous.MyApplication(){}
// Method descriptor #24 ()V
// Stack: 4, Locals: 1
public void print();
0 getstatic java.lang.System.out : java.io.PrintStream [30]
3 ldc <String "Local value: 100"> [36]
5 invokevirtual java.io.PrintStream.println(java.lang.String) : void [38]
8 getstatic java.lang.System.out : java.io.PrintStream [30]
11 new java.lang.StringBuilder [44]
14 dup
15 ldc <String "Local2 value: "> [46]
17 invokespecial java.lang.StringBuilder(java.lang.String) [48]
20 aload_0 [this]
21 getfield levin.test.anonymous.MyApplication$1.val$local2 : long [18]
24 invokevirtual java.lang.StringBuilder.append(long) : java.lang.StringBuilder [50]
27 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [54]
30 invokevirtual java.io.PrintStream.println(java.lang.String) : void [38]
33 getstatic java.lang.System.out : java.io.PrintStream [30]
36 new java.lang.StringBuilder [44]
39 dup
40 ldc <String "Parameter: "> [58]
42 invokespecial java.lang.StringBuilder(java.lang.String) [48]
45 aload_0 [this]
46 getfield levin.test.anonymous.MyApplication$1.val$param : java.lang.Integer [20]
49 invokevirtual java.lang.StringBuilder.append(java.lang.Object) : java.lang.StringBuilder [60]
52 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [54]
55 invokevirtual java.io.PrintStream.println(java.lang.String) : void [38]
58 getstatic java.lang.System.out : java.io.PrintStream [30]
61 new java.lang.StringBuilder [44]
64 dup
65 ldc <String "Field value: "> [63]
67 invokespecial java.lang.StringBuilder(java.lang.String) [48]
70 aload_0 [this]
71 getfield levin.test.anonymous.MyApplication$1.this$0 : levin.test.anonymous.MyApplication [16]
74 invokestatic levin.test.anonymous.MyApplication.access$0(levin.test.anonymous.MyApplication) : int [65]
77 invokevirtual java.lang.StringBuilder.append(int) : java.lang.StringBuilder [71]
80 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [54]
83 invokevirtual java.io.PrintStream.println(java.lang.String) : void [38]
86 return
Line numbers:
[pc: 0, line: 16]
[pc: 8, line: 17]
[pc: 33, line: 18]
[pc: 58, line: 19]
[pc: 86, line: 20]
Local variable table:
[pc: 0, pc: 87] local: this index: 0 type: new levin.test.anonymous.MyApplication(){}
Inner classes:
[inner class info: #1 levin/test/anonymous/MyApplication$1, outer class info: #0
inner name: #0, accessflags: 0 default]
Enclosing Method: #66 #77 levin/test/anonymous/MyApplication.print(Ljava/lang/Integer;)V
}
// 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] }
從這兩段字節碼中可以看出,編譯器為我們的匿名類起了一個叫MyApplication$1的名字,它包含了三個final字段(這里synthetic修飾符是指這些字段是由編譯器生成的,它們并不存在于源代碼中):
MyApplication的應用this$0
long值val$local2
Integer引用val$param
這些字段在構造函數中賦值,而構造函數則是在MyApplication.print()方法中調用。
由此,我們可以得出一個結論:Java對匿名內部類的實現是通過編譯器來支持的,即通過編譯器幫我們產生一個匿名類的類名,將所有在匿名類中用到的局部變量和參數做為內部類的final字段,同是內部類還會引用外部類的實例。其實這里少了local的變量,這是因為local是編譯器常量,編譯器對它做了替換的優化。
其實Java中很多語法都是通過編譯器來支持的,而在虛擬機/字節碼上并沒有什么區別,比如這里的final關鍵字,其實細心的人會發現在字節碼中,param參數并沒有final修飾,而final本身的很多實現就是由編譯器支持的。類似的還有Java中得泛型和逆變、協變等。這是題外話。
有了這個基礎后,我們就可以來分析為什么有些要用final修飾,有些卻不用的問題。
首先我們來分析local2變量,在”匿名類”中,它是通過構造函數傳入到”匿名類”字段中的,因為它是基本類型,因而在夠著函數中賦值時(撇開對函數參數傳遞不同虛擬機的不同實現而產生的不同效果),它事實上只是值的拷貝;因而加入我們可以在”匿名類”中得print()方法中對它賦值,那么這個賦值對外部類中得local2變量不會有影響,而程序員在讀代碼中,是從上往下讀的,所以很容易誤認為這段代碼賦值會對外部類中得local2變量本身產生影響,何況在源碼中他們的名字都是一樣的,所以我認為了避免這種confuse導致的一些問題,Java設計者才設計出了這樣的語法。
對引用類型,其實也是一樣的,因為引用的傳遞事實上也只是傳遞引用的數值(簡單的可以理解成為地址),因而對param,如果可以在”匿名類”中賦值,也不會在外部類的print()后續方法產生影響。雖然這樣,我們還是可以在內部類中改變引用內部的值的,如果引用類型不是只讀類型的話;在這里Integer是只讀類型,因而我們沒法這樣做。
以上就是關于“java內部類引用局部變量與外部類成員變量實例分析”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。