Java 继承的属性/方法/静态方法行为

Java 继承的属性/方法/静态方法行为,java,Java,让我们设置一个简单的场景: A类{ String bar=“附件A”; 公共图书馆{ System.out.println(“foo A”); } 公共静态无效sfoo(){ 系统输出打印号(“sfoo A”); } } B类扩展了A类{ 字符串栏=“附件B”; 公共图书馆{ System.out.println(“foo B”); } 公共静态无效sfoo(){ 系统输出打印号(“sfoo B”); } } 班长{ 公共静态void main(字符串[]args){ A b=新b(); b、

让我们设置一个简单的场景:

A类{
String bar=“附件A”;
公共图书馆{
System.out.println(“foo A”);
}
公共静态无效sfoo(){
系统输出打印号(“sfoo A”);
}
}
B类扩展了A类{
字符串栏=“附件B”;
公共图书馆{
System.out.println(“foo B”);
}
公共静态无效sfoo(){
系统输出打印号(“sfoo B”);
}
}
班长{
公共静态void main(字符串[]args){
A b=新b();
b、 foo();
b、 sfoo();
系统输出打印LN(b.bar);
}
}
哪些产出:

foo B
sfoo A
att A
我认为,
sfoo A
是由查看对象类型的静态方法引起的,而不是对象本身-您可以自由地纠正/纠正这一点


然而,我想知道为什么
atta
会被打印出来。直观地说,属性与对象本身一起存储,其行为类似于实例方法。为什么不呢?您能否在
foo
sfoo
的上下文中解释一下(即比较这三种情况)?

这确实是一种字段隐藏的情况。 B类现在有两个
bar
成员。一个在
A
子对象中,一个在
B
部分中。 当访问类型静态定义为
A
的元素上的字段时,您指的是基类中定义的字段

您可以将其视为等效于以这种方式访问bar
((A)b).bar)

关于数据与函数实例成员的上下文解释:

访问实例方法的工作方式与访问实例成员略有不同。
foo
是一个非最终函数,因此是虚拟函数。运行时通过跟踪从实际对象到虚拟函数表的指针来选择要调用的实际方法(如果我们忽略优化),并且该指针对于
a
类型的对象和
B
类型的对象是不同的,因此对象的实际运行时类型决定了实际调用的函数

对于访问变量,编译器只需发出一条指令,从对象开始的已知偏移量读取地址。此偏移量严格由对象的编译时类型决定,因此对象的声明类型将决定读取哪个bar成员


顺便说一句,在访问静态函数时,编译器忽略对象,并使用类类型静态调用函数,这是正确的。

关于
atta
的提示:。。。。和do
System.out.println(((B)B).bar)
您将看到一条建议:不要通过实例变量调用
静态
方法。这至少是一个编译器警告,类似的语言(如C#)将其视为编译器错误,因为
静态
方法属于类而不是实例/对象。另外,正如您所注意到的,如果您使用继承,它们的行为与您预期的不一样。@Powerlord谢谢,但别担心,我无意这样做。明天是我的Java考试,我听说这是我最喜欢的问题之一。谢谢你的详细回答。从您的回答来看,是否返回
B.bar
a.bar
似乎是一个相当随意的选择-我们都知道,根据编译时间,声明的类型是
a
,实际类型是
B
,并且可以相应地选择
bar
中的一个。为什么会这样?编译时不知道真正的类型。设想以下情况:
如果userInput.equals(“Yes”)b=newa();else b=新的b()这就是为什么实例函数在运行时使用v-table机制来获得正确的代码。根据语言设计,此机制不用于数据成员。