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