Topic: 关于继承的深入讨论

  Print this page

1.关于继承的深入讨论 Copy to clipboard
Posted by: jameszhang
Posted on: 2005-08-11 19:24

来点基础的


class Test1
{
public String str = "test1";
public String getStr()
{
return str;
}
}
class Test2 extends Test1
{
public String str = "test2";
public String getStr()
{
return str;
}
}
...
...
Test1 t1 = new Test2();
System.out.println(t1.str);
System.out.println(t1.getStr());


谁能给出结果,并进行详细说明,如果是C++ 会怎样呢?

2.Re:关于继承的深入讨论 [Re: jameszhang] Copy to clipboard
Posted by: truthawp
Posted on: 2005-08-11 23:22

恩,通过对楼主代码修修改改,又对继承概念有了进一步了解,希望是彻底弄懂了 呵呵Smile C++的,不会^_^!

3.Re:关于继承的深入讨论 [Re: jameszhang] Copy to clipboard
Posted by: hxz5830
Posted on: 2005-08-15 18:38

result:test1
test2(验证过的)
过程是:生成一个子类对象Test2,上传给父类Test1。好象调用函数getStr()时,是子类对象作用;而调用 Str时,又体现成父类对象。不是太清楚, 请高手指教

4.Re:关于继承的深入讨论 [Re: jameszhang] Copy to clipboard
Posted by: ww1ww1
Posted on: 2005-08-15 20:35

有人能说why吗。。。

5.Re:关于继承的深入讨论 [Re: jameszhang] Copy to clipboard
Posted by: jigsaw
Posted on: 2005-08-15 22:12

看看javap便知
常识

6.Re:关于继承的深入讨论 [Re: jameszhang] Copy to clipboard
Posted by: whyuaou
Posted on: 2005-08-16 09:17


运行结果和楼上的一样
Test1
Test2
不知道这样的理解有没有问题,还请高手们指教啊!

7.Re:关于继承的深入讨论 [Re: jameszhang] Copy to clipboard
Posted by: ww1ww1
Posted on: 2005-08-16 10:03

FoodFoodFoodFoodFoodFoodFoodFood

public interface Animals {
void run();
}

public class Cat implements Animals {
private String category;
public Cat() {
this.category = "cat";
}
public run() {
System.out.println(this.category +" is running.");
}

public static void main(String[] args) {
Animals something = new Cat();
something.run();
}
}


如果interface也是一种class的话,那么something是子类Cat的实例,在interface中讲run() implements(实现)了Animals的run()方法,如果Animals是solid class那么就是override(重定义)了父类的run()方法

大多数人会认为System.out.println(t1.str);结果是test2
关键是System.out.println(t1.str);这个地方怎么理解。。。

但是如果将interface变成solid class
你会发现一个编译错误DevilDevilDevilDevilDevilDevilDevilDevilDevilDevilDevilDevilDevil

public class Animals {
public String category = "animals";
public void run() {
System.out.println( this.category + " is running.");
}
}

public class Cat extends Animals {
public String category;
public Cat() {
this.category = "cat";
}
public void run() {
System.out.println(this.category +" is running.");
}
public String getCat() {
return category;
}
public static void main(String[] args) {
Animals something = new Cat();
something.run();
something.getCat();
}
}


StupidStupidStupidStupidStupidStupidStupidStupidStupidStupidStupidStupid

8.Re:关于继承的深入讨论 [Re: whyuaou] Copy to clipboard
Posted by: jameszhang
Posted on: 2005-08-16 19:25

whyuaou wrote:

运行结果和楼上的一样
Test1
Test2
不知道这样的理解有没有问题,还请高手们指教啊!

有点自相矛盾,前面说 t1 是 Test1的实例,后面又说t1调用的是 Test2里面的方法

9.Re:关于继承的深入讨论 [Re: jameszhang] Copy to clipboard
Posted by: ww1ww1
Posted on: 2005-08-16 20:38

很少将类里面的成员变量public用的,一般都是private,然后get出来用的,所以较少碰到 t1.str 的问题。

另外发现,如果 Test2中有Test1中没有的方法例如:getSomething()


Test1 t1 = new Test2();
t1.getSomething(); //这个方法存在于Test2中,不存在于Test1中


会报错误,说 getSomething()没有在Test1中定义。。。StupidStupidStupidStupidStupid

10.Re:关于继承的深入讨论 [Re: jameszhang] Copy to clipboard
Posted by: breezehou
Posted on: 2005-08-17 12:58

只有方法才能被overridden,而成员变量不可以,所以t1.getStr());调用的是子类的方法。

11.Re:关于继承的深入讨论 [Re: jameszhang] Copy to clipboard
Posted by: whyuaou
Posted on: 2005-08-17 14:12

jameszhang wrote:
有点自相矛盾,前面说 t1 是 Test1的实例,后面又说t1调用的是 Test2里面的方法

不矛盾啊,Test1里的getstr()方法被Test2的getstr()覆盖了啊,所以t1 调用的是Test2里的getstr()方法啊。

12.Re:关于继承的深入讨论 [Re: jameszhang] Copy to clipboard
Posted by: jokehan
Posted on: 2005-08-17 15:56

字段是不能重写的,只有方法能重写!
楼主例子例子里,'t1'的型别在“编译期”就已经确定了是'Test1',而在“运行期”被向下转型为它的派生类'Test2',所以这时't1'里的'getStr()'方法被重写了,因此当调用't1.str'时,因字段不能重写,所以返回的还是'Test1'里的'str',而当调用't1.getStr()'时,因'Test1'里的'getStr()'方法被'Test2'重写了,所以调用的是被重写后的'getStr()'方法,也就是'Test2'里的'getStr()'方法,并返回内联字段'str',也就是'Test2'里的'str' :)

13.Re:关于继承的深入讨论 [Re: ww1ww1] Copy to clipboard
Posted by: ww1ww1
Posted on: 2005-08-17 17:20

ww1ww1 wrote:

Test1 t1 = new Test2();
t1.getSomething(); //getSomething()这个方法存在于Test2中,不存在于Test1中

编译报错,说 getSomething()没有在Test1中定义。。。StupidStupidStupidStupidStupid


t1不是Test2的实例吗,它怎么不可以调用Test2的方法。。。难道。。。
深度套牢中。。。。。
Thumbs downThumbs downThumbs downThumbs downThumbs downThumbs downThumbs downThumbs down

14.Re:关于继承的深入讨论 [Re: breezehou] Copy to clipboard
Posted by: jameszhang
Posted on: 2005-08-17 21:09

breezehou wrote:
只有方法才能被overridden,而成员变量不可以,所以t1.getStr());调用的是子类的方法。

真有人用类的特性来解释,呵呵,“成员变量不可以“ 那么t2.str 的值说明什么?

15.Re:关于继承的深入讨论 [Re: jokehan] Copy to clipboard
Posted by: jameszhang
Posted on: 2005-08-17 21:41

jokehan wrote:
字段是不能重写的,只有方法能重写!
楼主例子例子里,'t1'的型别在“编译期”就已经确定了是'Test1',而在“运行期”被向下转型为它的派生类'Test2',所以这时't1'里的'getStr()'方法被重写了,因此当调用't1.str'时,因字段不能重写,所以返回的还是'Test1'里的'str',而当调用't1.getStr()'时,因'Test1'里的'getStr()'方法被'Test2'重写了,所以调用的是被重写后的'getStr()'方法,也就是'Test2'里的'getStr()'方法,并返回内联字段'str',也就是'Test2'里的'str' :)

好哇! 快接近了,虽然从编译和运行期来看是这样,但为什么,也许你会说面向对象中的继承就是这样的,那好我们来看C++ 中是怎样的(请斑竹让我在java板块中写段C++代码,谢谢)
----------------------------------------------------
TestA.h

class TestA
{
public :
  int i;

public :
  int getI();
  TestA();
  TestA(int i);
  
};


TestB.h
#include "TestA.h"

class TestB :public TestA
{
public :
  int i;

public :
  int getI();
  TestB();
  TestB(int i);

};


TestA.cpp
#include "TestA.h"

TestA::TestA()
{
}
TestA::TestA(int i)
{
  this->i= i;
}

int TestA::getI()
{
  return this->i;
}


TestB.cpp

#include <iostream.h>

#include "TestB.h"

TestB::TestB()
{

}

int TestB::getI()
{
  return this->i;
}

TestB::TestB(int i)
{
  this->i = i;
}

void main()
{
  TestA *ta = new TestB(8);
  TestB *tb = new TestB(6);
  

  cout<<ta->i<<endl;
  cout<<ta->getI()<<endl;
  cout<<tb->i<<endl;
  cout<<tb->getI()<<endl;

  ta->i = 10;
  cout<<ta->i<<endl;
  cout<<ta->getI()<<endl;

  
}


只是为了举例子所以写的很简单,在vc6上跑的结果是

16.Re:关于继承的深入讨论 [Re: jameszhang] Copy to clipboard
Posted by: jameszhang
Posted on: 2005-08-20 10:09

其实这是个我们经常会忽略的语言中的概念 “隐藏规则“

17.Re:关于继承的深入讨论 [Re: jameszhang] Copy to clipboard
Posted by: jameszhang
Posted on: 2005-08-28 14:29

这样做的好处实现动态绑定,如果按这样隐藏规则,java 能实现 动态绑定 ,而C++不能实现了, 动态绑定 又是多态 实现重要部分 , C++ 是 面向对象的语言,也是非常灵活的语言怎么会不支持动态绑定,哈哈,C++如果改变 隐藏规则,只需要在 基类的 函数前面加上 virtual,就跟java的实现一样了,呵呵

18.加精 [Re: jameszhang] Copy to clipboard
Posted by: ww1ww1
Posted on: 2005-08-28 23:13

Thumbs upThumbs upThumbs upThumbs upThumbs upThumbs upThumbs upThumbs upThumbs upThumbs up

19.Re:关于继承的深入讨论 [Re: jameszhang] Copy to clipboard
Posted by: hxz5830
Posted on: 2005-08-29 15:03

jokehan wrote:
字段是不能重写的,只有方法能重写!
字段也可以重写吧.

ww1ww1 wrote:
Test1 t1 = new Test2();
t1.getSomething(); //getSomething()这个方法存在于Test2中,不存在于Test1中
编译报错,说 getSomething()没有在Test1中定义。。。
t1不是Test2的实例吗,它怎么不可以调用Test2的方法。。。难道。。。

这样理解应该t1是Test1的实例,而不是Test2的实例.对吗?
楼主能说下t1是Test1的实例,还是Test2的实例,还是不能这样理解?

20.Re:关于继承的深入讨论 [Re: jameszhang] Copy to clipboard
Posted by: jameszhang
Posted on: 2005-08-29 21:21

= 的左边 是个变量 右边 是在堆里 分配的空间的 地址 左边 和 右边 要用隐藏规则来看

21.Re:关于继承的深入讨论 [Re: hxz5830] Copy to clipboard
Posted by: ww1ww1
Posted on: 2005-08-29 23:02

hxz5830 wrote:
jokehan wrote:
字段是不能重写的,只有方法能重写!
字段也可以重写吧.

ww1ww1 wrote:
Test1 t1 = new Test2();
t1.getSomething(); //getSomething()这个方法存在于Test2中,不存在于Test1中
编译报错,说 getSomething()没有在Test1中定义。。。
t1不是Test2的实例吗,它怎么不可以调用Test2的方法。。。难道。。。

这样理解应该t1是Test1的实例,而不是Test2的实例.对吗?
楼主能说下t1是Test1的实例,还是Test2的实例,还是不能这样理解?


这个东西好多书好像都没讲的,属于盲区。。。

22.Re:关于继承的深入讨论 [Re: jameszhang] Copy to clipboard
Posted by: ccikk
Posted on: 2005-08-31 10:53

core java 的polymorphism描述中,明确说了,java的继承中,对象方法调用是late binding:object-oriented languages use the concept of late binding. When you send a message to an object, the code being called isn’t determined until run time. The compiler does ensure that the method exists and performs type checking on the arguments and return value (a language in which this isn’t true is called weakly typed), but it doesn’t know the exact code to execute

还举了例子:
void doStuff(Shape s) {
s.erase();
// ...
s.draw();
}

Circle c = new Circle();
Triangle t = new Triangle();
Line l = new Line();
doStuffCoffee;
doStuffPhone;
doStuff(l);

而对class的成员变量,则使用early binding。只会根据变量的类型,来获取成员变量的值。
所以楼主的问题就很明确了。

23.Re:关于继承的深入讨论 [Re: jameszhang] Copy to clipboard
Posted by: ccikk
Posted on: 2005-08-31 11:14

我觉得楼上的朋友用C++对比来说明很能说明问题,我也试了一下:
#include "stdio.h"

class TestA {
public:
  int m;

  TestA() {
    m = 1;
  }

  virtual int getM() {
    return m;
  }
};

class TestB : public TestA {
  
public:
  int m;

  TestB() {
    m = 2;
  }

  int getM() {
    return m;
  }
};

void main() {
  
  TestA *a = new TestB();
  
  printf("%d\r\n", a->m);
  printf("%d\r\n", a->getM());
}

打印结果:
1
2

如果把TestA中getM的virtual修饰符号去掉,
打印结果:
1
1

这样我们就明白了,java的默认方式是lata binding(也叫动态帮顶),而C++默认的是静态绑定,除非使用了virtual修饰符。
还有,无论c++还是java,对成员变量都不能动态绑定的。

24.Re:关于继承的深入讨论 [Re: jameszhang] Copy to clipboard
Posted by: zcjl
Posted on: 2005-09-05 21:14

《Java Pitfalls》 Item 1、5、6有说明:隐藏(Hidden)和覆盖(Overridden)的区别


   Powered by Jute Powerful Forum® Version Jute 1.5.6 Ent
Copyright © 2002-2021 Cjsdn Team. All Righits Reserved. 闽ICP备05005120号-1
客服电话 18559299278    客服信箱 714923@qq.com    客服QQ 714923