Java 如何使用默认(包)可见性范围覆盖方法?
我的问题是,我无法理解在以下情况下方法解析是如何工作的:假设我们有两个包,Java 如何使用默认(包)可见性范围覆盖方法?,java,inheritance,Java,Inheritance,我的问题是,我无法理解在以下情况下方法解析是如何工作的:假设我们有两个包,A和B。有两个类,A放在A中,B放在B中 A: package com.eka.IO.a; import com.eka.IO.b.B; public class A { void foo() { System.out.println("parent"); } public static void main(String... args) { B obj = n
A
和B
。有两个类,A
放在A
中,B
放在B
中
A:
package com.eka.IO.a;
import com.eka.IO.b.B;
public class A {
void foo() {
System.out.println("parent");
}
public static void main(String... args) {
B obj = new B();
obj.foo();
}
}
package com.eka.IO.b;
import com.eka.IO.a.A;
public class B extends A {
public void foo() {
System.out.println("child");
}
}
B:
package com.eka.IO.a;
import com.eka.IO.b.B;
public class A {
void foo() {
System.out.println("parent");
}
public static void main(String... args) {
B obj = new B();
obj.foo();
}
}
package com.eka.IO.b;
import com.eka.IO.a.A;
public class B extends A {
public void foo() {
System.out.println("child");
}
}
上面的代码打印“child”,这完全可以。但如果我以以下方式更改主方法:
public static void main(String... args) {
A obj = new B();
obj.foo();
}
代码显示“家长”,我不明白为什么。(obj
具有运行时类型B
,B
具有公共方法foo
)
接下来,我将更改foo对公众的可见性
public class A {
public void foo() {
代码再次打印“child”
据我所知,实例方法是在运行时解析的,使用以下原则:
obj
的运行时类总是B
B
的方法foo
始终是公共的。为什么在第二种情况下JVM调用A
的方法
向上:
答案很好,但有些事情我还不清楚。
a) 编译器检查一个方法是否重写另一个方法。(希望我是对的)。
b) 在A的情况下,obj=新的b()代码>编译器生成以下代码:
INVOKEVIRTUAL com/eka/IO/a/A.foo ()V
b1)如果A的foo是在没有修饰符(包可见性)的情况下声明的,则JVM调用A的方法。
b2)如果A的foo被声明为公共的,那么JVM调用B的方法
不清楚的是,为什么在第二种情况下INVOKEVIRTUAL实际上调用B.foo。它如何知道B覆盖了该方法?您正在经历方法阴影。来自(我的):
某些声明可能在其部分范围内被另一个同名声明所掩盖,在这种情况下,不能使用简单名称来引用已声明的实体
(……)
如果d的作用域包括p,并且d在p处不被任何其他声明遮挡,则在程序中的p点处声明d是可见的
(……)
名为n的方法的声明d会在d在整个d的范围内出现的点处,隐藏封闭范围内名为n的任何其他方法的声明
让我们检查B#foo
是否覆盖A#foo
。发件人:
在类C中声明或由类C继承的实例方法mC重写了在类A中声明的另一个方法mA,如果以下所有条件均为真:
- A是C的一个超类
- C不继承mA
- mC的签名是mA签名的子签名(§8.4.2)
- 以下情况之一是正确的:
- 马是公众人物。(不是你的情况)
- 马是受保护的。(不是你的情况)
- mA与C在同一个包中使用包访问权声明(不是您的情况,因为类在不同的包中),并且C声明mC或mA是C的直接超类的成员
- mA是通过包访问声明的,mC覆盖了C的某个超类中的mA(不是您的情况,因为在C和A之间应该有另一个类让您覆盖mA)
- mA是通过包访问声明的,mC重写C中的方法m'(m'不同于mC和mA),这样m'重写C的某个超类中的mA(不是您的情况,因为在C和a之间应该有另一个类,让您重写mA)
因此,B#foo
不会以任何方式覆盖A#foo
。记住这一点,当您调用obj.foo()
时,将根据编译时指定的类obj
获取foo
。有关这部分的说明,请参阅
如果要避免这种情况,请在子类中使用@Override
注释标记您的方法,以确保您专门重写了所需的方法,而不是隐藏它。如果在注释方法时出现编译器错误,那么您将知道您不是在重写此类方法,而是在隐藏它
因此,不能从与父类不同的包中的子类重写具有默认作用域的方法。在父类中将该方法标记为受保护的,或者相应地重新设计类以避免这种情况。该过程与您描述的略有不同。首先,Java将只使声明类中存在的、在当前作用域中可见的方法可用。这已经在编译时完成了
在运行时
- JVM检查对象的运行时类
- JVM检查对象的运行时类是否重写了声明类的方法
- 如果是这样,那就是调用的方法。否则,将调用声明类的方法
现在,棘手的部分是“它被推翻了吗”
类不能重写对其不可见的方法。它可以用相同的名称和参数声明一个方法,但不认为该方法重写了原始方法。这只是一个新方法,就像其他在B中定义但在a中没有定义的方法一样
如果不是这样,那么您可以在作者认为不应该破坏父类契约的地方破坏父类契约,因此不允许访问父类契约
因此,由于该类没有重写该方法,因此您只能引用该方法,就像您能够引用在B中声明的、不在A中的任何方法一样-仅通过B引用
那么,为什么编译器不阻止您使用父类中已有的方法的名称呢
好吧,如果你收到一个包裹