public class Sub extends Super {
public static void main(String[] args) {
Sub sub = new Sub();
sub.say();
} Sub() {
System.out.println("sub_constructor()");
} void say() {
System.out.println("sub_say()");
}
}class Super {
Super() {
                say();
System.out.println("super_constructor()");

} void say() {
System.out.println("super_say()");
}
}执行的结果是
sub_say()
super_constructor()
sub_constructor()
sub_say()
可以看到子类的方法是在子类构造函数之前被调用的,请问java是用什么机制做到的,怎么实现的。

解决方案 »

  1.   

    The JVM looks at the real object at the other end of the reference, "sees" that it has overridden the instance method of the declared reference variable type, and invokes the instance method of the object's actual class.
      

  2.   

    继承
    new 类构造名 时,会依次调用该类的所有非Object类的父类的构造,先父类后子类,知道该类构造。
      

  3.   

    就这么一个问题,在csdn上至少讨论10次了,都讨论吐血了。。public,private,重载,覆盖等 变化太多了,排列组合下情况很难枚举全部的。这个话题曾经讨论最多有百楼之高。。
      

  4.   

      有关这个问题,可以看看<<Java2核心技术>>.
    那本书里对此做了详细讲述.
      

  5.   

    看懂这道题你就得需要首先知道一个对象的生命周期,new 子构造时,不管怎样总会调到父构造先执行父构造的第一句,再者重载的调用、重写在父子类的调用,还得分static 和member。
      

  6.   

    冲你这句话我加了100分(一次只能加100分)我只想知道java怎么能在类的实例没有的时候怎么可以调用到方法的,无论你是复制的内容。还是给我的连接,只要我看明白就行(或者告诉我那本书的那一节讲的也行,一定要能回答我的问题的)。
    ps:1.我知道构造函数调用的顺序
    2.我知道重载的选择是编译时决定的
    3.我知道重写是在运行时选择的,多态是动态绑定,延时绑定
    4.我知道没人会这样写程序(我只想知道原理)
    如果是多态,没有子类的对象的时候怎么调用了子类的方法(我认为的是多态一定要有父类的引用和子类的对象的)
    是不是我对多态的理解有错误呀?
    再说下我的问题:没有子类的实例怎么就能调用子类的方法(就算是反射也要先有实例的呀)。
      

  7.   

    看大家的意见偏向于多态,其实我看到这个题的时候我第一想法也是多态的,但是我否定了,我说下我的想法
    先从main函数说起
    Sub sub = new Sub();
    这时内存中会有一个子类的引用,同时会先调用父类的构造方法,但是在调用父类的构造方法的时候就访问了子类(重写后)的方法,这时子类的构造方法还没有执行,那么此时内存中,有的是子类的引用,父类的对象(此时不是多态把),但是此时关键的地方已经发生过了。试问怎么能解释成多态哪?
      

  8.   

    “polymorphism(多态)”一词来自希腊语,意为“多种形式”。多态在面向对象语言中是个很普遍的概念,同时也是对象开发软件的一个特殊特性,指的是一个程序中同名的不同方法共存的情况。Java语言支持两种类型的多态性:运行时的多态性和编译时的多态性。运行时的特性(动态多态性)是指Java中的动态多态性实现手段---覆盖(替换)基类中的同名成员函数(函数原型一致),其调用规则是依据对象在实例化时而非定义时的类型相应地调用对应类中的同名成员函数。编译时的特性(静态多态性)是指Java中的静态多态性实现手段-----重载函数,其调用规则是依据对象在定义时的类型相应地调用对应类中的重载函数。Java多态性的主要表现形式有:继承多态、抽象多态和接口多态。 1 继承实现的多态    
        在Java中,当一个类获取另一个类中所有非私有的数据和操作的定义作为自己的部分或全部成分时,就称这两个类之间具有「继承」关系。「继承」可分为「介面继承」和「实作继承」两类,「介面继承」就是只继承父类别的函数名称,然后子类别一定会实作取代之。所以当我们以父类别的指标「多型」于各子类别时,由于子类别一定会实作父类别的多型函数,所以每个子类别的实作都不一样,此时我们(使用父类别指标的人)并不知道此多型函数到底怎么完成,因之称为「黑箱设计」。而「实作继承」就是继承父类别的函数名称,子类别在实作时,也会用到父类别的函数实作。所以我们(使用父类别指标的人)知道此多型函数怎么完成工作,因为大概也跟父类别的函数实作差不多,因之称为「白箱设计」。 
      Java的类别继承为实作继承,子类别会用到父类别的实作(更正确地说应该是父类别有定义实作,所以子类别可能会使用到,即使不使用到也会遵循父类别实作的演算法),所以父类别与子类别有一定程度的相关性;Java的interface接口则是介面继承,因为接口只能定义函数名称,无法定义函数实作,所以子类别必须用「implements」关键字来实现继承,且每个继承同一介面的子类别当然彼此不知道对方如何实作,因此为一个黑箱设计。 1.1方法的覆盖 
      根据实作继承及动态多态性的特点,派生类(子类)将继承基类(父类)所有的方法、属性和事件。同时,我们可根据需要来重新定义基类的方法、属性和事件,甚至增加或者修改部分内容,以提供不同形式的实现。 
      代码示例一: 
      //定义父类superc 
      import java.io.*; 
      class superc 
      {public void sc() 
       { System.out.println("This is superc!"); 
       }} 
      //定义子类subc1 
      class subc1 extends superc 
      {public void sc() 
       { System.out.println("This is subc1!!"); 
       }} 
      //定义子类subc2 
      class subc2 extends superc 
      { public void sc() 
       { System.out.println("This is subc2!!"); 
       }} 
      class Test 
      {public static void main(String[] arg) 
      {superc a; 
      subc1 b=new subc1(); 
      subc2 c=new subc2(); 
      a=b; 
      a.sc(); 
        a=c; 
      a.sc(); 
      }} 
      程序运行结果为: 
      如上例所示,在父类superc中我们定义了方法SC(),其每一个子类都将继承这个方法。但是,这个方法在每个子类中的具体实现是各不相同的。   那么,Java编译器如何实现对同名方法函数的调用呢?面向对象程序开发中,我们将一个方法调用同一个方法主体连接到一起称为“绑定”(Binding)。Java中绑定的所有方法都采用后期绑定技术,即动态绑定:它意味着绑定在运行期间进行,以对象的类型为基础。若一种语言实现了后期绑定,同时必须提供一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。不同的语言对后期绑定的实现方法是有所区别的。但我们至少可以这样认为:它们都要在对象中安插某些特殊类型的信息,并可通过这些区别信息实现多态。由于动态绑定技术的支持,Java的程序在执行时灵活性就大大提高了。Java 的这种机制遵循如下原则:     其一,当超类(父类)对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类(父类)中定义过的,也就是说被子类覆盖的方法。     其二,每一个实例对象都自带一个虚拟函数表(virtualtable),这个表中存储的是指向虚函数的指针,实例对象通过这个表来调用虚函数,以实现多态。     实际上,使用虚拟函数表的方法,表项在编译时便已固定,把函数映射为在虚拟函数表中的偏移,到了运行时,只知道“偏移、执行”,至于究竟是哪个函数,无从知晓。类似于查表的过程,在编译的时候一定是存在的,但不存在于运行时。对程序而言,从源码到运行是一个完整的过程,一些功能被某些语言放到了编译时,而在另一些语言中被放到了运行时,折衷的原则取决于语言设计。虚拟函数表的实现中,每个类的表中,不仅仅要保持自己的定义的方法,还要保持自己超类的方法。我们知道,在面向对象的语言中,子类对象常常要当作超类对象使用,而在运行时,要找某个方法,只知“偏移”,所以,子类的虚拟函数表必须完全兼容超类的虚拟函数表,才能保证整个系统的正常运行,而保证的方法就是保存超类的所有表项。这样带来的问题是,当子类增多,虚拟函数表就无可避免的会增多,即便子类只有一个属于自己的方法,但它仍要带有超类所有的方法,这是一个巨大的负担。所以,那些建议“不要定义太庞杂的继承系统”的说法,是有一定物理基础的。 1.2函数的重载 
      重载是同一类中定义同名方法的情况。这些方法同名的原因,是它们的最终功能和目的都相同,但是由于在完成同一功能时,可能遇到不同的具体情况,所以需要定义含不同的具体内容的方法,来代表多种具体实现形式。 
      Java支持用户定义的函数重载。一个类中可以有相同名字的方法,这些方法可以有不同的意义。但是,这些重载的方法中,必须满足参数数目不同,相同位置上的参数类型不同等等。这些不同可以帮助编译器区分不同版本的方法;根据静态多态性调用规则,编译器依据对象在定义时的类型相应地调用对应类中的重载函数。 
        我们可以定义了若干个构造函数Gz(),当用户创建该类对象的语句时,编译器会自动根据给出的实际参数的数目、类型和顺序来确定调用哪个构造函数来完成对新对象的初始化工作。 
      同样地,子类也可以根据实际情况对其父类的构造函数进行覆盖,有异曲同工的效果。但应注意:子类如果有多个构造函数的时候,父类要么没有构造成函数,让编译器自动产生,那么在执行子类构造函数之前先执行编译器自动产生的父类缺省的构造函数;要么至少要有一个显式的缺省构造函数可以让子类的构造函数调用。 2 抽象类实现的多态 
      在很多Java程序应用中,类层次的顶层类并不具备下层类的一些功能和方法。我们可以在超类中将这些方法声明为没有实现的抽象方法,如果一个类里包含了一个或多个抽象方法,类就必须指定成abstract即「抽象类」。使用abstract类型修饰符的抽象方法,属于一种不完整的方法,只含有一个声明,没有方法主体,基类不能实现它,必须由派生类过载实现,为其它子孙类用抽象机制实现多态性提供了统一的界面。对所有与基础类声明的签名相符的衍生类方法,都可以通过上面介绍过的动态绑定机制进行调用,该类未实现的方法由派生类提供,已实现的成员仍可被重写,并且派生类仍可以是抽象类或实现附加接口等功能。 
        由于抽象类是其所有子类的公共属性和方法的集合,它可以在类的某些属性和方法中提供不变的因素和功能,同时大大提高了类的其他过程的灵活性。从上面的例子可以看出,除基础类以外,实际并没有进行什么改变。创建抽象类和方法有时对我们非常有用,因为它们使一个类的抽象变成明显的事实,可明确告诉用户和编译器自己打算如何用它。 3 接口实现的多态 
       
      以上所谈到的多态行为均用到了类的继承关系所建立起来的子类型关系。Java接口同样支持用户定义的类型,可以实现类型的「界面继承」;并且Java的接口机制启动了建立在类型层次结构上的多态行为,能够实现接口的组合和扩充,一定程度上对Java类型及其功能进行了优化。Java中一个类只能有一个父类,但是单个类可以实现一个或多个接口,多个类可实现相同的“接口”。 
       
      “interface”(接口)关键字使接口的抽象概念更深入了一层,我们可将其想象为一个“纯”抽象类。接口常常被用来为具有相似功能的一组类,对外提供一致的服务接口,这一组类可以是相关的,也可以是不相关的,而抽象类则是主为一组相关的类提供一致的服务接口。所以说接口往往比抽象类具有更大的灵活性,它允许创建者规定一个类的基本形式、方法名、自变量列表以及返回类型,但不规定方法主体。接口也包含了基本数据类型的数据成员,但它们都默认为static和final。接口只提供一种形式,并不提供实施的细节,这也为其本身及子类提供了较广泛的空间。      由于接口中只有方法原形,实现接口时无成员变量名字冲突问题,也没有对父类方法的重定义问题,也不存在重复继承问题,比一般类的多态度继承简单。接口继承形成的层次独立于类层次,因此允许不同层次中的类实现同一接口,这些实现接口的类支持公共的行为,但实现这些行为的方法可以不同,无须共享任何实现方式,呈现多样化。同时,这样的多态行为使Java的接口的功能的重大意义显得很明显。通过接口,每个类都可以自由决定其实现的细节;利用继承技术,可方便地为一个接口添加新的方法声明,也可以将几个接口合并成一个新接口。这样,实现某一接口的多个类或接口可以以不同的方式实现相同的接口,而每个类或接口仍支持同一组方法,当实例化这些类或实现接口后,就弥补了Java中“一个子类,只能有一个父类”的不足,实现了多态性的“一个接口,多种方法”。 4 结束语 
       
      “多态性”意味着“不同的形式”,是建立对象模型的有力工具。为充分使用多态性乃至面向对象的技术,我们必须将自己的编程视野扩展到不仅包括单独一个类的成员和消息,也要包括类与类之间的一致性以及它们的关系。因为只有这样才可真正有效地加快自己的编程速度、更好地组织代码、更容易做出包容面广的程序以及更易对自己的代码进行维护与扩展。本文对Java中的多态性及其实现原理进行了深入地解析,目的在于希望学习和使用Java语言的程序设计人员,能更好地掌握开发Java程序。
    原文地址: http://qkzz.net/magazine/1009-3044/2007/24/2259750.htm
      

  9.   

    这个网摘希望对楼主理解有帮助,该网摘是"深入解析Java的多态性及应用研究"
      

  10.   

    The Java Virtual Machine Specification
    Second Edition
    2.17.6 Creation of New Class Instances
    Unlike C++, the Java programming language does not specify altered rules for method dispatch during the creation of a new class instance. If methods are invoked that are overridden in subclasses in the object being initialized, then these overriding methods are used, even before the new object is completely created.
      

  11.   

    多态啊
    第一个类是子类,第二个类是父类,子类继承了父类
    所以对像sub调用方法时是调用本类中的,不是调用父类中的方法,
    因为父类中的方法被子类覆盖,要想调用父类中的say()方法需要使用super()语句.
    这是实现多态的一种方法,也可以定义接口来实现多态.
    interface是定义接口类implements是实现接口类.
      

  12.   

    java编程思想中多态那章,构造器内部的多态方法的行为有你想要的所有内容,自己翻书找去
      

  13.   


    这个说的很对,对象还没被初始化,这些被重写的方法就被可以用了,不管对象有没有new出来
    所以 Super() {
                    say();
            System.out.println("super_constructor()");
    这里已经可以调用say()方法了;
    你new的是子类的对象, 就调用子类say()方法
      

  14.   

    因为say()方法被子类重写了么..
    所以父类构造方法中调用的其实调用的是子类的say()方法
      

  15.   

    又整理了一下
    public class UseMethodBeforeCreate { /**
     * @param args
     */
    public static void main(String[] args) { Sub sub = new Sub();//1
    sub.say();//5 }}class Sub extends Super { Sub() {
    System.out.println("sub_constructor()");//4
    } void say() {//6
    System.out.println("sub_say()");
    }
    }class Super {
    Super() {
    say();//2 对象还没被初始化,这些被重写的方法就被可以用了,不管对象有没有new出来
    System.out.println("super_constructor()");//3 } void say() {
    System.out.println("super_say()");
    }
    }
    // 1子类构造的过程 先调用父类无参数的构造方法,2,3 然后再是4, 最后 5,6
      

  16.   

    说了这么多,你们还没有解决LZ的问题,LZ把分给我一半吧
      

  17.   

    昨天睡的晚,现在来看了一下
    谢谢这个,果然很强大,我正在找thinking in java 中的解释。
      

  18.   

    我说企鹅发的杂这么眼熟,原来是THINKING E文的,我看的全是中文的~
    不过这样的问题的确不应该拿出来了,讨论的够多了~
      

  19.   

    这不是Thinking in Java上的。
      

  20.   

    我去翻它出来~THINKING4 P163
      

  21.   

    感觉公司的教材是不是think in java啊
    我是记住不能这吗做了