2014-01-09 118 views
30
public class PrivateOverride { 

    private void f() { 
     System.out.println("private f()"); 
    } 
} 

public class Derived extends PrivateOverride { 

    public void f() {       //this method is never run. 
     System.out.println("public f()");  
    } 
} 

public static void main(String[] args) { 

    // instantiate Derived and assign it to 
    // object po of type PrivateOverride. 
    PrivateOverride po = new Derived(); 

    // invoke method f of object po. It 
    // chooses to run the private method of PrivateOveride 
    // instead of Derived 
    po.f();       
    } 
} 

因此,这段代码的输出是private f()。现在,我的脑海里出现了这样一个问题:如何po哪一个是的对象派生类调​​用私有方法PrivateOverride这是它的基类吗?派生类如何调用基类的私有方法?

+2

保护可能会帮助你 –

回答

45

因为你在PrivateOverride类中定义的主要方法。如果将主要方法放在Derived类中,它将不会编译,因为.f()在那里不可见。在PrivateOverride

po.f()调用不是多态,因为在f()PrivateOverrideprivate,所以在f()Derived未重写。

+1

那么,这是一个没有人注意到我猜。谢谢彼得。;-) –

+1

这是真正的原因:)当我在eclipse中看到代码时,我的眼睛调试速度更快。 –

+0

那很好,但* po *包含* Derived *的一个实例。那么说它仍然是有效的并且应该能够调用* PrivateOverride *类的方法,即使它不包含它的实例也是正确的。 –

4

我真的不明白这个问题。这个方法在课堂中被称为“”,这是非常期待的。 这种方法根本没有被覆盖,而是被另一个覆盖。

0

在Java中,包四级可视性水平(不使用任何可见性暗示),公共(每个人都可以召本),私人(只有我,而且看到我作为全球内部类,可以调用这个)和受保护的(我和任何子类可以调用它)。

你可以让你的第二类成为一个内部类,然后你不重写,而只是简单地调用该函数就好像它是全局的(就内部类而言),但是你不能真正创建实例无论你在哪里,因为它是一个只能由所有者使用的类(所以如果你想在其他地方使用它,那么所有者需要成为一个工厂并返回它,就像它是基类一样),或者你可以使该方法受保护,然后扩展类可以调用该方法。

1

它的行为这种方式,因为这是JVM是如何定义在这些情况下的行为。

困难的部分是了解正在发生什么,为什么。

你从它是私有的类中调用的私有方法。所以特洛伊木马在城堡内,他可以摆弄私人变数。将特洛伊木马带出城堡,私有方法不再可见。

这个例子可以明确的事情了,考虑一下这个程序:

public class Bicycle { 
    private void getCost() { 
     System.out.println("200"); 
    } 

    public static void main(String[] args) { 
     Bicycle ACME_bike = new ACME_bike(); 
     ACME_bike.getCost(); 

     Bicycle mybike = new Bicycle(); 
     mybike.getCost(); 

     ACME_bike acme_bike = new ACME_bike(); 
     acme_bike.getCost(); 

     //ACME_bike foobar = new Bicycle(); //Syntax error: Type mismatch: 
              //cannot convert from 
              //Bicycle to ACME_bike 
    } 
} 

class ACME_bike extends Bicycle { 
    public void getCost(){ 
     System.out.println("700"); 
    } 
} 

这个程序打印:

200 
200 
700 

如果更改getCost的访问修饰符自行车内publicprotected,或包私人(无修改),然后打印此:

700 
200 
700 
2

Java中的方法根据接收方的静态类型进行调度,在本例中为PrivateOverride。不要因变量通过检查代码而只能在该行保存Derived实例这一事实让人困惑:只有声明在搜索可用方法时很重要。

而且,顺便说一句,到f()电话甚至没有翻译成最终的字节码虚拟呼叫,因为当编译器会在类PrivateOverride可能适用的方法,只发现Object方法和f()定义,因为main()方法在PrivateOverride本身中定义(请参阅JLS 15.12

2

当调用方法时,JVM必须找出要执行的代码片段:有时这是在运行时完成的例如当重写方法时);有时这是在编译时完成的(例如,当重载方法时)。一旦JVM解析了它正在执行的代码位,你所指的实际实例并不比其他参数更重要。

给出的示例代码设置了一个可能看起来像方法重写但不是的方案,因此该方法最终在编译时得到绑定。不会违反private可见性修改器,因为调用不会触及Derived的任何代码。

望着字节码(在Java代码通过javac编译)是有益的 -

说我们稍微修改原始代码:

public class PrivateOverride { 
private void f() { 
    System.out.println("private f()"); 
} 

public static void main(String[] args) { 
    PrivateOverride po = new Derived(); 
    po.f(); 
    Derived d = new Derived(); 
    d.f(); 
} 
} 

class Derived extends PrivateOverride { 
public void f() { 
    System.out.println("public f()"); 
} 
} 

主要方法编译成(编辑为简洁):

public static main([Ljava/lang/String;)V 
    NEW Derived 
    DUP 
    INVOKESPECIAL Derived.<init>()V 
    ASTORE 1 
    ALOAD 1 
    INVOKESPECIAL PrivateOverride.f()V 
    NEW Derived 
    DUP 
    INVOKESPECIAL Derived.<init>()V 
    ASTORE 2 
    ALOAD 2 
    INVOKEVIRTUAL Derived.f()V 
    RETURN 

请注意,在每种情况下,该方法都是在编译时类型上调用的。另请注意,f()的第二次调用使用INVOKEVIRTUAL指令。这是告诉JVM检查运行时类型并根据它决定要调用什么的。

2

我刚刚浏览了上面类的编译版本的字节码,并得到了invokespecial Opcode。这个操作码足以说明实际输出显而易见的原因。 Invokespecial用于三种情况,其中必须根据引用的类型调用实例方法,而不是基于对象的类。所述三种情况是:

1)实例初始化的调用()方法

2)的私有方法

调用

3)的使用super关键字方法的调用

上面的例子位于内第二种情况是我们调用了私有方法。因此,该方法根据引用类型(即PrivateOverride)而不是类的类型进行调用,即Derived

所以现在问题出现了为什么invokespecial?我们还有其他操作码像invokevirtual,它是根据类型而不是引用类型为方法调用的。因此,让我们来讨论为什么invokespecial Opcode用于私有方法。但是我们应该知道invokevirtual和invokespecial的区别。 Invokespecial与invokevirtual的不同之处主要在于invokespecial基于引用的类型而不是对象的类来选择方法。换句话说,它执行静态绑定而不是动态绑定。在使用invokespecial的三种情况下,动态绑定都不会产生所需的结果。

相关问题