public class Base {    private String name = "base";    public Base() {
            tellName();
            printName(name);
    }    public void tellName() {
        System.out.println("Base tell name:" + name);
    }    public void printName(String name) {        System.out.println("Base print name:" + name);
    }
}public class Derived extends Base {    private String name = "Derived";    public Derived() {
            tellName();
            printName(name);
    }    public void tellName() {
        System.out.println("Derived tell name:" + name);
    }    public void printName(String name) {        System.out.println("Derived print name:" + name);
    }    public static void main(String[] args) {
        Derived derived = new Derived();    }}针对程序跑出的结果,思考许久,想了2种解释,一种是多态,一种是程序空间的解释,貌似稍微改一下程序就无法解释了。
还求大神给分析分析这个到底是如何执行的?java多态

解决方案 »

  1.   

    为什么第一行的name输出为null,真是有点奇怪。
      

  2.   

    我一开始认为子类构造器的this引用会传递给父类构造器,进而引发多态,而根据执行顺序,在执行父类的构造器中得tellName时,因为多态,所以执行的是子类的tellName,而此时在执行父类构造器(即super())时,Base中得name已经初始化了,但是Derived中的name此时还没有初始化,因而是Null。我这么理解有一个漏洞,就是假设父类的tellName方法是private的,那么就不存在多态了,但是如果按之前的假设this会传递给父类构造器,那么应该是执行子类的tellName,但是实际执行的却是父类的tellName,所以我就凌乱了,求大神解释
      

  3.   

    http://hi.baidu.com/linyongboole/item/74c32815dbe5c10fd1d66d04  可以看看这篇文章,你就懂为什么会这样了!
      

  4.   

    这个也很好理解。子类继承父类后,new 一个子类的实例,就会调用父类的构造方法,又因为你在子类中重写了父类的方法,所以会调用子类的方法。这时就是多态的形式了。但在调用父类构造方法里的方法时,第一个方法调用时,name字段还没有值,所以会出现null(这点会很让人迷惑),因为在调用这个无参方法时,会把这个name看作局部的变量,离方法最近的变量。这个变量就是光声明了,并没有赋值,所以为null。第二个方法调用时,指明了从外部传入参数,所以会name是有值的。就是你声明的字段name。一家之言,请多赐教。
      

  5.   

    构造函数的目的是为各字段初始化,所以除了这些操作之外的方法一般不要在构造函数中调用。如果把代码改成下面这样,你就会看出来了。class Base {
     
        //private String name = "base";
    int name = 2;
        public Base() {
    printName(name);
                tellName();
                
        }
     
        public void tellName() {
            System.out.println("Base tell name:" + name);
        }
     
        public void printName(int name) {
     
            System.out.println("Base print name:" + name);
        }
     
     
    }
     
    public class Derived extends Base {
     
        //private String name = "Derived";
    int name = 3;
        public Derived() {
                tellName();
                printName(name);
        }
     
        public void tellName() {
            System.out.println("Derived is name:" + name);
        }
     
        public void printName(int name) {
     
            System.out.println("Derived print name:" + name);
        }
     
        public static void main(String[] args) {
            Derived derived = new Derived();
     
        }
     
    }
      

  6.   


    Hi,谢谢你的回答
    针对你的回家,我有两个不明白的地方:
    1.“又因为你在子类中重写了父类的方法,所以会调用子类的方法。这时就是多态的形式了。”这句话你能详细解释下原理吗?因为是多态,那么就是由调用tellName方法的对象在运行时决定到底调用父类还是子类的tellName方法,那么这个对象我认为是this引用。那么是否是在Derived构造器开始执行的时候,跳转到执行Base的构造器时,将this引用传递过去了?我觉得我这种理解是没有道理的,还请指教这里的多态到底是怎么一回事?
    2.如果我把Base的tellName的public改为private,那么对于tellName方法的多态条件就不在成立,此时又该如何解释呢?谢谢
      

  7.   

    Derived是Base的子类,在new时,它就会调用父类的构造函数,所以也会调用其中的方法。而方法又被子类重写,所以会调用子类的同名方法。但字段是不可以重写的.也就说在父类的构造函数中调用tellName()方法时,会用两个name字段同时存在的,jvm也搞不明白也你是调用哪一个了,所以会出现在null。再一个String类型的字段是比较特殊的字段。因为String类型是不可变量,java在声明String类型的变量的时候,如果是个直接量(String  name="张三";这就是直接量,而String str = new String("张三");这是new 出来的.这两种声明的方式在内存是不一样的。所以在内存中使用的静态变量区来保存直接量以提高性能。如果一个静态变量区有一个变量时,则在次声明一个同名的变量时,就会把静态变量区中的String类型的变量直接给你,而不用再声明一个了。回到你的第2个问题上来,那就是你把public void tellName()方法修改成private void tellName()后,因这个方法是父类私有的,它不会被子类继承,所以它当中的变量会有一个明确的指向那就是父类的name的字段。
      

  8.   

    首先你给的代码 有点疑问、如果上面的两个类在同一个包那么你的public 只能有一个 还有你的main方法在 类的里面 有问题(根据你的代码我猜测应该放在 base这个类)
    开始分析:程序首先调用父类的构造器
     public Base() {
                tellName();//这里tellName 在子类 Driver中被重写了所以执行的是子类的tellname和 printName 有疑问请在子类中加上@Override
                printName(name1);
        }
    看子类 Drivered中的方法
    @Override
        public void tellName() {/**由于没有传入参数name是父类的name没有赋值默认为null 所以第一次输出null 不信可以在父类构造器中传入参数name试试看看是不是输出Base 继续看printName**/
            System.out.println("Derived tell name:" + name);
        }
        @Override
        public void printName(String name) {/**这里传入了参数name所以默认就是
         父类的 name 所以 编译器会去找name的值 开始初始化 所以输出 Base
     
            System.out.println("Derived print name:" + name);
        }
    下面就是 子类调用自己的构造器 我就不啰嗦了 请多指正!
      

  9.   


    貌似被你这么一说就更糊涂了,关于String那个,不是同名变量,而是同字符串吧?针对这句话“但字段是不可以重写的.也就说在父类的构造函数中调用tellName()方法时,会用两个name字段同时存在的,jvm也搞不明白也你是调用哪一个了,所以会出现在null。”我的理解略有不同,我觉得是动态分配导致tellName的实际执行是在子类的上下文中,因此输出的是子类上下文哄的name,而此时子类的name还没有被初始化,因而是默认值null。我的问题是这个动态分配是如何实现的,因为以前一直认为多态就是靠obj.function()中的obj来决定到底执行谁,所以这里联想到是不是有一个子类对象的引用在这里起作用,如果是,那么这个引用又是如何传到父类的构造器里的?
      

  10.   


    代码没有问题,这点请放心,我是自己跑过的,两个类,main方法位于Derived中不会有任何问题的。
      

  11.   


    这个我知道的哈,其实我经过一晚上的思考,已经确定了是动态分配主导了这一结果,但是以往的经验让我认为这个动态分配是靠对象.方法()的方式在运行时确定的,那就需要一个对象在父类的构造器中起作用,不用质疑肯定是Derived,这点我也调试确认过了,但是我不清楚的是这个子类引用是如何进入到父类构造器中的?
      

  12.   

    你最开始给的代码1楼 有两个public我看的是第一楼顶的 反正是那个意思!我把 class Derived extends Base {的 public去掉  main方法仿造base中 反正是一样 的
      

  13.   

    我都说了是 方法的重写 也就说 在 调用父类的构造器中父类的  tellName();printName(name);
    两个方法已经被重写 父类构造器调用 这个两个方法的时候 就是调用的子类的 这两个方法,不知道我这样说 说明白没
      

  14.   

    首先是初始化Base类的name,然后Base的构造函数,调用的是Derived的tellName(),但是子类的name属性还没有被初始化,所以打印出null,接着调用Derived的printName(name)方法,传的是Base类的name,此时Base类的name已经初始化为"base"了,所以打印的是base。然后继续执行,初始化Derived的name属性,然后调用构造函数,结果可想而知了
                
      

  15.   

    这个主要是继承中重写的父类方法,程序的调用顺序问题,由于重写了父类的方法在初始化的时候先初始化父类,由于重写了父类的方法所以后直接调用子类的方法,此时子类中的 private String name = "Derived";稍为初始化所以第一个输出就为null了.如果子类中的private String name = "Derived"设置问static情况就不一样了.
    这个简单的例子其实考的地方蛮多的.只要好好的理解继承,重写和static的使用就不难判断了.
      

  16.   

    这个题的类似版本在李刚的疯狂java一书中看到过,这个和类的初始化顺序以及this有关,大概的理解就是在调用时调用的是子类的方法,而子类方法中的对应的属性还未来得及初始化,所以就是null,具体怎么说的忘记了。
      

  17.   

    public class Base {
    private String name = "base";
    public Base() {
    System.out.println("-6-");
    tellName();
    printName(name);
    }
    public void tellName() {
    System.out.println("-4-");
    System.out.println("Base tell name:" + name);
    }
    public void printName(String name) {
    System.out.println("-5-");
    System.out.println("Base print name:" + name);
    }
    }public class Derived extends Base {
    private String name = "Derived";
    public Derived() {
    System.out.println("-3-");
    tellName();
    printName(name);
    }
    public void tellName() {
    System.out.println("-1-");
    System.out.println("Derived tell name:" + name);
    }
    public void printName(String name) {
    System.out.println("-2-");
    System.out.println("Derived print name:" + name);
    }
    public static void main(String[] args) {
    Derived derived = new Derived();
    }
    }
    这样就可以看出来是走的顺序了。
    结果是:
    -6-
    -1-
    Derived tell name:null
    -2-
    Derived print name:base
    -3-
    -1-
    Derived tell name:Derived
    -2-
    Derived print name:Derived至于为什么,真的不能理解
      

  18.   

    quote]这个也很好理解。子类继承父类后,new 一个子类的实例,就会调用父类的构造方法,又因为你在子类中重写了父类的方法,所以会调用子类的方法。
    说得很好