最近看到两个Java基础题,自己搞了半天糊里糊涂的,看来自己Java基础还有很多理解不到位,希望高手给俺讲讲!
[code="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();
}
}
[/code]
输出结果是什么?为什么?如果你懂了再讲讲下面这个问题:
[code="java"]
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();
}
}
[/code]
俺不是想问输出结果,想问为什么会输出这样的结果?
第一个问题:
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();
}
}
明白了吧。
先回答第一个问题:
(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。
不知道我是否把问题解释明白了,呵呵。
解释错误的地方,还请批评指教,大家一起学习!
第一个问题:
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();
}
}
明白了吧。
第一个问题上面两位已经解释清楚了,实际上就是一个"动态绑定"问题,java默认返回返回的是当前类。
第二个问题。表面上是一个名字迷惑问题。这个问题在实际编程中几乎不存在,但提出这个问题有助于理解java对象初始化。在java中new一个对象时从父类开始,所有父类
的变量都将被初始化,名字只是变量在程序中的一个标识,在类存中变量他们都具有自己的地址,是拿地址来区分变量的,不是那名字来区分。这个b对象实际拥有两个名字为i的变量一个是父类的i=1,自身的i=2,在java中每个类中方法调用自己本身的变量,java为了不混淆,在子类对象调用父类的变量时需要super.i,并且是直接父类的i。
看看下面的代码你就明白了:
[code="java"]
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);
}
}
[/code]
结果:
1
2
[code="java"]
B b = new C();
[/code]
这里不管是A,B,C谁来引用实例C,都是动态绑定到C中的print
[code="java"]
System.out.println(super.i);
[/code]
没有输出顶层父类中的变量 而是C的直接父类B中的2.
前面几个说的很对,可能你没看懂
第一个其实是动态绑定
你可能认为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元红双喜
[quote]我也晕了,第二个问题的输出结果是1!!! 看来你也理解不到位啊 [/quote]
:?
我没有就题论题,扯远了,其实主要想说的是 [color=red]第二个其实就是作用域问题[/color]
估计你没有看我的回答,第二个问题其实就是:子类定义与父类同名的成员变量,并没有覆盖父类的成员变量,而是两个成员变量共存。
你理解的没问题,但不会举一反三
当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();//这里调用的就是子类的方法,多态的体现
理解这个才是重要的。
对,就这样理解,就把它当成一个方法,返回就看它API所说的,其中的实现方法不用深究了。因为这里没有重写,调用的就是Object中的本地方法。