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

时间:2023-01-15 21:00:08
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 po which is an object of Derived Class call a private method of PrivateOverride which is its base class?

因此,此代码的输出是私有f()。现在,问题出现在我的脑海中:作为Derived Class对象的po如何调用PrivateOverride的私有方法,它是它的基类?

7 个解决方案

#1


45  

Because you defined the main method in PrivateOverride class. 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 PrivateOverride class is not a polymorphism, because the f() in PrivateOverride class is private, so f() in Derived class is not overriden.

PrivateOverride类中的po.f()调用不是多态,因为PrivateOverride类中的f()是私有的,因此Derived类中的f()不会被覆盖。

#2


4  

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.

我真的没有看到这个问题。该方法在“”类中称为“”,这是非常期待的。这个方法根本不会被覆盖,而是被另一个方法遮蔽。

#3


3  

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 po variable, by examining the code, can only hold a Derived instance 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 Object methods and the f() definition, which is only visible because the main() method is defined in PrivateOverride itself (see JLS 15.12)

顺便说一下,对f()的调用甚至没有转换成最终字节码中的虚拟调用,因为当编译器在类PrivateOverride中查找可能适用的方法时,它只能找到Object方法和f()定义,只能看到,因为main()方法是在PrivateOverride本身中定义的(参见JLS 15.12)

#4


2  

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)调用实例的initialization()方法

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.

所以现在问题出现了为什么要特别注意?我们有其他Opcode,比如invokevirtual,它基于classtype而不是引用类型被调用。因此,让我们讨论为什么invokespecial Opcode用于私有方法。但我们应该知道invokevirtual和invokespecial之间的区别。 Invokespecial与invokevirtual的不同之处主要在于invokespecial选择基于引用类型而不是对象类的方法。换句话说,它执行静态绑定而不是动态绑定。在使用invokespecial的三种情况中的每一种情况下,动态绑定都不会产生期望的结果。

#5


2  

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 private visibility modifier is not violated because the invocation doesn't touch any of Derived's code.

给出的示例代码设置了一个可能看起来像方法覆盖但不是的方案,因此该方法最终会在编译时受到限制。不会违反私有可见性修饰符,因为调用不会触及Derived的任何代码。

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

查看字节码(通过javac编译Java代码)是有益的 -

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检查运行时类型并根据它来决定调用什么的原因。

#6


1  

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 * horse is inside the castle, he can fiddle with the private variables. Take the * 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:

如果将Bicycle中的getCost的访问修饰符更改为public,protected或package private(无修饰符),则打印出:

700
200
700

#7


0  

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中有四个可见性级别,包级别(不使用任何可见性隐含),public(每个人都可以调用此),private(只有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.

你可以让你的第二个类成为一个内部类,然后你不要覆盖,只需调用该函数就好像它是全局的(就内部类而言),但是你无法在任何地方创建实例,因为它是一个可以由所有者独占使用的类(所以如果你想在别处使用它,那么所有者需要是一个工厂并将其作为基类返回)或者你可以使方法受到保护,然后扩展类可以调用该方法。

#1


45  

Because you defined the main method in PrivateOverride class. 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 PrivateOverride class is not a polymorphism, because the f() in PrivateOverride class is private, so f() in Derived class is not overriden.

PrivateOverride类中的po.f()调用不是多态,因为PrivateOverride类中的f()是私有的,因此Derived类中的f()不会被覆盖。

#2


4  

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.

我真的没有看到这个问题。该方法在“”类中称为“”,这是非常期待的。这个方法根本不会被覆盖,而是被另一个方法遮蔽。

#3


3  

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 po variable, by examining the code, can only hold a Derived instance 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 Object methods and the f() definition, which is only visible because the main() method is defined in PrivateOverride itself (see JLS 15.12)

顺便说一下,对f()的调用甚至没有转换成最终字节码中的虚拟调用,因为当编译器在类PrivateOverride中查找可能适用的方法时,它只能找到Object方法和f()定义,只能看到,因为main()方法是在PrivateOverride本身中定义的(参见JLS 15.12)

#4


2  

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)调用实例的initialization()方法

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.

所以现在问题出现了为什么要特别注意?我们有其他Opcode,比如invokevirtual,它基于classtype而不是引用类型被调用。因此,让我们讨论为什么invokespecial Opcode用于私有方法。但我们应该知道invokevirtual和invokespecial之间的区别。 Invokespecial与invokevirtual的不同之处主要在于invokespecial选择基于引用类型而不是对象类的方法。换句话说,它执行静态绑定而不是动态绑定。在使用invokespecial的三种情况中的每一种情况下,动态绑定都不会产生期望的结果。

#5


2  

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 private visibility modifier is not violated because the invocation doesn't touch any of Derived's code.

给出的示例代码设置了一个可能看起来像方法覆盖但不是的方案,因此该方法最终会在编译时受到限制。不会违反私有可见性修饰符,因为调用不会触及Derived的任何代码。

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

查看字节码(通过javac编译Java代码)是有益的 -

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检查运行时类型并根据它来决定调用什么的原因。

#6


1  

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 * horse is inside the castle, he can fiddle with the private variables. Take the * 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:

如果将Bicycle中的getCost的访问修饰符更改为public,protected或package private(无修饰符),则打印出:

700
200
700

#7


0  

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中有四个可见性级别,包级别(不使用任何可见性隐含),public(每个人都可以调用此),private(只有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.

你可以让你的第二个类成为一个内部类,然后你不要覆盖,只需调用该函数就好像它是全局的(就内部类而言),但是你无法在任何地方创建实例,因为它是一个可以由所有者独占使用的类(所以如果你想在别处使用它,那么所有者需要是一个工厂并将其作为基类返回)或者你可以使方法受到保护,然后扩展类可以调用该方法。