派生类如何调用基类的私有方法?

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() 。 现在,问题出现在我的脑海里:怎么可能是派生类的对象调用私有方法PrivateOverride这是它的基类?

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

在PrivateOverride类中的po.f()调用不是多态,因为PrivateOverride类中的f()private ,所以Derived类中的f()不会被覆盖。

我真的不明白这个问题。 这种方法在课堂上被称为“”,这是非常期待的。 这个方法根本没有被过度使用,而是被另一个隐藏。

Java中的方法根据接收方的静态types(在本例中为PrivateOverride进行分派。 不要被povariables通过检查代码只能容纳Derived实例的事实所迷惑:只有当可用的方法被search时,声明才是重要的。

而且,顺便说一下, f()调用在最终的字节码中甚至没有被转化为虚拟调用,因为当编译器在类PrivateOverride寻找可能适用的方法时,它只能findObject方法和f()定义,这是唯一可见的,因为main()方法是在PrivateOverride本身中定义的(参见JLS 15.12 )

我刚刚通过上面类的编译版本的字节代码,并得到了调用特定的操作码。 这个Opcode足以说明实际产出明显的原因。 Invokespecial用于三种情况,必须根据引用的types而不是对象的类来调用实例方法。 这三种情况是:

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

2)调用私有方法

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

上面的例子在第二个场景中,我们调用了私有方法。 所以这个方法是根据引用的types(即PrivateOverride)而不是类的types(即Derived)来调用的

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

当一个方法被调用时,JVM必须找出要执行的代码片段:有时这是在运行时完成的(例如覆盖方法时)。 有时这是在编译时完成的(例如,当重载方法时)。 一旦JVMparsing了正在执行的代码,你所指的实际实例并不比其他任何参数重要。

给出的示例代码设置了一个可能看起来像是方法重写但不是的scheme,所以这个方法在编译时就会被绑定。 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(); df(); } } 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 

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

它的行为是这样的,因为这就是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 

如果您将Bicycle中的getCost的访问修饰符更改为publicprotected或package private(无修饰符),则会打印此选项:

 700 200 700 

Java中有四个可见性级别,包级别(隐含不使用任何可见性),公共(每个人都可以调用这个),私有(只有我和内部类,看到我的function是全局的,可以调用这个) (我和任何子类可以调用这个)。

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