class Animal{
String color="red";
Animal(){
print();
color="black";
}
void print(){
System.out.println("animal color is "+color);
}
}
class Cat extends Animal{
String color="yellow";
Cat(){
print();
super.print();
}
void print(){
System.out.println("cat color is "+color);
}
public static void main(String[] args){
new Cat();
}
}
结果是:
cat color is null
cat color is yellow
animal color is black
疑问:
1.当子类构造函数去调用父类构造函数时,如果父类构造函数中调用的成员函数被子类覆盖过,那么就用子类?
2.为什么是null? 显示初始化不是优先于构造函数么?当构造函数启动时,成员变量不是已经被显示初始化了么?
12 个解决方案
#1
1.是的
2.new Cat() 先调用父类构造方法 调print()方法 所以null
2.new Cat() 先调用父类构造方法 调print()方法 所以null
#2
1.根据打印出来的结果,可以肯定是这样的。
2.之所以是null是因为当父类调用子类的print方法时,子类的color变量还没有执行初始化。显示初始化时先于构造函数没错,但是指的是当前的类的变量的初始化,楼主给出的例子中,JVM首先初始化了父类的color变量,然后调用了子类的print方法,但是此时还没有初始化子类的color变量。而子类的方法调用的是子类的color变量,所以打印出来的就是null了。当构造函数启动时,成员变量是被加载,而不是初始化,也就是说这个指针存在,但是这个指针不指向任何对象。
#3
++正解
#4
LZ好好理解一下以下的例子就知道了
new 子类()的执行顺序是
1 父类初始化块
2 父类构造方法
3 子类初始化块
4 子类构造方法
LZ再好好体会一下吧
class A {
{System.out.println("执行顺序1");} //这里叫做初始化处理块
public A() {
System.out.println("执行顺序2");
}
}
class B extends A {
{System.out.println("执行顺序3");} //初始化处理块,String color = "yellow"; 相当于
//String color; //声明变量,相当于给变量分配内存空间并初始化为缺省值
//{color = "yellow"} //在初始化块给变量赋值
//所以,从执行顺序来看,LZ应该知道为什么是null了吧
public B() {
System.out.println("执行顺序4");
}
}
public class C {
public static void main(Srting[] args) {
new B();
}
}
new 子类()的执行顺序是
1 父类初始化块
2 父类构造方法
3 子类初始化块
4 子类构造方法
LZ再好好体会一下吧
#5
楼上说的和楼主说的又是两个问题了。初始化块儿和变量的初始化是两个概念,楼主可以自己写一些例子用调试模式看看。
#6
1.至于为什么会调用子类的print,那是因为你创建的是子类这个对象,在类中直接调用print()相当于this.print(),然而当前这个对象是Cat所以就调用了子类的print方法。如果你new的父类,那么自然调用的就是父类的print方法咯。
2.为什么会是null?这个很明显,对象是先初始化然后复制,虽然String color在构造函数之前,但是他的复制却是要等待对象初始化完成之后。而对象创建是构造函数执行完成之后,所以你在构造函数中调用color属性,而这个时候color是还没有赋值的。那么String的初始默认为null。所以也就打印null咯
2.为什么会是null?这个很明显,对象是先初始化然后复制,虽然String color在构造函数之前,但是他的复制却是要等待对象初始化完成之后。而对象创建是构造函数执行完成之后,所以你在构造函数中调用color属性,而这个时候color是还没有赋值的。那么String的初始默认为null。所以也就打印null咯
#7
有点小疑问:既然赋值在初始化之后,可以解释子类构造函数中super()那句的打印结果,但为什么下一行print()打印的结果有值。
#8
多谢的你的回答,很不想为这样的问题浪费那么多时间,说不定这辈子也就遇到这一次了,但还是想了解。。:
能否这样理解,当jvm执行到子类构造函数的super()之前,已经做好了什么事?真是太痛苦了遇到”先有鸡还是先有蛋“这样的问题。我想这是大家公认的结果吧:先初始化父类的静态代码--->初始化子类的静态代码-->初始化父类的非静态代码--->初始化父类构造函数--->初始化子类非静态代码--->初始化子类构造函数
也就是说当到了子类构造函数时,前面的事都完了。。 又回到这个问题的起点,我崩溃了- -!!
#9
首先,这不是先有鸡还是先有蛋的问题,这个问题有非常明确的答案,然后,下面这段代码,你自己在Eclipse里执行一下吧!问题就迎刃而解了。呵呵!
class Animal{
static{
System.out.println("Static Animal....");
}
{
System.out.println("Animal....");
}
public String color="red";
Animal(){
print();
color="black";
}
void print(){
System.out.println("animal color is "+color);
}
}
public class Cat extends Animal{
static{
System.out.println("Static Cat.....");
}
{
System.out.println("Cat...");
}
public String color="yellow";
Cat(){
print();
super.print();
}
void print(){
System.out.println("cat color is "+color);
}
public static void main(String[] args){
new Cat();
}
}
#10
哇,一目了然,不容易啊,终于找到哪里犯傻了。。。
老是在意子类构造函数的第一行是隐式调用父类构造函数,竟然不自觉的认为父类构造函数初始化就是子类构造函数中的第一步,顺着这个想,到了子类构造函数了,就已经显示初始化了,所以泪奔了。。
让各位大虾见笑了- -|||
#11
从内存分配的角度上讲,我有一个疑问,(当然,如果是静态代码块的话,内存会给它分配一块独立与实例块的空间),在类的实例创建成功后,内存才会给这个实例分配空间,这里在调用子类的构造方法之前,是先调用父类的构造,那是不是内存即为子类的实例分配空间,又为父类的实例分配另外的空间?(其实我连有没有生成父类的实例也不清楚)。
#12
你首先要明白的是,从根本上来讲,对于子类来说,他包含了父类的所有方法,即便是他覆盖的方法他也是包含了父类的和子类自己的。所以,这个内存空间当然是包含了所有子类的和父类需要的空间。而且,当你只创建子类对象时,不存在还要创建父类对象这个说法。你可以在父类的构造函数中打印一下this指针,在子类中也打印一下,你会发现,this指的是同一个对象。
#1
1.是的
2.new Cat() 先调用父类构造方法 调print()方法 所以null
2.new Cat() 先调用父类构造方法 调print()方法 所以null
#2
1.根据打印出来的结果,可以肯定是这样的。
2.之所以是null是因为当父类调用子类的print方法时,子类的color变量还没有执行初始化。显示初始化时先于构造函数没错,但是指的是当前的类的变量的初始化,楼主给出的例子中,JVM首先初始化了父类的color变量,然后调用了子类的print方法,但是此时还没有初始化子类的color变量。而子类的方法调用的是子类的color变量,所以打印出来的就是null了。当构造函数启动时,成员变量是被加载,而不是初始化,也就是说这个指针存在,但是这个指针不指向任何对象。
#3
++正解
#4
LZ好好理解一下以下的例子就知道了
new 子类()的执行顺序是
1 父类初始化块
2 父类构造方法
3 子类初始化块
4 子类构造方法
LZ再好好体会一下吧
class A {
{System.out.println("执行顺序1");} //这里叫做初始化处理块
public A() {
System.out.println("执行顺序2");
}
}
class B extends A {
{System.out.println("执行顺序3");} //初始化处理块,String color = "yellow"; 相当于
//String color; //声明变量,相当于给变量分配内存空间并初始化为缺省值
//{color = "yellow"} //在初始化块给变量赋值
//所以,从执行顺序来看,LZ应该知道为什么是null了吧
public B() {
System.out.println("执行顺序4");
}
}
public class C {
public static void main(Srting[] args) {
new B();
}
}
new 子类()的执行顺序是
1 父类初始化块
2 父类构造方法
3 子类初始化块
4 子类构造方法
LZ再好好体会一下吧
#5
楼上说的和楼主说的又是两个问题了。初始化块儿和变量的初始化是两个概念,楼主可以自己写一些例子用调试模式看看。
#6
1.至于为什么会调用子类的print,那是因为你创建的是子类这个对象,在类中直接调用print()相当于this.print(),然而当前这个对象是Cat所以就调用了子类的print方法。如果你new的父类,那么自然调用的就是父类的print方法咯。
2.为什么会是null?这个很明显,对象是先初始化然后复制,虽然String color在构造函数之前,但是他的复制却是要等待对象初始化完成之后。而对象创建是构造函数执行完成之后,所以你在构造函数中调用color属性,而这个时候color是还没有赋值的。那么String的初始默认为null。所以也就打印null咯
2.为什么会是null?这个很明显,对象是先初始化然后复制,虽然String color在构造函数之前,但是他的复制却是要等待对象初始化完成之后。而对象创建是构造函数执行完成之后,所以你在构造函数中调用color属性,而这个时候color是还没有赋值的。那么String的初始默认为null。所以也就打印null咯
#7
有点小疑问:既然赋值在初始化之后,可以解释子类构造函数中super()那句的打印结果,但为什么下一行print()打印的结果有值。
#8
多谢的你的回答,很不想为这样的问题浪费那么多时间,说不定这辈子也就遇到这一次了,但还是想了解。。:
能否这样理解,当jvm执行到子类构造函数的super()之前,已经做好了什么事?真是太痛苦了遇到”先有鸡还是先有蛋“这样的问题。我想这是大家公认的结果吧:先初始化父类的静态代码--->初始化子类的静态代码-->初始化父类的非静态代码--->初始化父类构造函数--->初始化子类非静态代码--->初始化子类构造函数
也就是说当到了子类构造函数时,前面的事都完了。。 又回到这个问题的起点,我崩溃了- -!!
#9
首先,这不是先有鸡还是先有蛋的问题,这个问题有非常明确的答案,然后,下面这段代码,你自己在Eclipse里执行一下吧!问题就迎刃而解了。呵呵!
class Animal{
static{
System.out.println("Static Animal....");
}
{
System.out.println("Animal....");
}
public String color="red";
Animal(){
print();
color="black";
}
void print(){
System.out.println("animal color is "+color);
}
}
public class Cat extends Animal{
static{
System.out.println("Static Cat.....");
}
{
System.out.println("Cat...");
}
public String color="yellow";
Cat(){
print();
super.print();
}
void print(){
System.out.println("cat color is "+color);
}
public static void main(String[] args){
new Cat();
}
}
#10
哇,一目了然,不容易啊,终于找到哪里犯傻了。。。
老是在意子类构造函数的第一行是隐式调用父类构造函数,竟然不自觉的认为父类构造函数初始化就是子类构造函数中的第一步,顺着这个想,到了子类构造函数了,就已经显示初始化了,所以泪奔了。。
让各位大虾见笑了- -|||
#11
从内存分配的角度上讲,我有一个疑问,(当然,如果是静态代码块的话,内存会给它分配一块独立与实例块的空间),在类的实例创建成功后,内存才会给这个实例分配空间,这里在调用子类的构造方法之前,是先调用父类的构造,那是不是内存即为子类的实例分配空间,又为父类的实例分配另外的空间?(其实我连有没有生成父类的实例也不清楚)。
#12
你首先要明白的是,从根本上来讲,对于子类来说,他包含了父类的所有方法,即便是他覆盖的方法他也是包含了父类的和子类自己的。所以,这个内存空间当然是包含了所有子类的和父类需要的空间。而且,当你只创建子类对象时,不存在还要创建父类对象这个说法。你可以在父类的构造函数中打印一下this指针,在子类中也打印一下,你会发现,this指的是同一个对象。