如上所示例程1,输出结果如下:
A and A
A and A
A and D
B and A
B and A
A and D
B and B
B and B
A and D
理解各个输出,关键理解点在继承与多态,我们知道,对一个引用变量而言,编译时,变量与其所声明的类型绑定,而运行环境中,其和实际引用类型绑定,所以上述例程中,引用变量a2编译时是和A绑定,但在运行时引用的是B的对象,依据如此理解,或许有人觉得a2.show(b)理当输出“B and B”才对,但实际输出的却是“B and A”,也就是说a2.show(b)实际上调用的是class B的show (A obj)方法,为什么不调用class B的show (B obj)方法呢,细揣摩之,如此理解,未知确否。
所谓编译时和声明类型绑定,编译器在编译a2.show(B)时,认为该方法调用的是A的show(A)方法,因为A类里只有两个方法,参数b是B的实例,B继承自A,所以理应在类A的两个方法里决定调用show(A);那么实际运行的时候,java虚拟机运行环境究竟决定怎么调用这个方法呢,这就用到了重载和重写的概念,a2实际上是B的一个实例,B从A继承了show(A)的方法,虚拟机判断B对方法show(A)进行了重写,所以就调用被重写了的show(A)方法,于是输出“B and A”。
我们对上面的程序稍作修改,进一步验证一下我们的推论。
如下例程2,更换了一行代码
输出:
O and A
O and A
A and D
O and A
O and A
A and D
B and B
B and B
A and D
依据上述原理,验证通过。
这里又发现一个值得注意的问题,编译器在根据参数决定调用那个方法的时候,编译器总是将和参数声明类型最相近的那一个方法作为调用方法。比如例程2的最后一个,b.show(d),在B的方法中(包含继承自父类的),四个方法均参数符合调用要求,编译器选择的是A类中的show(D d)。我们不妨称之为“就近原则”。
[color=red][b]欢迎指正学习。[/b][/color]
public class Extendc {
class A {
public String show(D obj){
return ("A and D");
}
public String show(A obj){
return ("A and A");
}
}
class B extends A{
public String show(B obj){
return ("B and B");
}
public String show(A obj){
return ("B and A");
}
}
class C extends B{}
class D extends B{}
class E{}
public static void main(String[] args){
Extendc et=new Extendc();
Extendc.A a1= et.new A();
A a2 = et.new B();
B b = et.new B();
C c = et.new C();
D d = et.new D();
System.out.println(a1.show(b));
System.out.println(a1.show(c));
System.out.println(a1.show(d));
System.out.println(a2.show(b));
System.out.println(a2.show(c));
System.out.println(a2.show(d));
System.out.println(b.show(b));
System.out.println(b.show(c));
System.out.println(b.show(d));
}
}
如上所示例程1,输出结果如下:
A and A
A and A
A and D
B and A
B and A
A and D
B and B
B and B
A and D
理解各个输出,关键理解点在继承与多态,我们知道,对一个引用变量而言,编译时,变量与其所声明的类型绑定,而运行环境中,其和实际引用类型绑定,所以上述例程中,引用变量a2编译时是和A绑定,但在运行时引用的是B的对象,依据如此理解,或许有人觉得a2.show(b)理当输出“B and B”才对,但实际输出的却是“B and A”,也就是说a2.show(b)实际上调用的是class B的show (A obj)方法,为什么不调用class B的show (B obj)方法呢,细揣摩之,如此理解,未知确否。
所谓编译时和声明类型绑定,编译器在编译a2.show(B)时,认为该方法调用的是A的show(A)方法,因为A类里只有两个方法,参数b是B的实例,B继承自A,所以理应在类A的两个方法里决定调用show(A);那么实际运行的时候,java虚拟机运行环境究竟决定怎么调用这个方法呢,这就用到了重载和重写的概念,a2实际上是B的一个实例,B从A继承了show(A)的方法,虚拟机判断B对方法show(A)进行了重写,所以就调用被重写了的show(A)方法,于是输出“B and A”。
我们对上面的程序稍作修改,进一步验证一下我们的推论。
如下例程2,更换了一行代码
public class Extendc {
class A {
public String show(D obj){
return ("A and D");
}
public String show(Object obj){
return ("O and A");
}
}
class B extends A{
public String show(B obj){
return ("B and B");
}
public String show(A obj){
return ("B and A");
}
}
class C extends B{}
class D extends B{}
public static void main(String[] args){
Extendc et=new Extendc();
Extendc.A a1= et.new A();
A a2 = et.new B();
B b = et.new B();
C c = et.new C();
D d = et.new D();
System.out.println(a1.show(b));
System.out.println(a1.show(c));
System.out.println(a1.show(d));
System.out.println(a2.show(b));
System.out.println(a2.show(c));
System.out.println(a2.show(d));
System.out.println(b.show(b));
System.out.println(b.show(c));
System.out.println(b.show(d));
}
}
输出:
O and A
O and A
A and D
O and A
O and A
A and D
B and B
B and B
A and D
依据上述原理,验证通过。
这里又发现一个值得注意的问题,编译器在根据参数决定调用那个方法的时候,编译器总是将和参数声明类型最相近的那一个方法作为调用方法。比如例程2的最后一个,b.show(d),在B的方法中(包含继承自父类的),四个方法均参数符合调用要求,编译器选择的是A类中的show(D d)。我们不妨称之为“就近原则”。
[color=red][b]欢迎指正学习。[/b][/color]