Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/308.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/jquery/87.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 派生类如何调用基类的私有方法?_Java_Oop_Inheritance - Fatal编程技术网

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

Java 派生类如何调用基类的私有方法?,java,oop,inheritance,Java,Oop,Inheritance,因此,此代码的输出是private f()。现在,我想到了一个问题:作为派生类对象的po如何调用作为其基类的PrivateOverride的私有方法?我真的看不出问题所在。该方法在类中被称为“”,这是意料之中的。 此方法根本没有被覆盖,而是被另一个方法覆盖。因为您在PrivateOverride类中定义了主方法。如果将main方法放在派生类中,它将不会编译,因为.f()在那里不可见 PrivateOverride类中的po.f()调用不是多态性,因为PrivateOverride类中的f()是p

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

我真的看不出问题所在。该方法在类中被称为“”,这是意料之中的。
此方法根本没有被覆盖,而是被另一个方法覆盖。

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


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


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

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

困难的部分是理解发生了什么以及为什么

您从私有方法所在的类中调用了该私有方法。因此,特洛伊木马位于城堡内,他可以摆弄私有变量。将特洛伊木马移出城堡,私有方法将不再可见

这个例子可能会澄清问题,考虑这个程序:

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();                         
  }  
}  
此程序打印:

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");
  }
}
如果将自行车内getCost的访问修饰符更改为
public
protected
、或package private(无修饰符),则它会打印以下内容:

200
200
700

Java中的方法根据接收方的静态类型进行调度,在本例中是一个
PrivateOverride
。请不要被以下事实所迷惑:
po
变量通过检查代码,只能在该行保存一个
派生的
实例:搜索可用方法时,只有声明才起作用


顺便说一句,对
f()
的调用甚至没有转换成最终字节码中的虚拟调用,因为当编译器在类
PrivateOverride
中查找可能适用的方法时,它只会找到
对象
方法和
f()
定义,这只有在主()方法是在
PrivateOverride
本身中定义的(请参阅)

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

给出的示例代码设置了一个场景,该场景可能看起来像方法重写,但实际上不是,因此该方法最终会在编译时被绑定。不会违反
private
可见性修饰符,因为调用不会触及任何
派生的
代码

查看字节码(Java代码通过
javac
编译成字节码)很有启发性-

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

700
200
700
主要方法编译为(为简洁起见进行了编辑):

publicstaticmain([Ljava/lang/String;)V
新衍生
重复
调用特殊派生的。()V
阿斯托尔1号
阿洛德1号
调用专用PrivateOverride.f()V
新衍生
重复
调用特殊派生的。()V
阿斯托尔2号
阿洛德2号
INVOKEVIRTUAL派生的.f()V
返回

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

我刚刚检查了上述类编译版本的字节码,得到了invokespecial操作码。该操作码足以说明实际输出明显的原因。invokespecial用于ree必须根据引用的类型而不是对象的类调用实例方法的情况。这三种情况是:

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

2) 私有方法的调用

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

上面的例子在第二个场景中,我们调用了私有方法。因此,方法是基于引用的类型(即PrivateOverride)而不是类的类型(即派生的)来调用的


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

protected可能会帮助您,这是一个这里没人
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