0 0

关于Java继承和多态的问题!!!5

最近看到两个Java基础题,自己搞了半天糊里糊涂的,看来自己Java基础还有很多理解不到位,希望高手给俺讲讲!
class A {}
class B extends A {
  void print() {
    System.out.println(super.getClass().getName());
  }
  public static void main(String[] args) {
    B b = new B();
    b.print();
  }
}

输出结果是什么?为什么?如果你懂了再讲讲下面这个问题:
class A {
  int i = 1;
  void print() {
    System.out.println(i);
  }
}
class B extends A {
  int i = 2;
  public static void main(String[] args) {
    B b = new B();
    b.print();
  }
}

俺不是想问输出结果,想问为什么会输出这样的结果?


问题补充:嗯,你回答的似乎没错,但好像不是用Java语言本身定义的一些规则来解释,感觉像是在看着答案自圆其说,看了之后还是模模糊糊,无法让俺理解透彻和信服。不过还是谢谢你!!!

问题补充:
shjavaer 写道
前面几个说的很对,可能你没看懂

第一个其实是动态绑定

你可能认为super是A,它的getClass返回是A.class,所以getName应该是A,这里的问题是super确实是A,但A,B都是object的子类,getClass又是object中的final方法,其返回的是Runtime class,这是规范定义好了的,而你的runtime class的实例是B,所以返回的class对象是B.class


第二个其实就是作用域问题
子类B中i覆盖了父类A中i,要想操作A中的i,就要用super.i进行显示操作,否则默认操作B中的i,就跟买烟一样,你说买红双喜,可能人家老板给你拿一盒10元红双喜,可你就喜欢5块钱的红双喜,所以你下次就会说买5元红双喜




第二个问题的输出结果是2,你好像打错了吧!!!

问题补充:
shjavaer 写道
前面几个说的很对,可能你没看懂

第一个其实是动态绑定

你可能认为super是A,它的getClass返回是A.class,所以getName应该是A,这里的问题是super确实是A,但A,B都是object的子类,getClass又是object中的final方法,其返回的是Runtime class,这是规范定义好了的,而你的runtime class的实例是B,所以返回的class对象是B.class


第二个其实就是作用域问题
子类B中i覆盖了父类A中i,要想操作A中的i,就要用super.i进行显示操作,否则默认操作B中的i,就跟买烟一样,你说买红双喜,可能人家老板给你拿一盒10元红双喜,可你就喜欢5块钱的红双喜,所以你下次就会说买5元红双喜


我也晕了,第二个问题的输出结果是1!!! 看来你也理解不到位啊

问题补充:
shjavaer 写道

引用
我也晕了,第二个问题的输出结果是1!!! 看来你也理解不到位啊



我没有就题论题,扯远了,其实主要想说的是 第二个其实就是作用域问题

呵呵,别误会,"就题论题..."那个是之前说一楼的。
不过你第二个问题确实回答错了,输出结果是A中的i为1。而你的意思是会输出B中的i为2......

问题补充:
longxiaoyan 写道
估计你没有看我的回答,第二个问题其实就是:子类定义与父类同名的成员变量,并没有覆盖父类的成员变量,而是两个成员变量共存。

嗯,是的,继承中不存在属性或者说实例变量的覆盖,只有方法的覆盖。

我的理解第二题,实例一个B对象,并调用print()方法。但B中并没有覆盖A中的print()方法,故实例化B之后,print()方法还是在A中的,并不会成为B的一部分,B的对象只能调用父类中的print()方法,或者说只有访问的权利,而print()方法并不是自身一部分。

简单点不知道可不可以这样理解:由于B没有覆盖A的print()方法,实际上B对象调用print()方法就是跳到A中执行print()方法,所以所访问的属性也就理所当然的是A中的i属性。

假如B中覆盖了A中的print()方法,一切都变得很简单了......
不知道这样理解是否正确,赐教~

问题补充:
longxiaoyan 写道
当B中没有覆盖print方法的时候,调用的是A中的print方法,加个this或许能更加容易理解:
class A { 
  int i = 1; 
  void print() { 
    System.out.println(this.i); 
  } 
}

当B覆盖print方法的时候,可以这样写:
void print() {
  System.out.println(this.i);
  System.out.println(super.i);
}

还有就是多态3要素:有继承,有重写,有父类引用指向子类对象。如果B覆盖了方法print,这样写就是多态了:
A a = new B();
a.print();//这里调用的就是子类的方法,多态的体现

理解这个才是重要的。

嗯,第二题理解的差不多了,说说第一题,getClass()方法的原型是:
public final native Class<?> getClass();
这是一个final方法,不能被继承,并且是一个native方法,所以不能用第二题的逻辑去解释,否则用那个逻辑解释下去就会输出“Object”。
这里就理解为它就是一个死的规定,死的约定,Java虚拟机执行这个方法时,就只返回调用处所在的类的Class对象。就是无论谁调用,就是返回调用者所在的类的Class对象,再用getName()方法返回这个Class对象名字,自然结果就是“B”,这样理解行否?

问题补充:
longxiaoyan 写道
对,就这样理解,就把它当成一个方法,返回就看它API所说的,其中的实现方法不用深究了。因为这里没有重写,调用的就是Object中的本地方法。

嗯,我又多次验证了一下,确实这个getClass()方法只依赖于调用者对象,就是说如果:
A中:
void print() {
  System.out.println(super.getClass().getName());
}

然后在B的main方法中:
如果
B b = new B();b.print();
打印结果为“B"。

如果
A a = new B();a.print();
打印结果为“B”

如果
A a = new A();
a.print();
打印结果为“A”。

通过几次测试,发现这个getClass()依赖于调用者实例,就是运行时对象(Runtime Class)。就是说getClass()方法,不论他在哪儿被使用,不论什么super、this指着,他只认最终调用者的身份。。。
呵呵,转了半天其实还是这句话:“运行时对象”,不过没有这一大圈的折腾,不会理解透彻这句话。。。
2011年12月30日 10:55

10个答案 按时间排序 按投票排序

0 0

采纳的答案

第一个问题:
getClass在Object中的定义为 public final native Class<?> getClass();既然定义为final说明这个方法可以继承但不能重写,既然没有重写,那么调用this.getClass()和调用

super.getClass()都是一样调用Object中的本地方法getClass()。所以我们只需知道getClass()是怎么实现的就可以了,但这是一个本地方法,我们没法看见源代码,那就看API:
Returns the runtime class of this {@code Object}.
这是API里的一句话,the runtime class当然是B了。

第二个问题:
这个问题无非就是考查能否继承实例变量?这里我先把Class A的代码变成这样:
class A { 
  int jjj = 1; 
  void print() { 
    System.out.println(jjj); 
  } 
}
我就是把变量名给变了一下而已,这里你肯定能理解了。剩下的问题就是子类定义与父类同名的成员变量时是个什么情况?子类定义与父类同名的成员变量,并没有覆盖父类的成

员变量,而是两个成员变量共存。再把Class B代码修改一下:
class B extends A {
int i = 2;

void print() {
System.out.println(i);
System.out.println(super.i);
}

public static void main(String[] args) {
B b = new B();
b.print();
}
}
明白了吧。

2011年12月30日 14:52
0 0

对,就这样理解,就把它当成一个方法,返回就看它API所说的,其中的实现方法不用深究了。因为这里没有重写,调用的就是Object中的本地方法。

2011年12月31日 17:44
0 0

当B中没有覆盖print方法的时候,调用的是A中的print方法,加个this或许能更加容易理解:
class A { 
  int i = 1; 
  void print() { 
    System.out.println(this.i); 
  } 
}

当B覆盖print方法的时候,可以这样写:
void print() {
  System.out.println(this.i);
  System.out.println(super.i);
}

还有就是多态3要素:有继承,有重写,有父类引用指向子类对象。如果B覆盖了方法print,这样写就是多态了:
A a = new B();
a.print();//这里调用的就是子类的方法,多态的体现

理解这个才是重要的。

2011年12月31日 17:15
0 0

你理解的没问题,但不会举一反三

2011年12月31日 16:39
0 0

估计你没有看我的回答,第二个问题其实就是:子类定义与父类同名的成员变量,并没有覆盖父类的成员变量,而是两个成员变量共存。

2011年12月31日 16:38
0 0


引用
我也晕了,第二个问题的输出结果是1!!! 看来你也理解不到位啊



我没有就题论题,扯远了,其实主要想说的是 第二个其实就是作用域问题

2011年12月31日 16:15
0 0

前面几个说的很对,可能你没看懂

第一个其实是动态绑定

你可能认为super是A,它的getClass返回是A.class,所以getName应该是A,这里的问题是super确实是A,但A,B都是object的子类,getClass又是object中的final方法,其返回的是Runtime class,这是规范定义好了的,而你的runtime class的实例是B,所以返回的class对象是B.class


第二个其实就是作用域问题
子类B中i覆盖了父类A中i,要想操作A中的i,就要用super.i进行显示操作,否则默认操作B中的i,就跟买烟一样,你说买红双喜,可能人家老板给你拿一盒10元红双喜,可你就喜欢5块钱的红双喜,所以你下次就会说买5元红双喜

2011年12月31日 00:12
0 0

第一个问题上面两位已经解释清楚了,实际上就是一个"动态绑定"问题,java默认返回返回的是当前类。

第二个问题。表面上是一个名字迷惑问题。这个问题在实际编程中几乎不存在,但提出这个问题有助于理解java对象初始化。在java中new一个对象时从父类开始,所有父类
的变量都将被初始化,名字只是变量在程序中的一个标识,在类存中变量他们都具有自己的地址,是拿地址来区分变量的,不是那名字来区分。这个b对象实际拥有两个名字为i的变量一个是父类的i=1,自身的i=2,在java中每个类中方法调用自己本身的变量,java为了不混淆,在子类对象调用父类的变量时需要super.i,并且是直接父类的i。
看看下面的代码你就明白了:

public class C extends B {

	  int i = 3;   
	  public static void main(String[] args) {   
	    B b = new C();   
	    b.print();   
	  }  
	  void print() {
		super.print();
	    System.out.println(super.i);
	  }
}

结果:
1
2
	    B b = new C();   


这里不管是A,B,C谁来引用实例C,都是动态绑定到C中的print
System.out.println(super.i);

没有输出顶层父类中的变量 而是C的直接父类B中的2.

2011年12月30日 19:42
0 0

第一个问题:
getClass在Object中的定义为 public final native Class<?> getClass();既然定义为final说明这个方法可以继承但不能重写,既然没有重写,那么调用this.getClass()和调用super.getClass()都是一样调用Object中的本地方法getClass()。所以我们只需知道getClass()是怎么实现的就可以了,但这是一个本地方法,我们没法看见源代码,那就看API:
Returns the runtime class of this {@code Object}.
这是API里的一句话,the runtime class当然是B了。

第二个问题:
这个问题无非就是考查能否继承实例变量?这里我先把Class A的代码变成这样:
class A { 
  int jjj = 1; 
  void print() { 
    System.out.println(jjj); 
  } 
}
我就是把变量名给变了一下而已,这里你肯定能理解了。剩下的问题就是子类定义与父类同名的成员变量时是个什么情况?子类定义与父类同名的成员变量,并没有覆盖父类的成员变量,而是两个成员变量共存。再把Class B代码修改一下:
class B extends A {
int i = 2;

void print() {
System.out.println(i);
System.out.println(super.i);
}

public static void main(String[] args) {
B b = new B();
b.print();
}
}
明白了吧。

2011年12月30日 14:54
0 0

先回答第一个问题:
(1)输出结果是B
因为,你new了一个b对象,调用print方法,打印super.getClass().getName(),在这里super指的是B的父类A,但是A类没有明确复写方法getClass(),所以这个地方真正调用的是Object类的getClass()方法,这个方法就是返回当前类B,因此getClass().getName()就是"B"。

(2)输出结果是1
new了一个b对象,调用print方法,由于当前B类没有明确定义print方法,因此会调用父类A的print方法,A中print方法打印的是A中的变量i,而不是B中的i。再说的具体一点就是,创建对象b后,调用一个父类的方法,因为方法没有声明为static类型,所有就得创建父类A的一个对象a,然后调用 a.print(),自然会打印a对象中的变量i。

不知道我是否把问题解释明白了,呵呵。
解释错误的地方,还请批评指教,大家一起学习!

2011年12月30日 11:49

相关推荐

Global site tag (gtag.js) - Google Analytics