JDK8编译
-
直接访问内部类的成员变量,读取或者写入都会在被访问的内部类中生成public类型的access方法。
-
访问内部类的私有方法,也会在被访问类的内部生成public类型的access方法。
-
内部的protected、public的方法和成员变量可以直接访问。
public class GameOut {
private int outId;
private String outName;
private InnerA innerA;
private int getOutId() {
return outId;
}
private void setOutId(int outId) {
this.outId = outId;
}
private String getOutName() {
return outName;
}
private void setOutName(String outName) {
this.outName = outName;
}
private String joinStr(){
return outId + outName;
}
private boolean isOk(String str) {
return str.length() > outName.length();
}
public void outTest() {
InnerA innerA = new InnerA();
innerA.setId(1001);
innerA.setName("innerA");
innerA.setAge(10);
printInner(innerA.getId(), innerA.getName(), innerA.getAge());
innerA.id = 2000;
innerA.name = "innerA2";
innerA.age = 20;
printInner(innerA.id, innerA.name, innerA.age);
setOutId(1);
setOutName("gameOut");
innerA.test();
}
private void printInner(int id, String name, int age) {
System.out.println("id = " + id + ", name = " + name + ", age = " + age);
}
private class InnerA {
private int id;
String name;
public int age;
private int getId() {
return id;
}
private void setId(int id) {
this.id = id;
}
String getName() {
return name;
}
void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void test() {
System.out.println("getOutId() = " + getOutId());
System.out.println("getOutName() = " + getOutName());
final String s = joinStr();
System.out.println("s = " + s);
final boolean ok = isOk(s);
System.out.println("ok = " + ok);
}
}
}
生成的GameOut字节码函数列表
outTest函数的字节码
0 new #58 <GameOut$InnerA>
3 dup
4 aload_0
5 aconst_null
6 invokespecial #60 <GameOut$InnerA.<init>>
9 astore_1
10 aload_1
11 sipush 1001
14 invokestatic #63 <GameOut$InnerA.access$1>
17 aload_1
18 ldc #67 <innerA>
20 invokevirtual #68 <GameOut$InnerA.setName>
23 aload_1
24 bipush 10
26 invokevirtual #71 <GameOut$InnerA.setAge>
29 aload_0
30 aload_1
31 invokestatic #74 <GameOut$InnerA.access$2>
34 aload_1
35 invokevirtual #78 <GameOut$InnerA.getName>
38 aload_1
39 invokevirtual #81 <GameOut$InnerA.getAge>
42 invokespecial #84 <GameOut.printInner>
45 aload_1
46 sipush 2000
49 invokestatic #88 <GameOut$InnerA.access$3>
52 aload_1
53 ldc #91 <innerA2>
55 putfield #93 <GameOut$InnerA.name>
58 aload_1
59 bipush 20
61 putfield #96 <GameOut$InnerA.age>
64 aload_0
65 aload_1
66 invokestatic #99 <GameOut$InnerA.access$4>
69 aload_1
70 getfield #93 <GameOut$InnerA.name>
73 aload_1
74 getfield #96 <GameOut$InnerA.age>
77 invokespecial #84 <GameOut.printInner>
80 aload_0
81 iconst_1
82 invokespecial #102 <GameOut.setOutId>
85 aload_0
86 ldc #104 <gameOut>
88 invokespecial #106 <GameOut.setOutName>
91 aload_1
92 invokevirtual #108 <GameOut$InnerA.test>
95 return
GameOut的accesss字节码
// GameOut#access$0
0 aload_0
1 invokespecial #134 <GameOut.getOutId>
4 ireturn
// GameOut#access$1
0 aload_0
1 invokespecial #137 <GameOut.getOutName>
4 areturn
// GameOut#access$2
0 aload_0
1 invokespecial #139 <GameOut.joinStr>
4 areturn
// GameOut#access$3
0 aload_0
1 aload_1
2 invokespecial #142 <GameOut.isOk>
5 ireturn
InnerA的字节码函数列表
InnerA的access方法列表
// access$1
0 aload_0
1 iload_1
2 invokespecial #101 <GameOut$InnerA.setId>
5 return
// access$2
0 aload_0
1 invokespecial #104 <GameOut$InnerA.getId>
4 ireturn
// access$3
0 aload_0
1 iload_1
2 putfield #26 <GameOut$InnerA.id>
5 return
// access$4
0 aload_0
1 getfield #26 <GameOut$InnerA.id>
4 ireturn
InnerA的test方法字节码
0 getstatic #41 <java/lang/System.out>
3 new #47 <java/lang/StringBuilder>
6 dup
7 ldc #49 <getOutId() = >
9 invokespecial #51 <java/lang/StringBuilder.<init>>
12 aload_0
13 getfield #15 <GameOut$InnerA.this$0>
16 invokestatic #53 <GameOut.access$0>
19 invokevirtual #59 <java/lang/StringBuilder.append>
22 invokevirtual #63 <java/lang/StringBuilder.toString>
25 invokevirtual #66 <java/io/PrintStream.println>
28 getstatic #41 <java/lang/System.out>
31 new #47 <java/lang/StringBuilder>
34 dup
35 ldc #71 <getOutName() = >
37 invokespecial #51 <java/lang/StringBuilder.<init>>
40 aload_0
41 getfield #15 <GameOut$InnerA.this$0>
44 invokestatic #73 <GameOut.access$1>
47 invokevirtual #77 <java/lang/StringBuilder.append>
50 invokevirtual #63 <java/lang/StringBuilder.toString>
53 invokevirtual #66 <java/io/PrintStream.println>
56 aload_0
57 getfield #15 <GameOut$InnerA.this$0>
60 invokestatic #80 <GameOut.access$2>
63 astore_1
64 getstatic #41 <java/lang/System.out>
67 new #47 <java/lang/StringBuilder>
70 dup
71 ldc #83 <s = >
73 invokespecial #51 <java/lang/StringBuilder.<init>>
76 aload_1
77 invokevirtual #77 <java/lang/StringBuilder.append>
80 invokevirtual #63 <java/lang/StringBuilder.toString>
83 invokevirtual #66 <java/io/PrintStream.println>
86 aload_0
87 getfield #15 <GameOut$InnerA.this$0>
90 aload_1
91 invokestatic #85 <GameOut.access$3>
94 istore_2
95 getstatic #41 <java/lang/System.out>
98 new #47 <java/lang/StringBuilder>
101 dup
102 ldc #89 <ok = >
104 invokespecial #51 <java/lang/StringBuilder.<init>>
107 iload_2
108 invokevirtual #91 <java/lang/StringBuilder.append>
111 invokevirtual #63 <java/lang/StringBuilder.toString>
114 invokevirtual #66 <java/io/PrintStream.println>
117 return
以上是示例代码。内部类会调用外部类的私有方法,外部类也会调用内部类的私有方法。但是使用java8编译器生成的代码,对于调用的私有方法会生成对应的access$的[static synthetic]方法,实现内部的私有方法访问。这样也会导致热更代码失败。
上面是内部类和外部类,生成的字节码,函数列表。都会包括很多access方法,用于访问对应的私有方法。
这样,内部类访问的私有变量就会转变为access方法的调用。
注意:jdk8下私有类的私有变量即使存在public方法,但如果使用“.变量”的方式访问,依然会生成一个access方法,影响热更。需要直接调用get方法才能正常热更。
总结:JDK8编译
- 内部类的私有方法调用,生成access$开头的[static synthetic]方法,访问级别package。
- 内部类访问私有变量,生成对应的get或者set开头的方法,访问级别package。(访问非私有变量,直接getfield访问)
- 内部的protected、public的方法和成员变量可以直接访问。
JDK11编译的字节码
GameOut的函数列表
没有access方法,GameOut的outTest方法的字节码
0 new #58 <GameOut$InnerA>
3 dup
4 aload_0
5 invokespecial #60 <GameOut$InnerA.<init>>
8 astore_1
9 aload_1
10 sipush 1001
13 invokevirtual #63 <GameOut$InnerA.setId>
16 aload_1
17 ldc #66 <innerA>
19 invokevirtual #67 <GameOut$InnerA.setName>
22 aload_1
23 bipush 10
25 invokevirtual #70 <GameOut$InnerA.setAge>
28 aload_0
29 aload_1
30 invokevirtual #73 <GameOut$InnerA.getId>
33 aload_1
34 invokevirtual #76 <GameOut$InnerA.getName>
37 aload_1
38 invokevirtual #79 <GameOut$InnerA.getAge>
41 invokevirtual #82 <GameOut.printInner>
44 aload_1
45 sipush 2000
48 putfield #86 <GameOut$InnerA.id>
51 aload_1
52 ldc #89 <innerA2>
54 putfield #91 <GameOut$InnerA.name>
57 aload_1
58 bipush 20
60 putfield #94 <GameOut$InnerA.age>
63 aload_0
64 aload_1
65 getfield #86 <GameOut$InnerA.id>
68 aload_1
69 getfield #91 <GameOut$InnerA.name>
72 aload_1
73 getfield #94 <GameOut$InnerA.age>
76 invokevirtual #82 <GameOut.printInner>
79 aload_0
80 iconst_1
81 invokevirtual #97 <GameOut.setOutId>
84 aload_0
85 ldc #99 <gameOut>
87 invokevirtual #101 <GameOut.setOutName>
90 aload_1
91 invokevirtual #103 <GameOut$InnerA.test>
94 return
JDK11下可以直接访问内部类的私有方法和变量。
InnerA的字节码函数列表
也没有access方法,InnerA的test方法字节码
0 getstatic #41 <java/lang/System.out>
3 new #47 <java/lang/StringBuilder>
6 dup
7 ldc #49 <getOutId() = >
9 invokespecial #51 <java/lang/StringBuilder.<init>>
12 aload_0
13 getfield #15 <GameOut$InnerA.this$0>
16 invokevirtual #53 <GameOut.getOutId>
19 invokevirtual #58 <java/lang/StringBuilder.append>
22 invokevirtual #62 <java/lang/StringBuilder.toString>
25 invokevirtual #65 <java/io/PrintStream.println>
28 getstatic #41 <java/lang/System.out>
31 new #47 <java/lang/StringBuilder>
34 dup
35 ldc #70 <getOutName() = >
37 invokespecial #51 <java/lang/StringBuilder.<init>>
40 aload_0
41 getfield #15 <GameOut$InnerA.this$0>
44 invokevirtual #72 <GameOut.getOutName>
47 invokevirtual #75 <java/lang/StringBuilder.append>
50 invokevirtual #62 <java/lang/StringBuilder.toString>
53 invokevirtual #65 <java/io/PrintStream.println>
56 aload_0
57 getfield #15 <GameOut$InnerA.this$0>
60 invokevirtual #78 <GameOut.joinStr>
63 astore_1
64 getstatic #41 <java/lang/System.out>
67 new #47 <java/lang/StringBuilder>
70 dup
71 ldc #81 <s = >
73 invokespecial #51 <java/lang/StringBuilder.<init>>
76 aload_1
77 invokevirtual #75 <java/lang/StringBuilder.append>
80 invokevirtual #62 <java/lang/StringBuilder.toString>
83 invokevirtual #65 <java/io/PrintStream.println>
86 aload_0
87 getfield #15 <GameOut$InnerA.this$0>
90 aload_1
91 invokevirtual #83 <GameOut.isOk>
94 istore_2
95 getstatic #41 <java/lang/System.out>
98 new #47 <java/lang/StringBuilder>
101 dup
102 ldc #87 <ok = >
104 invokespecial #51 <java/lang/StringBuilder.<init>>
107 iload_2
108 invokevirtual #89 <java/lang/StringBuilder.append>
111 invokevirtual #62 <java/lang/StringBuilder.toString>
114 invokevirtual #65 <java/io/PrintStream.println>
117 return
总结:对于java11编译出来的代码是没有access方法的,可以直接访问私有变量和方法。这么来说java11就不会影响热更。