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指针初始化后再去构造父类对象。这样匿名内部类就可以调用外部类的成员了。看到这里是不是眼熟,是的,其实匿名内部类的字节码实现机制和局部内部类是几乎一模一样的。唯一不同之处就是对于匿名内部类,编译器给他一个命名。另外需注意一点,局部内部类和匿名内部类都是有生效区域限制的,只能在外围方法内使用。跟局部变量的生命周期一样。