Example 1
这个例子中,Horse类继承了Animal,在实例化过程中,分别用Animal new了Animal,Horse new了Horse以及Animal new了Horse。经过测试可以发现,在方法调用上使用的分别是定义时类的方法,因为在传入到UseAnimal时是根据其定义时的类,例如Animal a = new Horse,即使实际上是Horse,但是编译时会传到第一个方法,其他的也是这样。
记住一点,重载是在编译阶段完成,重写于运行时完成。
Example 2
class Animal {
public void eat() {
System.out.println("I'm an animal. I like eating everything!");
}
}
class Horse extends Animal {
public void eat(String food) {
System.out.println("I'm a horse. I like eating "+ food);
}
public void eat() {
System.out.println(“I‘m a horse. I like eating grass!"
}
}
为了更好的区分,下面举几个例子:
Animal a = new Animal(); a.eat(); =>I’m an animal. I like eating everything!
Horse h = new Horse(); h.eat();=>I’m a horse. I like eating grass!
Animal ah = new Horse(); ah.eat();=>I’m a horse. I like eating grass!
这个例子这是多态性的体现,和Example 1有很好的对照,会调用实际类的方法。
Horse he = new Horse(); he.eat(“Apples!”);=>I’m a horse. I like eating Apples! The overloaded eat(String s) method in Horse is invoked.
Animal a2 = new Animal(); a2.eat(“Carrots”);=>Compiler error! Animal class doesn’t have an eat() method that takes a String
Animal ah2 = new Horse(); ah2.eat(“Carrots”);=>Compiler error!
这个例子也要特别注意,作为Animal类实例化,没有接受String的eat方法。
Example 3
第一个显然是不会编译通过的,接口无法实例化。
第二个会调用Dog类的方法,第三个会调用Cow类的方法。
注意第四个是无法调用成功的,因为Animal接口中不存在这个方法。
Example 4
public static void main(String[] args) {
A a = new A();
System.out.println(a.getClass());
A b = new B();
System.out.println(b.getClass());
}
再看这个例子,其中B是A的子类,结果如下:
class A
class B
可以发现,getClass能够返回其实际的类型。
Summary
搞清楚了上面几个例子就能避免很多错误和误区。
把握一个原则,重写方法后调用会执行子类的方法,除非在子类内部使用super.method(),无论将其作为什么定义(父类或子类)。
而如果调用了如上述Animal,即父类没有,子类的有的方法,无论是新的方法还是重载的方法都是不会成功的,因为编译时还是认为它是Animal。
在getClass时,能够返回其实际的类,但传参时会根据其定义的类(参考Example 1)。