1. 局部內部類
源碼:
public void subscribeQueue(Jedis jedis,String[] channels) {
// TODO Auto-generated method stub
class MyJedisPubSub extendsJedisPubSub{
public void onMessage(Stringchannel, String message) {
System.out.println(message);
loadLua(jedis,message);
}
}
MyJedisPubSub myJedisPubSub = newMyJedisPubSub();
jedis.subscribe(myJedisPubSub ,channels);
}
方法字節碼:
public voidsubscribeQueue(redis.clients.jedis.Jedis, java.lang.String[]);
descriptor: (Lredis/clients/jedis/Jedis;[Ljava/lang/String;)V
flags: ACC_PUBLIC
Code:
stack=4, locals=4, args_size=3
0: new #401 // classRedisDaoImpl$1MyJedisPubS
ub
3: dup
4: aload_0
5: aload_1
6: invokespecial #403 // MethodRedisDaoImpl$1MyJedisPub
Sub."<init>":(LRedisDaoImpl;Lredis/clients/jedis/Jedis;)V
9: astore_3
10: aload_1
11: aload_3
12: aload_2
13: invokevirtual #406 // Methodredis/clients/jedis/Jedi
s.subscribe:(Lredis/clients/jedis/JedisPubSub;[Ljava/lang/String;)V
16: return
LineNumberTable:
line 497: 0
line 498: 10
line 499: 16
LocalVariableTable:
Start Length Slot Name Signature
0 17 0 this LRedisDaoImpl;
0 17 1 jedis Lredis/clients/jedis/Jedis;
0 17 2 channels [Ljava/lang/String;
10 7 3 myJedisPubSub LRedisDaoImpl$1MyJedisPubSub;
}
SourceFile: "RedisDaoImpl.java"
InnerClasses:
#416= #401; //MyJedisPubSub=classRedisDaoImpl$1MyJedisPubSub
public static #417= #124 of #133;//Entry=class java/util/Map$Entry of clas
s java/util/Map
內部類字節碼:
Last modified 2017-7-21;size 1053 bytes
MD5 checksum 0f8a8ded714120d03f866c55bf5177d9
Compiled from"RedisDaoImpl.java"
classRedisDaoImpl$1MyJedisPubSub extends redis.clients.jedis.JedisPubSub
minor version: 0
major version: 52
flags: ACC_SUPER
Constantpool:
#1 = Class #2 // RedisDaoImpl$1MyJedisPubSub
#2 = Utf8 RedisDaoImpl$1MyJedisPubSub
#3 = Class #4 // redis/clients/jedis/JedisPubSub
#4 = Utf8 redis/clients/jedis/JedisPubSub
#5 = Utf8 this$0
#6 = Utf8 LRedisDaoImpl;
#7 = Utf8 val$jedis
#8 = Utf8 Lredis/clients/jedis/Jedis;
#9 = Utf8 <init>
#10 = Utf8 (LRedisDaoImpl;Lredis/clients/jedis/Jedis;)V
#11 = Utf8 Code
#12 = Fieldref #1.#13 // RedisDaoImpl$1MyJedisPubSub.this$0:
LRedisDaoImpl;
#13 = NameAndType #5:#6 // this$0:LRedisDaoImpl;
#14 = Fieldref #1.#15 // RedisDaoImpl$1MyJedisPubSub.val$jed
is:Lredis/clients/jedis/Jedis;
#15 = NameAndType #7:#8 //val$jedis:Lredis/clients/jedis/Jedi
s;
#16 = Methodref #3.#17 // redis/clients/jedis/JedisPubSub."<i
nit>":()V
#17 = NameAndType #9:#18 // "<init>":()V
#18 = Utf8 ()V
#19 = Utf8 LineNumberTable
#20 = Utf8 LocalVariableTable
#21 = Utf8 this
#22 = Utf8 LRedisDaoImpl$1MyJedisPubSub;
#23 = Utf8 onMessage
#24 = Utf8 (Ljava/lang/String;Ljava/lang/String;)V
#25 = Fieldref #26.#28 // java/lang/System.out:Ljava/io/Print
Stream;
#26 = Class #27 // java/lang/System
#27 = Utf8 java/lang/System
#28 = NameAndType #29:#30 // out:Ljava/io/PrintStream;
#29 = Utf8 out
#30 = Utf8 Ljava/io/PrintStream;
#31 = Methodref #32.#34 // java/io/PrintStream.println:(Ljava/
lang/String;)V
#32 = Class #33 // java/io/PrintStream
#33 = Utf8 java/io/PrintStream
#34 = NameAndType #35:#36 // println:(Ljava/lang/String;)V
#35 = Utf8 println
#36 = Utf8 (Ljava/lang/String;)V
#37 = Methodref #38.#40 // RedisDaoImpl.loadLua:(Lredis/client
s/jedis/Jedis;Ljava/lang/String;)Ljava/lang/String;
#38 = Class #39 // RedisDaoImpl
#39 = Utf8 RedisDaoImpl
#40 = NameAndType #41:#42 // loadLua:(Lredis/clients/jedis/Jedis
;Ljava/lang/String;)Ljava/lang/String;
#41 = Utf8 loadLua
#42 = Utf8 (Lredis/clients/jedis/Jedis;Ljava/lang/String;)Ljava/
lang/String;
#43 = Utf8 channel
#44 = Utf8 Ljava/lang/String;
#45 = Utf8 message
#46 = Utf8 SourceFile
#47 = Utf8 RedisDaoImpl.java
#48 = Utf8 EnclosingMethod
#49 = NameAndType #50:#51 // subscribeQueue:(Lredis/clients/jedi
s/Jedis;[Ljava/lang/String;)V
#50 = Utf8 subscribeQueue
#51 = Utf8 (Lredis/clients/jedis/Jedis;[Ljava/lang/String;)V
#52 = Utf8 InnerClasses
#53 = Utf8 MyJedisPubSub
{
final RedisDaoImplthis$0;
descriptor:LRedisDaoImpl;
flags: ACC_FINAL,ACC_SYNTHETIC
private finalredis.clients.jedis.Jedis val$jedis;
descriptor: Lredis/clients/jedis/Jedis;
flags: ACC_PRIVATE,ACC_FINAL, ACC_SYNTHETIC
RedisDaoImpl$1MyJedisPubSub(RedisDaoImpl,redis.clients.jedis.Jedis);
descriptor:(LRedisDaoImpl;Lredis/clients/jedis/Jedis;)V
flags:
Code:
stack=2,locals=3, args_size=3
0: aload_0
1: aload_1
2:putfield #12 // Field this$0:LRedisDaoImpl;
5: aload_0
6: aload_2
7:putfield #14 // Fieldval$jedis:Lredis/clients/
jedis/Jedis;
10:aload_0
11:invokespecial #16 //Method redis/clients/jedis/Jedi
sPubSub."<init>":()V
14: return
LineNumberTable:
line 491: 0
LocalVariableTable:
Start Length Slot Name Signature
0 15 0 this LRedisDaoImpl$1MyJedisPubSub;
public void onMessage(java.lang.String,java.lang.String);
descriptor:(Ljava/lang/String;Ljava/lang/String;)V
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=3
0: getstatic #25 // Fieldjava/lang/System.out:Ljav
a/io/PrintStream;
3: aload_2
4: invokevirtual #31 // Methodjava/io/PrintStream.prin
tln:(Ljava/lang/String;)V
7: aload_0
8: getfield #12 // Field this$0:LRedisDaoImpl;
11: aload_0
12: getfield #14 // Fieldval$jedis:Lredis/clients/
jedis/Jedis;
15: aload_2
16: invokevirtual #37 // Method RedisDaoImpl.loadLua:(Lr
edis/clients/jedis/Jedis;Ljava/lang/String;)Ljava/lang/String;
19: pop
20: return
LineNumberTable:
line 493: 0
line 494: 7
line 495: 20
LocalVariableTable:
Start Length Slot Name Signature
0 21 0 this LRedisDaoImpl$1MyJedisPubSub;
0 21 1 channel Ljava/lang/String;
0 21 2 message Ljava/lang/String;
}
SourceFile: "RedisDaoImpl.java"
EnclosingMethod:#38.#49 //RedisDaoImpl.subscribeQueue
InnerClasses:
#53= #1;//MyJedisPubSub=class RedisDaoImpl$1MyJedisPubSub
--可以看到局部內部類的字節碼佈局和正常內部類大同小異,但是注意紅色字體內容,首先局部內部類定義了一個外部類對象的引用final RedisDaoImpl this$0;還有如果內部類使用外圍方法的局部變量(包括參數),那麼還得在內部類中定義和該局部變量一致的引用比如private final redis.clients.jedis.Jedis val$jedis;這個有什麼用呢,再看內部類的構造方法:RedisDaoImpl$1MyJedisPubSub(RedisDaoImpl, redis.clients.jedis.Jedis);可見構造方法中是需要初始化這兩個成員變量的,而這兩個成員變量其實就是使用了外部類的this引用和外圍方法參數Jedis jedis來賦值的:
0:aload_0
1: aload_1
2: putfield #12 // Field this$0:LRedisDaoImpl;
5: aload_0
6: aload_2
7: putfield #14 // Fieldval$jedis:Lredis/clients/
jedis/Jedis;
--指令的意思是轉載參數1和參數2 ,並賦值給成員變量this$0和val$jedis。(注意對於外部類指針而言是必須的定義和賦值的,而參數是內部類使用到才需要這樣操作),目的就是局部內部類可以正常使用外部類的成員變量和方法(包括私有)還有外圍方法的局部變量。編譯器通過這種機制保證局部內部類的使用訪問效果跟局部語句塊一樣。
注意:內部類的構造方法跟正常類不一樣,它先初始化了編譯器添加的成員變量後再調用父類的構造方法。
2.成員內部類
源碼:
publicclass ChildTest extends test {
Map<String,String> map = newHashMap<String,String>();
/**
* @paramparam
*/
ChildTest() {
map.put("hello","world");
// TODOAuto-generated constructor stub
}
void fun(){
}
class interClass{
void usefun(){
map.get("hello");
fun();
}
}
}
內部類的字節碼:
Classfile/D:/javadevelop/eclipse/user/workspace/RedisDao/bin/ChildTest$interCla
ss.class
Last modified 2017-7-21;size 629 bytes
MD5 checksum 49d8bf61790022b45521275fc68a0abd
Compiled from"ChildTest.java"
classChildTest$interClass
minor version: 0
major version: 52
flags: ACC_SUPER
Constantpool:
#1 = Class #2 // ChildTest$interClass
#2 = Utf8 ChildTest$interClass
#3 = Class #4 // java/lang/Object
#4 = Utf8 java/lang/Object
#5 = Utf8 this$0
#6 = Utf8 LChildTest;
#7 = Utf8 <init>
#8 = Utf8 (LChildTest;)V
#9 = Utf8 Code
#10 = Fieldref #1.#11 // ChildTest$interClass.this$0:LChildT
est;
#11 = NameAndType #5:#6 // this$0:LChildTest;
#12 = Methodref #3.#13 //java/lang/Object."<init>":()V
#13 = NameAndType #7:#14 // "<init>":()V
#14 = Utf8 ()V
#15 = Utf8 LineNumberTable
#16 = Utf8 LocalVariableTable
#17 = Utf8 this
#18 = Utf8 LChildTest$interClass;
#19 = Utf8 usefun
#20 = Fieldref #21.#23 // ChildTest.map:Ljava/util/Map;
#21 = Class #22 // ChildTest
#22 = Utf8 ChildTest
#23 = NameAndType #24:#25 // map:Ljava/util/Map;
#24 = Utf8 map
#25 = Utf8 Ljava/util/Map;
#26 = String #27 // hello
#27 = Utf8 hello
#28 = InterfaceMethodref #29.#31 // java/util/Map.get:(Ljava/lang/Objec
t;)Ljava/lang/Object;
#29 = Class #30 // java/util/Map
#30 = Utf8 java/util/Map
#31 = NameAndType #32:#33 // get:(Ljava/lang/Object;)Ljava/lang/
Object;
#32 = Utf8 get
#33 = Utf8 (Ljava/lang/Object;)Ljava/lang/Object;
#34 = Methodref #21.#35 // ChildTest.fun:()V
#35 = NameAndType #36:#14 // fun:()V
#36 = Utf8 fun
#37 = Utf8 SourceFile
#38 = Utf8 ChildTest.java
#39 = Utf8 InnerClasses
#40 = Utf8 interClass
{
final ChildTestthis$0;
descriptor:LChildTest;
flags: ACC_FINAL,ACC_SYNTHETIC
ChildTest$interClass(ChildTest);
descriptor: (LChildTest;)V
flags:
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2:putfield #10 // Field this$0:LChildTest;
5: aload_0
6: invokespecial #12 // Methodjava/lang/Object."<init>
":()V
9: return
LineNumberTable:
line 29: 0
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this LChildTest$interClass;
void usefun();
descriptor: ()V
flags:
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: getfield #10 // Field this$0:LChildTest;
4: getfield #20 // FieldChildTest.map:Ljava/util/
Map;
7: ldc #26 // String hello
9: invokeinterface #28, 2 // InterfaceMethod java/util/Map.g
et:(Ljava/lang/Object;)Ljava/lang/Object;
14: pop
15: aload_0
16: getfield #10 // Field this$0:LChildTest;
19: invokevirtual #34 // Method ChildTest.fun:()V
22: return
LineNumberTable:
line 31: 0
line 32: 15
line 33: 22
LocalVariableTable:
Start Length Slot Name Signature
0 23 0 this LChildTest$interClass;
}
SourceFile: "ChildTest.java"
InnerClasses:
#40= #1 of #21;//interClass=class ChildTest$interClass of class ChildTest
--其實和局部方法類似,還更簡單點,編譯器自動添加了一個外部類引用,並且把外部類的指針傳遞進來賦給了該引用,明白這點就能明白爲什麼局部內部類和成員內部類可以正常訪問外部類的所有成員,更明白了爲什麼創建一個局部內部類或成員內部類對象必須用外部類成員對象.new創建(因爲這兩類內部類的構造方法中需要傳入外部類對象作爲參數)。如:
ChildTesttest = new ChildTest();
ChildTest.interClasstest2 = test.new interClass();
這兩行代碼字節碼如下:
0: new #1 // class ChildTest
3: dup
4: invokespecial #36 // Method"<init>":()V
7: astore_1
8: new #37 // class ChildTest$interClass
11: dup
12: aload_1
13: dup
14: invokevirtual #39 // Methodjava/lang/Object.getClas
s:()Ljava/lang/Class;
17: pop
18: invokespecial #45 // MethodChildTest$interClass."<i
nit>":(LChildTest;)V
21: astore_2
22: return
--翻譯過來就是先new出一個ChildTest對象,然後把該對象作爲參數創建出一個ChildTest$inter對象。
2. 靜態內部類:
源碼:
publicclass ChildTest extends test {
private Map<String,String> map = newHashMap<String,String>();
/**
* @paramparam
*/
ChildTest() {
map.put("hello","world");
// TODO Auto-generated constructorstub
}
void fun(){
}
static class interClass{
void usefun(){
System.out.println("zrf");
}
}
/**
*
* @author : zhengrf1
* @date 創建時間:2017年7月20日 上午10:47:38
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ChildTest.interClass test2 = newChildTest.interClass();
}
}
內部類的字節碼:
Last modified 2017-7-22; size 551 bytes
MD5 checksum 20c26ef1c8018c21b7c63e47edc3938d
Compiled from "ChildTest.java"
classChildTest$interClass
minor version: 0
major version: 52
flags: ACC_SUPER
Constantpool:
#1 = Class #2 // ChildTest$interClass
#2 = Utf8 ChildTest$interClass
#3 = Class #4 // java/lang/Object
#4 = Utf8 java/lang/Object
#5 =Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Methodref #3.#9 //java/lang/Object."<init>":()V
#9 = NameAndType #5:#6 // "<init>":()V
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 LChildTest$interClass;
#14 = Utf8 usefun
#15 = Fieldref #16.#18 // java/lang/System.out:Ljava/io/Print
Stream;
#16 = Class #17 // java/lang/System
#17 = Utf8 java/lang/System
#18 = NameAndType #19:#20 // out:Ljava/io/PrintStream;
#19 = Utf8 out
#20 = Utf8 Ljava/io/PrintStream;
#21 = String #22 // zrf
#22 = Utf8 zrf
#23 = Methodref #24.#26 // java/io/PrintStream.println:(Ljava/
lang/String;)V
#24 = Class #25 // java/io/PrintStream
#25 = Utf8 java/io/PrintStream
#26 = NameAndType #27:#28 // println:(Ljava/lang/String;)V
#27 = Utf8 println
#28 = Utf8 (Ljava/lang/String;)V
#29 = Utf8 SourceFile
#30 = Utf8 ChildTest.java
#31 = Utf8 InnerClasses
#32 = Class #33 // ChildTest
#33 = Utf8 ChildTest
#34 = Utf8 interClass
{
ChildTest$interClass();
descriptor: ()V
flags:
Code:
stack=1,locals=1, args_size=1
0: aload_0
1:invokespecial #8 //Method java/lang/Object."<init>
":()V
4: return
LineNumberTable:
line 29: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LChildTest$interClass;
void usefun();
descriptor: ()V
flags:
Code:
stack=2,locals=1, args_size=1
0:getstatic #15 // Fieldjava/lang/System.out:Ljav
a/io/PrintStream;
3:ldc #21 // String zrf
5:invokevirtual #23 //Method java/io/PrintStream.prin
tln:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 31: 0
line 32: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this LChildTest$interClass;
}
--首先看看構造方法,紅色字段,可見靜態成員內部類的構造方法相對簡單,就是個無參構造方法,另外編譯器也不會幫我們定義什麼成員變量。再看內部類的成員方法,沒什麼好講,跟正常類的方法一樣。另外注意,靜態內部類對外部類的訪問權限跟靜態方法一樣,只能調用外部類的靜態成員,不能調用外部類的非靜態成員。
3. 匿名內部類
源碼:
publicclass ChildTest extends test {
private Map<String,String> map = newHashMap<String,String>();
public interface interClass{
public void usefun(int a);
}
/**
* @paramparam
*/
ChildTest() {
map.put("hello","world");
// TODO Auto-generated constructorstub
}
public void test(){
}
void fun(int a){
new interClass(){
public void usefun(int b){
System.out.println(a);
}
}.usefun(a);
}
}內部類字節碼:
Last modified 2017-7-22; size 701 bytes
MD5 checksum 391c128ec2eb239d4927333fd42c99f7
Compiled from "ChildTest.java"
classChildTest$1 implements ChildTest$interClass
minor version: 0
major version: 52
flags: ACC_SUPER
Constantpool:
#1 = Class #2 // ChildTest$1
#2 = Utf8 ChildTest$1
#3 = Class #4 // java/lang/Object
#4 = Utf8 java/lang/Object
#5 = Class #6 // ChildTest$interClass
#6 = Utf8 ChildTest$interClass
#7 = Utf8 this$0
#8 = Utf8 LChildTest;
#9 = Utf8 val$a
#10 = Utf8 I
#11 = Utf8 <init>
#12 = Utf8 (LChildTest;I)V
#13 = Utf8 Code
#14 = Fieldref #1.#15 // ChildTest$1.this$0:LChildTest;
#15 = NameAndType #7:#8 // this$0:LChildTest;
#16 = Fieldref #1.#17 // ChildTest$1.val$a:I
#17 = NameAndType #9:#10 // val$a:I
#18 = Methodref #3.#19 //java/lang/Object."<init>":()V
#19 = NameAndType #11:#20 // "<init>":()V
#20 = Utf8 ()V
#21 = Utf8 LineNumberTable
#22 = Utf8 LocalVariableTable
#23 = Utf8 this
#24 = Utf8 LChildTest$1;
#25 = Utf8 usefun
#26 = Utf8 (I)V
#27 = Fieldref #28.#30 // java/lang/System.out:Ljava/io/Print
Stream;
#28 = Class #29 // java/lang/System
#29 = Utf8 java/lang/System
#30 = NameAndType #31:#32 // out:Ljava/io/PrintStream;
#31 = Utf8 out
#32 = Utf8 Ljava/io/PrintStream;
#33 = Methodref #34.#36 // java/io/PrintStream.println:(I)V
#34 = Class #35 // java/io/PrintStream
#35 = Utf8 java/io/PrintStream
#36 = NameAndType #37:#26 // println:(I)V
#37 = Utf8 println
#38 = Utf8 b
#39 = Utf8 SourceFile
#40 = Utf8 ChildTest.java
#41 = Utf8 EnclosingMethod
#42 = Class #43 // ChildTest
#43 = Utf8 ChildTest
#44 = NameAndType #45:#26 // fun:(I)V
#45 = Utf8 fun
#46 = Utf8 InnerClasses
#47 = Utf8 interClass
{
final ChildTest this$0;
descriptor:LChildTest;
flags: ACC_FINAL,ACC_SYNTHETIC
private final intval$a;
descriptor: I
flags: ACC_PRIVATE,ACC_FINAL, ACC_SYNTHETIC
ChildTest$1(ChildTest, int);
descriptor:(LChildTest;I)V
flags:
Code:
stack=2,locals=3, args_size=3
0: aload_0
1: aload_1
2:putfield #14 // Field this$0:LChildTest;
5: aload_0
6: iload_2
7:putfield #16 // Field val$a:I
10: aload_0
11:invokespecial #18 //Method java/lang/Object."<init>
":()V
14: return
LineNumberTable:
line 1: 0
line 33: 10
LocalVariableTable:
Start Length Slot Name Signature
0 15 0 this LChildTest$1;
public void usefun(int);
descriptor: (I)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: getstatic #27 // Fieldjava/lang/System.out:Ljav
a/io/PrintStream;
3: aload_0
4: getfield #16 // Field val$a:I
7: invokevirtual #33 // Method java/io/PrintStream.prin
tln:(I)V
10: return
LineNumberTable:
line 35: 0
line 36: 10
LocalVariableTable:
Start Length Slot Name Signature
0 11 0 this LChildTest$1;
0 11 1 b I
}
SourceFile:"ChildTest.java"
EnclosingMethod:#42.#44 // ChildTest.fun
InnerClasses:
#1; //class ChildTest$1
public static #47= #5 of #42;//interClass=class ChildTest$interClass of cl
ass ChildTest
--編譯器會爲匿名內部類命名一個類名,一般是外部類名$1,再看看裏面的實現,編譯器爲我們添加了兩個成員變量:final ChildTest this$0和private final int val$a;並且構造方法也是先把該成員變量用外部類對象的this指針初始化後再去構造父類對象。這樣匿名內部類就可以調用外部類的成員了。看到這裏是不是眼熟,是的,其實匿名內部類的字節碼實現機制和局部內部類是幾乎一模一樣的。唯一不同之處就是對於匿名內部類,編譯器給他一個命名。另外需注意一點,局部內部類和匿名內部類都是有生效區域限制的,只能在外圍方法內使用。跟局部變量的生命週期一樣。