作者sbrhsieh (十年一梦)
看板java
标题Re: [问题] 多型的修饰词?
时间Sat Dec 28 01:48:21 2013
※ 引述《dream1124 (全新开始)》之铭言:
: 实验过程中倒是意外发现有趣的事,以下这个相似的例子,结果却能执行,
: 命令列会印出
: private foo
: public foo
:
: 请问各位乡民们会怎麽解释这个结果?
:
: public class PolyTest {
: public static void runBase(Base base){
: base.foo();
: }
: public static void runDes(Descendent des){
: des.foo();
: }
: public static void main(String[] args){
: PolyTest poly = new PolyTest();
: runBase(poly.new Descendent());
: runDes(poly.new Descendent());
: }
: public class Base {
: private void foo(){
: System.out.println("private foo");
: }
: }
: public class Descendent extends Base{
: public void foo(){
: System.out.println("public foo");
: }
: }
: }// polyTest end
:
: --
:
※ 发信站: 批踢踢实业坊(ptt.cc)
: ◆ From: 118.167.102.112
: → lovdkkkk:与 inner class 的私有物可以互通 12/27 19:37
: 推 broodstare:谢谢大大!! 我也来想一下 12/27 23:27
: 推 LaPass:inner class基本上无视privite之类的描述,因为都是同个物 12/28 01:10
: → LaPass:件 12/28 01:11
我觉得这一篇是已经离题了...
若就这一篇所提的主题来说,大致上就是如版友的推文所述:
在 Java 语言层面,enclosed class(推及 enclosed object) 视为 enclosing
class 的 member,故 enclosing class 里可以存取 enclosed object 里的任何
member;反之,enclosed class/object 也可以存取 enclosing class/object
的任何 member。
就工程面来说,上述的行为对 JVM 而言不是一种特例,实际上这是靠编译器产生
的辅助码去达成此行为,JVM 未作任何妥协。
不论是 enclosed object 去存取 enclosing object 的 private member 或是
反过来,编译器会视有无此种存取的 statement 来替被存取的部分开後门。
若以你提供的范例码来说,你可以看成你的程式码被编译器改成如下的程式:
*这不是真实可编译的码,只是说明编译器所产生的 bytecode 的等效码
public class PolyTest {
public static void runBase(Base base) {
Base.syntheticInvokeFoo(base);
}
public static void runDes(Descendent des) {
des.foo();
}
public static void main(String[] args) {
PolyTest poly = new PolyTest();
runBase(poly.new Descendent());
runDes(poly.new Descendent());
}
public class Base {
private void foo(){
System.out.println("private foo");
}
static void syntheticInvokeFoo(Base target) {
target.foo();
}
}
public class Descendent extends Base {
public void foo() {
System.out.println("public foo");
}
}
}// polyTest end
可以透过使用 JDK 内建的 javap 工具程式来验证是不是这样子
javap -c -private PolyTest.Base
==========================================
public class PolyTest$Base {
final PolyTest this$0;
public PolyTest$Base(PolyTest);
Code:
0: aload_0
1: aload_1
2: putfield #2 // Field this$0:LPolyTest;
5: aload_0
6: invokespecial #3 // Method java/lang/Object."<init>":()V
9: return
private void foo();
Code:
0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #5 // String private foo
5: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
// 多了这个函式,其所做的事等同 syntheticInvokeFoo
static void access$000(PolyTest$Base);
Code:
0: aload_0
1: invokespecial #1 // Method foo:()V
4: return
}
javap -c -private PolyTest
=====================================================
Compiled from "PolyTest.java"
public class PolyTest {
public PolyTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void runBase(PolyTest$Base);
Code:
0: aload_0
1: invokestatic #2 // Method PolyTest$Base.access$000:(LPolyTest$Base;)V
4: return
public static void runDes(PolyTest$Descendent);
Code:
0: aload_0
1: invokevirtual #3 // Method PolyTest$Descendent.foo:()V
4: return
public static void main(java.lang.String[]);
...[略]
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 1.172.188.13
※ 编辑: sbrhsieh 来自: 1.172.188.13 (12/28 02:49)
1F:推 dream1124:推, 真有研究精神 12/28 10:22
※ 编辑: sbrhsieh 来自: 218.166.238.226 (12/28 15:23)
2F:→ dream1124:最上synthe例子是说明编译後的程式用reflect走後门吗? 12/29 00:24
3F:→ dream1124:不然就凭例子,foo还是private,从static呼叫结果应该一样 12/29 00:26
4F:推 lovdkkkk:看来是 base 被加一个 static 方法 call 自己的 private 12/29 01:55
5F:→ lovdkkkk:然後 call private 的部份会被改成 call public static 12/29 03:34
没错,所谓开後门的部分就是 compiler 发现 PolyTest class 有存取 private foo
method 的部分,於是爲 Base class 注入类似这样的 method:
static(*) void syntheticInvokeFoo(Base target) {
target.foo();
}
*之前我打成 public static method,已修改过。
target 是 Base type,target.foo() 是 non-polymorphic call(因为 foo method
在 Base class 里是 private),导致同一个 package 内的 class 皆可透过
syntheticInvokeFoo 来 invoke Base object 的 private foo method。
PolyTest 内存取的 Base::foo method 的部分皆是透过 syntheticInvokeFoo。
若是存取 private field 也是采类似的做法,唯会分别注入 getter/setter
两个 method 来取代取/存 private field 的 expression。
後门部分没有牵涉到 reflection。
※ 编辑: sbrhsieh 来自: 1.172.232.5 (12/29 16:40)