黑马程序员技术交流社区
标题: 这道题我想了很久都想不明白,谁能给一个详细的解释? [打印本页]
作者: 李敬卫 时间: 2013-1-21 19:43
标题: 这道题我想了很久都想不明白,谁能给一个详细的解释?
本帖最后由 张向辉 于 2013-1-23 09:34 编辑
class X {
X() {
System.out.print("X");
}
Y b = new Y();
}
class Y {
Y() {
System.out.print("Y");
}
}
public class Z extends X {
Y y = new Y();
Z() {
System.out.print("Z");
}
public static void main(String[] args) {
new Z();
}
}
我的结果是XYYZ可是错了,但是我想了很久就是想不明白为什么结果是YXYZ?现在我才发现继承还是没学明白啊,请彻底理解了继承的高手来详细解答一下这道题?要详细 要详细
作者: 唐晓 时间: 2013-1-21 20:13
本帖最后由 唐晓 于 2013-1-21 20:16 编辑
如果有继承,父类的信息就优先加载到了子类。在子类还没new的时候就已经加载了。所以Z方法里会优先加载X类。
X类的话就涉及到了类的加载顺序了。类首先会加载静态成员/代码块,然后再加载非静态成员/代码块,在接下来才加载调用构造方法。
所以X类会先调用new Y方法。
作者: 陈丽莉 时间: 2013-1-21 20:23
本帖最后由 陈丽莉 于 2013-1-21 20:29 编辑
要明确的几个知识点:
1、子类中所有构造函数默认都会访问父类中空参数构造函数。
2、初始化子类对象时,会先执行父类构造对象,再执行子类构造函数。
3、当父类既有创建对象,又有构造函数时,先执行创建对象操作。
下面是楼主的代码,格式稍微整理了一下,加了注释:
class X
{
X()
{
System.out.print("X"); //创建b对象并初始化Y的构造函数后,初始化父类构造函数,所以在输出Y后,输出X
}
Y b = new Y(); //加载时,创建对象的操作要先于构造函数执行
}
class Y
{
Y()
{
System.out.print("Y"); //创建对象后就会初始化这个类的构造函数,所以先输出Y
}
}
public class Z extends X
{
Y y = new Y();
/* 输出Y和X后,子类中创建对象的操作要先于子类构造函数,所以创建了新的Y
类对象,由于不是同一个b对象,Y类的构造函数又执行了一次,所以在输出Y和X
之后,继而又输出了Y。 */
Z()
{
//super(); 默认隐式的访问父类空参数构造函数
System.out.print("Z"); // 完成了子类中创建对象的操作后,初始化子类构造函数,最后输出了Z
}
public static void main(String[] args)
{
new Z();
/*程序的入口,若没有这个创建Z类匿名对象的操作,则父类
不加载构造函数,什么也不会输出。创建本类对象的操作,先加载父类X */
}
}
作者: 黄金龙 时间: 2013-1-21 20:28
- class X {
- X() {
- System.out.print("X");//这里第5步执行
- }
- Y b = new Y();//这里第6步执行
- }
- class Y {
- Y() {
- System.out.print("Y");//这里第3步执行>>>>>//这里第7步执行
- }
- }
- public class Z extends X {
- Y y = new Y();//这里第2步执行
- Z() {
- //super();这里隐藏了 //这里第4步执行
- System.out.print("Z");//这里第8步执行
- }
- public static void main(String[] args) {
- new Z();//这里第1步执行
- }
- }
- //所以就是YXYZ
复制代码
作者: 黑马刘杰 时间: 2013-1-21 20:30
你看这里的解释可不可以http://bbs.itheima.com/forum.php ... mp;page=1#pid212264
作者: 小学生0987 时间: 2013-1-21 20:36
这主要是运行顺序的问题,java虚拟机在实例化子类时,先加载父类的成员变量,执行父类的构造方法,接着加载子类的成员变量,运行子类的构造方法,所以结果是YXYZ
作者: 陈丽莉 时间: 2013-1-21 20:37
看了楼上的,觉得用数字标志确实很清晰,那我也标记一下,顺序有点出入:
class X {
X() {
System.out.print("X"); //--------这里第四步
}
Y b = new Y(); //--------这里第二步
}
class Y {
Y() {
System.out.print("Y"); //--------这里第三步 第六步
}
}
public class Z extends X {
Y y = new Y(); //--------这里第五步
Z() {
System.out.print("Z"); //--------这里第七步
}
public static void main(String[] args) {
new Z(); //--------这里第一步
}
}
注:如果将X中的Y b = new Y(); 注释掉,结果为XYZ,可见并不是楼上说的先执行了Z类中的Y y=new Y();语句。
作者: 王晓斌 时间: 2013-1-21 20:37
一,说下对象的初始化过程
1,new(Z)用到了z.class,因此会先找到z.class文件并加载到内存中~
2,找到该类中的静态代码块,如果有的话~给z.class类进行初始化
3,在堆内存中开辟空间,分配内存地址
4,在堆内存中建立对象的特有属性,并进行默认的初始化
5,对构造代码块初始化
6,构造函数初始化
如果有继承,那么默认先对父类进行初始化
这样一来,此题也就清楚了~~~
对于Y b=new(Y)b可以算是X的特有属性,当然先于构造函数执行了~~~
作者: 黑马-小龙 时间: 2013-1-21 20:55
结果yxyz:原因是构造函数随着对象的加载而加载,当该类继承了父类的时候,子类的构造函数在加载的过程中,虚拟机会首先去父类找父类的的构造函数,在执行子类的构造函数
作者: 李敬卫 时间: 2013-1-21 21:11
感谢大家,特别是2楼的这句话:创建对象的操作要先于类构造函数。我想知道,这句话有为什么没?如果有还请给解释一下,让我学的明白点?
作者: 刘军亭 时间: 2013-1-21 21:50
class X {
X() {
//super();//默认的super()调用,这里是第3步,因为这里默认也有super(),因为所有的类都是Object的子类,所以去调用父类的空参构造函数,但是Object类的默认构造函数没有输出。所以一般不会察觉到他执行了。
System.out.print("X");//这里第7步执行
}
Y b = new Y();//第4步,执行。第一个Y是这里调用Y 的构造函数输出的
}
class Y {
Y() {
//super();//第5步同上 // 第9步,执行第八步时候同样调用Y构造函数
System.out.print("Y");//第6步,执行 // 第10步,第八步执行再次调用Y的构造函数
}
}
public class Z extends X {
Y y = new Y();//第8步,执行 先对y变量默认初始化为Null,然后显示初始化, 此语句会调用Y的构造函数,在Y的构造函数执行完毕后会执行第11步
Z() {
//super(); //这里第2步执行,去给父类初始化。父类初始化完毕然后去给此类成员变量默认初始化,如:Y y = 默认为null;int类型默认初始化0,如果是类对象(类类型)初始化null;再后给成员变量进行显示初始化,如:Y y = new Y();
//执行完第七步时候super()执行完毕,父类初始化完毕。
System.out.print("Z");//第11步,最后执行构造初始化
}
public static void main(String[] args) {
new Z();//第1步,执行创建Z的对象,调用Z的构造函数。
}
}
//所以就是YXYZ
/*不知道你能不能明白,我觉得自己写的挺乱的,类的初始化过程用语言描述就是:
先调用本类的构造函数,但是构造函数默认第一行都由super();关键字去调用父类的构造函数,给父类进行初始化。然后才是成员变量的默认初始化。再后就是成员变量的显示初始化,最后才是构造初始化。
如:*/
class A {
int x = 1; //3.默认初始化为0
int y; //3.默认初始化为0
String s; //3.默认初始化为null
//4.成员变量都默认初始化完毕后后,才会进行显示初始化,x = 1; y=0;因为y没有显示初始化的值。s=null;因为s也没有显示的赋值,都显示初始化完毕后才会执行构造函数的初始化。
public A(){
//super();//2.因为构造函数都有默认的super();会先给父类进行初始化。父类的初始化过程和子类相同。初始化完父类,会给本类的成员变量进行初始化,显示默认初始化,再是显示初始化。
x = 5; //5.构造初始化
y =55; //5.构造初始化
s = "str"; //5.构造初始化
}//至此初始化完毕
public static void main(String[] args){
A a = new A();//1.在new A对象的时候,会先调用A的构造函数
}
}
作者: 唐长智 时间: 2013-1-22 00:24
本帖最后由 唐长智 于 2013-1-22 00:27 编辑
唐晓 发表于 2013-1-21 20:13
如果有继承,父类的信息就优先加载到了子类。在子类还没new的时候就已经加载了。所以Z方法里会优先加载X类 ...
2楼的亲解答的简单明了。子类初始化。
1.首先加载父类的静态成员/代码块。
2.加载子类的静态成员/代码块。
3.调用父类的非静态成员/代码块。
4.调用父类的构造函数。
5.调用子类的非静态成员/代码块。
6.调用子类的构造函数。
附上代码。如有错误,请大家指正。
结果是:X的静态代码块
Z的静态代码块
Y
X的构造代码块
X
Z的构造代码块
Y
Z- class X{
- X(){
- System.out.println("X");
- }
-
- Y b = new Y();
- {
- System.out.println("X的构造代码块");
- }
-
- static{
- System.out.println("X的静态代码块");
- }
- }
- class Y{
- Y(){
- System.out.println("Y");
- }
- }
- public class Z extends X{
- static{
- System.out.println("Z的静态代码块");
- }
-
- {
- System.out.println("Z的构造代码块");
- }
-
- Z(){
- System.out.println("Z");
- }
-
- Y y = new Y();
-
- public static void main(String[] args){
- new Z();
- }
- }
复制代码
作者: 陈丽莉 时间: 2013-1-22 12:07
李敬卫 发表于 2013-1-21 21:11
感谢大家,特别是2楼的这句话:创建对象的操作要先于类构造函数。我想知道,这句话有为什么没?如果有还请 ...
个人理解是,构造函数有可能会用到类中创建的对象的,所以加载的时候先加载对象。八楼的第4和第5点是说这个顺序的,那个答案应该是挺官方的吧~
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) |
黑马程序员IT技术论坛 X3.2 |