这跟重载没关系。A a;新建的是属性。
test.a(b);调用的方法。你的main函数只是调用4个方法,自然打印出来这个结果。

解决方案 »

  1.   

    但是我引用a明明是引用的是B类的实例嘛,他也知道他引用的是B的实例,但是他为什么不调用参数为B类的方法,而是去调用类型为A的方法呢,楼上的人兄的话没有听明白是什么意思,我当然知道谁是变量,谁是方法了。难道我搞了这么久了,这个还不知道?只是我的方法名称起的比较难看而已,和变量名称一样了样;
      

  2.   

    每清楚摟主意思不同類型的參數 應該是多態吧就像uint16 和 uint32不一樣小聲地說
      

  3.   

    to JullienSorel(月光光) ,呵呵,多态不是“不同類型的參數”,不同类型的参数是函数重载,而多态是指如下代码:
    void fun(A a){}
    那么此时因为B是从A继承而来,"B is a A",可以将类型为B的参数也传到fun(A a)方法中。但是现在是他两个方法都可以传进去,如现在的A a=new B();他肯定是A又是B,是吧,那么到底调用哪一个方法呢,是fun(A a)还是fun(B b)呢,从验证来看,他调用的是其引用类型(A)的方法,但是他实际上引用的是一个类型为B的实例呀,难道假如调用参数类型为B的方法不更加合乎情理吗?
      

  4.   

    这么打个比方吧:
    这个fun()方法在查看传给它的参数时,就好像戴了一个滤光镜,假设这个滤光镜只能让绿色的光通过,fun()方法认为给它的光都是绿色的,将其作为绿光对待。这种情况下,白色的光可以看作是绿光的子类吧?因为它具有了比绿光更多的一些别的颜色的光。白光通过这个滤光镜的时候,所有别的颜色的光都被过滤了,只剩下绿光,fun()只会将其看作绿光。
    同样的道理,a引用尽管指向的是一个B对象,但它被声明为了A对象,方法在查看这个对象的时候,只会看到它是A对象,而不会认为它是B对象,于是将其作为A对象进行处理。
      

  5.   

    这里用的不是重载吧,这里是java的上溯特性
    既然你声明了a,无论他的实例是什么类型,在编译的时候都认定是a;
    楼主似乎把类型和对象实例搞混了
      

  6.   

    没说错的话a 是 B 的上转型吧!这么调用方法与a,b 的内存中的东西无关,正如jiangnanyuzi(江南愚子) 所说的那样.楼主我的意思是这样,你在A类和B类中println内容这时,调用a时println的内容应该是B类的方法,没错吧!在text类中只看a,b是什么类的对象不研究a,b具体是那个内存中的东西.其实a就是B类继承A类的那一总部分并不是A类原来的东西了.上转型是这样吧!
      

  7.   

    我知道这,这和c++里面的差不多,当A a;B b;a=b;时,那么b的代表B的那部分信息就丢失了,这就是“缩小”(专业属于怎么说来着),但是你们分析的都是结果!我想讨论的是为什么要这么设计,正如我上面提到的“难道假如调用参数类型为B的方法不更加合乎情理吗”。但是我现在不知道在技术上能不能作到,因为这跟方法覆盖不一样,方法覆盖是因为动态连结事存在一个一个函数指针列表,里面存储的是子类的该方法的地址。但是我想既然a.getClass().getName()都可以得到"B",那么要识别引用a到底是引用的哪个类的实例是不成问题的,现在关键就是我没有明白设计时为什么不这么设计?
      

  8.   

    好象这个结果很正常啊,有什么问题吗?
    B不是继承的A吗?而且向上转型
    D的实现接口C啊,也是向上转型
      

  9.   

    这和java的运行机制有关,想了解可以看下
    王森的<<深度历险>>这本书。
      

  10.   

    事情是这个样子的:
    A a=new B();  ----> 此时实例化了一个B实例,并使A声明的引用a指向该实例,并且便该实例屏蔽掉了B,
                        而是成为了A类型;
    B b=(B)a;    ---->因为B extends A,所以此时即使a被强制类型转换成了B类型,也是无法增加上它原来
                      所没有而在B中有的新方法,也就是说它无法强制转换成B类型;故a的类型依然还是A类
                      型,它并没有被改变,但b所指向的实例(new B())又被强转成了B类型的(b是B类所
                      声明的引用,它指向的是(new B())这个实例);
    也就是说,子类可以上转成父类,因为它天生继承了父类中的成员及方法;
    但父类不可以完成下转成子类,因为它天生不具备子类中后添加的成员及方法;
      

  11.   

    建议去看看TIJ里面的相关章节,你就能明白了。偶只看了一遍,能明白其中的意思,但是说不太清楚。
      

  12.   

    正好准备离职了,这2天临时学点java准备跳槽一用,这种问题适合俺这种入门级的,houhou
    我说一下我的理解,说错了大家不要扔砖头搂主可能把他和多态联系上了,多态其实和这个是2档子事,多态在c++中比较好理解,是通过虚表实现了所谓的迟绑定,所以有了多态特性,但这个和传递给函数的参数完全不同,编译器在编译的时候看的是实际定义的类型,编译器编译的时候看到你传的参数的类型是什么就是什么,道理就是这么简单
    楼主不明白为什么这么设计---因为这个是编译器的事,你说的什么get什么name可以得到B什么的都是运行期的事,理解否?public class Test {
      public static void main(String[] args) {
        Integer haha=new Integer(0);
        haha.toString();
        Test test=new Test();
        A a=new B();
        B b=(B)a;
        test.a(b);
        test.a(a);    C c=new D();
        D d=(D)c;
        test.c(c);
        test.c(d);
      }
      public void a(A a){
        System.out.print("a(A)  实际类型是:");
        System.out.println(a.toString());
      }
      public void a(B b){
        System.out.print("a(B)  实际类型是:");
        System.out.println(b.toString());
      }
      public void c(C c){
        System.out.print("c(C)  实际类型是:");
        System.out.println(c.toString());
      }
      public void c(D d){
        System.out.print("c(D)  实际类型是:");    if( d instanceof D )
        {
          System.out.println(d.toString());
        }
        else
        {
          System.out.println("同学们好");
        }
      }
    }
    class A extends Object{
      public String toString() {
          return new String("A");
        }
    }
    class B extends A{
      public String toString() {
          return new String("B");
        }}
    interface C{
    }
    class D implements C{
      public String toString() {
          return new String("C");
        }
    }
    输出结果为
    a(B)  实际类型是:B
    a(A)  实际类型是:B
    c(C)  实际类型是:C
    c(D)  实际类型是:C
      

  13.   

    楼主不明白为什么这么设计---因为这个是编译器的事,你说的什么get什么name可以得到B什么的都是运行期的事,理解否?编译期不是编译器,review一下就是有这么多bug ^_^
      

  14.   

    “难道假如调用参数类型为B的方法不更加合乎情理吗”
    可能是考虑效率吧,如果都要到运行时再去检查类型再去决定怎么调用,JAVA的速度估计会再一倍:)
    理论上是楼主的说法更合理,
    方法的重载有两种方式,overloaded和override,前者指可以有多个相同的方法名,只是参数不同,后者批子类覆盖父类的方法,楼主的写法属于overloaded,
    对于overloaded,JAVA的实现机制没研究过,但看过C++的,它是编译器编译的时候自动把你那些有overloaded的方法名都改了,改成原来的方法名+类型构成一个新的方法名(保持跟C的兼容,这样所有的方法还是没有重名的),它把的你的方法名改了,那你所有调用这个方法的地方所引用的名称它也要帮你改过来,不要我说为什么吧,所以编译时就决定了该调用哪个方法,没有动态可言,这也是为什么overloaed可以接受参数不同而不接受返回类型不同的方法的重载。
    JAVA从表面上看跟C++提供的功能是一模一样的,不知道内部实现是否也是一样
      

  15.   

    同样如果需要多态,那就用override,也就是A类写一个test方法,B类也写一个test方法,这样肯定是a调A的,b调B的,还更符合面向对象的原则,达到了封装,也达到了多态的目的。
    不管JAVA还是C++实现的都是一个妥协的方案,谁说的,妥协的方案就是最好的方案,smalltalk是纯面向对象的,应该实现了楼主的功能:)
      

  16.   

    晕,我还以为是什么呢,其实很简单的,只是abcd看的头晕。简单的说A a=...只看前面的,根本和后面没关,但是如果后面的new也好,(b)强制转换也好牛头不对马嘴,在运行的时候有强制转换失败的异常抛出
      

  17.   

    楼主主要关心为什么要这样设计?
    我的理解是效率。
    因为getClass方法调用是java反射机制的一部分。并非普通的方法调用。
    而且在java的早期,1.1版本是没有反射功能。所以他不可能按照楼主的想法去设计。
    而且即使有反射机制。一般而言,效率要差1000倍。如果需要动态编程。偶尔调用一个反射
    方法还可以。如果整个方法调用的设计都采用这种机制。java该多慢啊。而且所得有限。不过是一点语法甜头而已。
    我没有深入研究。只是学习过程中的一点理解。供参考。
      

  18.   

    终于有几个人理解我在说什么了!哎!为什么有这么多人拿了一截就乱跑,不要以为我那么白痴,不知道在当前的java设计原理上为什么会产生这样的问题。类型是什么?类型的目的就是为了标志一片内存区域的意义。在C++中比较好理解。如A a;那么假如从汇编的角度来理解的话,相当于a指向了这片内存的开始,而不同内型的内存空间以及内存意义是不一样的。如
    class A{
      char data[10];
    }
    那么表明a指向的内存开始的前10个字节是存储data数据,而现在假如有
    class B{
      char data[10];
    }
    此时将指向A的指针强制转化为B,那么也能得出和A一样的结果,因为这两个类型在内存中的意义是一样的。所以在java中当将子类转化为父类时,那么此时编译器只知道这个类型是父类的类型,那么当然也就只能看但属于父类的内存空间,至于属于字类的那一部分因为超过了父类的内存范围,所以看不见了。这就是原因。但是假如是换一种设计方案,比如我在每一个类中都默认增加一个空间来标志该引用实际指向的类型,比如
    struct object{
         char type[20];
         other    
    }
    那么即使现在该片内存空间被一个引用类型为Object的引用所引用,但是从其内存中任可以发现实际的类型,也就是说他能够根据这看见所有的内存空间,而不是只是那片属于Object的空间,这与现在的设计是不一样的
      

  19.   

    重载是编译期决定的,根据入口参数类型决定调用哪个函数Test test=new Test();
    A a=new B();
    B b=(B)a;
    test.a(b); //参数类型是B,所以调test.a(B b), 
    test.a(a);//参数类型是a,所以调test.a(A a), *编译器在这里就要决定调用关系,生成运行字节码楼主的意思非要运行期时得到a是A还是B,再决定调哪个函数,这把重载搞得太复杂了,可看不出有什么好,重载的作用也就是图个方便,不然改个函数名不就结了
      

  20.   

    类型是什么?类型的目的就是为了标志一片内存区域的意义。
    还有一个意义用于编译器检查,检查你的赋值及方法调用是否合法,所以说JAVA与C++都是强类型的语言,对于JAVA来说不只有编译时检查,还有JVM在运行时检查,这也就是你在进行错误的强制类型转换时会抛出ClassCastException.而C++则不会报错,也就是说楼上说的解决办法java内部都已经实现了的,就是JAVA的“反射”机制,它在运行时是知道一个对象的类型的。
      

  21.   

    我觉的父类好像可以强制转化成子类,比如可以将object转化成某个他的之类,不信你们试试
      

  22.   

    to yoi() :
    父类当然可以转化为子类了
      

  23.   

    感觉楼主还是不大明白但是假如是换一种设计方案,比如我在每一个类中都默认增加一个空间来标志该引用实际指向的类型,比如
    struct object{
         char type[20];
         other    
    }
    那么即使现在该片内存空间被一个引用类型为Object的引用所引用,但是从其内存中任可以发现实际的类型,也就是说他能够根据这看见所有的内存空间,而不是只是那片属于Object的空间,这与现在的设计是不一样的楼主表达的这个意思很草草,不过还是大致清楚,但是感觉你思考的和你想到的东西总是没有完整清晰的连接到一起对于rtti的支持,java本身就具备,对于mfc,mfc里通过一套宏也实现了,对于c++,c++是比较折中的做法,c++的rtti支持是可选择的(比如vc编译器里你可以加上编译选项/Gr,这样也可以使用c++天身的rtti特性),不过这个和你说的函数调用时候的选择问题还是大相径庭对于函数调用来说,如果要实现按照实际类型来选择相应的函数,这就必须要加上一层间接性,而且这个间接性是简历在rtti基础之上的,比如c++这种编译成二机制代码的编译器是无法在编译时直接知道该调用什么的,从而直接生成call xx函数的二进制代码,而在运行期去判断这个是要付出代价的,对于java这种虚拟机来解释的语言,这种间接性带来的时间伤和空间上代价也是不可避免的楼主想要的这种特性你有没有想过能带来什么好处?好处在哪里,或者好处比代价要多吗?另外重载也没有说只是图个方便,我也没觉得有什么方便的,重载是面向对象语言的一种表达方式,比如一个方法eat,eat不同的东西我还要用不同的词汇来表达,不仅是不方便而且也不确切
    对于类新转换的概念,内存的解释那些理解比较确切,不过用看的见看不见、内存的意义就不一样了这样的词汇去描述容易误倒人吧 @_@;
      

  24.   

    我感觉javaSky82() 说得挺好,说得对。我也说几句。楼主的疑问是:为什么已经清楚地知道A类型的引用a引用的是B的
    对象了,还要调用A(A a),而不调用A(B b)呢?!
    我想说的是:如果把一个值为null的引用a传递给了A方法,
    最后编译器要使用哪个方法呢?传递null过去是可以通过编译的,
    只是在运行时会出现问题。
        结论:对于一个类中被重载过的多个方法,编译器编译时由
    参数类型来决定使用哪个方法;最后使用哪个方法,在程序运行
    以前已经确定了。或者这样说:Java决定这样去做了,我们接受吧。而运行时的多态性就是另外一回事儿了。
    运行时,由对象来决定使用哪个方法。(这些方法是子类重写过
    的,而不是重载过的。c++中需要virtual关键字。)希望下面的代码能帮助你理解运行时多态性。
    public class Test{
    public static void main(String []args){
    Chinese stone = new Chinese();
    //即使写成Person stone=new Chinese();
    //也不会影响结果。 methodA(stone);
    }
    static void methodA(Person p){
    p.sayHello();
    }
    }class Person{
    public void sayHello(){
    System.out.println("Hello");
    }
    }
    class Chinese extends Person{
    public void sayHello(){
    System.out.println("您吃了吗?");
    }
    }
    ***输出结果是:您吃了吗?
    原因是,stone实际上是Chinese的对象,而且,Chinese重写
    了Person的sayHello方法。所以,调用了Chinese的sayHello方法。
    这就是运行时的多态性。    看过我的留言以后,你可能会说:“你当我白痴啊?!这些
    我知道。”而实际上,你恰恰是没把这些东西搞明白。
    如有冒犯,请原谅。
      

  25.   

    不是javaSky82(),记错了,是别人来着。
    javaSky82() 说得有问题。A a=new B();
    B b=(B)a; b仍然引用了一个B的实例。
    可以转换回去。
    new B();创建一个B的对象。不管谁去引用它,它就是B的对象。
    不会因为a=new B();而变成别的对象。
      

  26.   

    shiyongfan(追梦人) 说的很明白了,我总结一下,抓住本质,别绕的太多了,晕了一圈实际还是回到本质。
    (1) 调用哪个方法,靠参数类型来识别,这个类型是对象被声明的类型。(2) 至于运行时用什么方法,看这个对象实际 new 时候的类型。
      

  27.   

    再多说一句,子类可以向父类转型,但是由于当初new时候的内存空间还是老样子,所以转了以后需要用到子类有而父类没有的方法,还是可以用的,当然父类用的时候必须是父类中声明了的,对于子类就不会有任何影响了吧?因为你本来就是中国人,即使你没定义为是亚洲人,或者是地球人,实际上你还是中国人啊?对不对??PS: 看了我写的应该很清楚了,我不喜欢那些抽象的语句,那样我会晕的,我喜欢抓住本质,用容易懂得语言来描述。