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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21016776/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 05:58:40  来源:igfitidea点击:

How can a derived class invoke private method of base class?

javaoopinheritance

提问by Saurab Parakh

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();                         
  }  
}  

So, the output of this code is private f(). Now, the question arises to my mind: how can powhich is an object of DerivedClass call a private method of PrivateOverridewhich is its base class?

所以,这段代码的输出是private f()。现在,我想到了一个问题:派生类的对象po如何调用其基类PrivateOverride的私有方法?

采纳答案by Péter Szakszon

Because you defined the main method in PrivateOverrideclass. If you put the main method in Derived class, it would not compile, because .f()would not be visible there.

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

po.f() call in PrivateOverrideclass is not a polymorphism, because the f()in PrivateOverrideclass is private, so f()in Derivedclass is not overriden.

在po.f()调用PrivateOverride类不是多态的,因为f()PrivateOverrideprivate,所以f()Derived类没有覆盖。

回答by Eugene

I do not really see the problem. That method is called ""within"" the class, this is pretty much expected. This method is not overidden at all, instead it is shadowed by another one.

我真的没有看到问题。该方法被称为类的“内部”,这在意料之中。这个方法根本没有被覆盖,而是被另一个方法遮蔽了。

回答by Mike 'Pomax' Kamermans

There are four visibility levels in Java, package-level (implied by not using any visibility), public (everyone can call this), private (only I, and internal classes that see my functions as global, can call this), and protected (I, and any subclass, can call this).

Java 中有四个可见性级别,包级(暗示不使用任何可见性)、公共(每个人都可以调用它)、私有(只有 I 和将我的函数视为全局函数的内部类可以调用它)和受保护(我和任何子类都可以调用它)。

You can either make your second class an internal class and then you don't override but simply call the function as if it's global (as far as the internal class is concerned), but then you can't really create instances wherever you want because it's a class that can be used exclusively by the owner (so if you want to use it elsewhere, the owner would need to be a factory and return it as if it's the base class) or you can make the method protected, and then the extending class can call the method.

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

回答by Eric Leschinski

It behaves this way because that is how the JVM was defined to behave in those cases.

它的行为方式是这样的,因为这就是 JVM 在这些情况下的行为方式。

The hard part is understanding what is going on and why.

困难的部分是了解正在发生的事情以及原因。

You invoked the private method from within the class in which it was private. So the trojan horse is inside the castle, he can fiddle with the private variables. Take the trojan horse out of the castle and the private method is no longer visible.

您从私有的类中调用了私有方法。所以特洛伊木马就在城堡里面,他可以摆弄私有变量。将木马带出城堡,私有方法不再可见。

This example might clear things up, Consider this program:

这个例子可能会澄清事情,考虑这个程序:

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");
  }
}

This program prints:

该程序打印:

200
200
700

If you change the access modifier of getCost within Bicycle to public, protected, or package private(no modifier), Then it prints this:

如果您在自行车更改getCost的访问修饰符来publicprotected或包专用(无修改),然后打印此:

700
200
700

回答by Raffaele

Methods in Java are dispatched depending on the static type of the receiver, which in this case is a PrivateOverride. Do not be confused by the fact that the povariable, by examining the code, can only hold a Derivedinstance at that line: only the declaration matters when available methods are searched.

Java 中的方法是根据接收器的静态类型分派的,在本例中为PrivateOverride. 不要被这样一个事实所迷惑:po通过检查代码,变量只能Derived在该行保存一个实例:只有在搜索可用方法时声明才重要。

And, by the way, the call to f()is not even translated into a virtual call in the final bytecode, because when the compiler looks for the potentially applicable methods in the class PrivateOverride, it only finds Objectmethods and the f()definition, which is only visible because the main() method is defined in PrivateOverrideitself (see JLS 15.12)

而且,顺便说一句,f()在最终字节码中,对 的调用甚至没有转换为虚拟调用,因为当编译器在类中寻找可能适用的方法时PrivateOverride,它只找到Object方法和f()定义,这仅是可见的,因为main() 方法是PrivateOverride自己定义的(参见JLS 15.12

回答by CurtainDog

When a method is invoked the JVM has to figure out what piece of code to execute: sometimes this is done at runtime (e.g. when overriding methods); sometimes this is done at compile time (e.g. when overloading methods). Once the JVM resolves what bit of code it is executing the actual instance that you are referring to isn't really any more significant than any other parameter.

当一个方法被调用时,JVM 必须弄清楚要执行哪一段代码:有时这是在运行时完成的(例如,当覆盖方法时);有时这是在编译时完成的(例如在重载方法时)。一旦 JVM 解析出它正在执行的代码位,您所指的实际实例实际上并不比任何其他参数更重要。

The example code given sets up a scenario that may look like method overriding but isn't, so the method ends up getting bound at compile time. The privatevisibility modifier is not violated because the invocation doesn't touch any of Derived's code.

给出的示例代码设置了一个可能看起来像方法覆盖但实际上并非如此的场景,因此该方法最终在编译时被绑定。该private可视性修饰符是不是侵犯,因为调用不接触任何Derived的代码。

Looking at the bytecode (which the Java code is compiled to via javac) is instructive -

查看字节码(Java 代码编译为 via javac)很有启发性 -

Say we slightly modify the original code to:

假设我们将原始代码稍微修改为:

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()");
}
}

The main method compiles to (edited for brevity):

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

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

Notice that in each case the method is invoked on the compile time type. Notice also that the second call of f() uses the INVOKEVIRTUAL instruction. This is what tells the JVM to check the runtime type and decide what to call based on that.

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

回答by evaipar

I just went through the byte code of the compiled version of the above class and got the invokespecial Opcode. This Opcode was enough to tell the reason why the actual output is obvious. Invokespecial is used in three situations in which an instance method must be invoked based on the type of the reference, not on the class of the object. The three situations are:

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

1)invocation of instance initialization () methods

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

2)invocation of private methods

2)调用私有方法

3)invocation of methods using the super keyword

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

Above example lies within the second scenario where we have invocation of private methods. So the method got invoked based on the the type of reference i.e PrivateOverride rather than type of class i.e Derived

上面的示例位于我们调用私有方法的第二个场景中。因此,该方法是根据引用类型(即 PrivateOverride)而不是类的类型(即 Derived)调用的

So now the question arises why invokespecial? We have other Opcode like invokevirtual which gets invoked for method on the basis of classtype rather than reference type. So lets discuss why invokespecial Opcode is used for private methods. But we should know the difference between invokevirtual and invokespecial. Invokespecial differs from invokevirtual primarily in that invokespecial selects a method based on the type of the reference rather than the class of the object. In other words, it does static binding instead of dynamic binding. In each of the three situations where invokespecial is used, dynamic binding wouldn't yield the desired result.

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