Java-java 继承中的问题

发布于 2017-01-03 06:45:04 字数 823 浏览 1329 评论 2

class Base
{
String name = "base";

public Base() {
print();
tell();
}
public void print()
{
System.out.println("Base print "+name);
}
public void tell()
{
System.out.println("Base tell "+name);
}

}
public class Derived extends Base {
String name = "derived";

public Derived() {
print();
tell();
}
public void print()
{
System.out.println("Derived print "+name);
}
public void tell()
{
System.out.println("Derived tell "+name);
}
public static void main(String[] args)
{
new Derived();
}
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

偏爱自由 2017-08-12 05:04:07

这主要涉及对象构造的过程,具体可以看Java语言定义中的12.5. Creation of New Class Instances

简单来说,子类构造器会先调用父类的构造器,父类的构造器返回之后再给实例变量赋值(在这之前是0、null等默认值),然后继续完成构造器里后续的语句。

而且值得注意的是两点:

如果子类重载了父类的方法,在父类的构造器里实际调用的是重载后的方法,这意味着在子类还没构造完成就可以调用子类的方法。
同名的实例变量在父类和子类里是分开的。

稍微改改代码,不要把两个方法都重载了(没重载print):

class Base
{
String name = "base";

public Base() {
print();
tell();
}
public void print()
{
System.out.println("Base print "+name);
}
public void tell()
{
System.out.println("Base tell "+name);
}

}

public class Derived extends Base {
String name = "derived";

public Derived() {
print();
tell();
}

public void tell()
{
System.out.println("Derived tell "+name);
}
public static void main(String[] args)
{
new Derived();
}
}

结果是这样的

Base print base
Derived tell null
Base print base
Derived tell derived

前两个输出是在父类构造器。Base的构造器会调用他的父类(Object)的构造器,Obejct构造完成之后,Base的实例变量被初始化赋值,然后再调用print()和tell(),而且这个tell()是Derived的tell(),这时Dervied还没构造完成,Derived的name还是null。

瑾兮 2017-04-18 04:49:56

也可以反编译看一下(为了方便阅读, 省略了无关的部分, 并调了顺序):

 // from Derived.class
public static void main(java.lang.String[] args);
0 new Derived [1]
3 invokespecial Derived() [56]
6 return

public Derived();
0 aload_0 [this]
1 invokespecial Base() [10]
4 aload_0 [this]
5 ldc <String "derived"> [12]
7 putfield Derived.name : java.lang.String [14]
10 aload_0 [this]
11 invokevirtual Derived.print() : void [16]
14 aload_0 [this]
15 invokevirtual Derived.tell() : void [19]
18 return

//from Base.class
public Base();
0 aload_0 [this]
1 invokespecial java.lang.Object() [10]
4 aload_0 [this]
5 ldc <String "base"> [12]
7 putfield Base.name : java.lang.String [14]
10 aload_0 [this]
11 invokevirtual Base.print() : void [16]
14 aload_0 [this]
15 invokevirtual Base.tell() : void [19]
18 return

可以看到, main函数里调用了Derived的构造函数,Derived的构造函数里首先会调用Base的构造函数.
注意看! 这里只生成了一个对象实例, 即main里new的那个Derived, 在Derived和Base的构造函数里的'this'都是这同一个Derived对象.

ok, 接下来就是主要的地方了, 在Base构造函数的这里:
11 invokevirtual Base.print() : void [16],
这里似乎调用了Base的print(), 按理应该打印出"Base print base"; BUT,
invokevritual这个指令是按照 当前对象实例(就是10 aload_0 [this] 的这个this)的类来操作的, JVM Spec7这么说: 先在对象实例类里找这个方法, 没有的话递归在父类找.

所以这里实际调用的是Derived.print(), 如下:

 public void print();
0 getstatic java.lang.System.out : java.io.PrintStream [26]
3 new java.lang.StringBuilder [32]
6 dup
7 ldc <String "Derived print "> [34]
9 invokespecial java.lang.StringBuilder(java.lang.String) [36]
12 aload_0 [this]
13 getfield Derived.name : java.lang.String [14]
16 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [39]
19 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [43]
22 invokevirtual java.io.PrintStream.println(java.lang.String) : void [47]
25 return

看第13行,要去拿Derived.name; 而实际上这里Derived.name还没有赋值(从Base构造函数返回后才回去赋值, 看Derived的构造函数).
所以打印出了:
Derived print null

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文