一、向上转型
//对于一个继承自 Animal 类的 Dog 类,可以写成:
Dog dog = new Dog("小汪");
Animal animal = dog;
//或写成:
Animal animal = new Dog("小汪");
2)方法传参:
public class Test1_21{
public static void main(String[] args) {
Dog dog = new Dog("小汪");
feed(dog);
}
public static void feed(Animal animal) {
animal.eat("狗粮");
}
}
//执行结果
小汪正在吃狗粮
3)方法返回:
public class Test1_21 {
public static void main(String[] args) {
Animal animal = findMyAnimal();
}
public static Animal findMyAnimal() {
Dog dog = new Dog("小汪");
return dog;
}
}
二、动态绑定
三、方法重写
子类实现父类的同名方法,并且参数的类型和个数完全相同,这种情况称为覆写/重写/覆盖(Override)。因为对于已经投入使用的类,尽量不要对其进行修改。因此我们可以重新定义一个新的类,来重复利用其中共性的内容,并且可以根据要求添加或者修改其中的内容。事实上,方法重写是 Java 语法层次上的规则,而动态绑定是方法重写这个语法规则的底层实现,两者本质上描述的是相同的事情,只是侧重点不同。
• 注意:普通方法可以重写,static 修饰的静态方法不能重写;重写中子类的方法的访问权限不能低于父类的方法访问权限;重写的方法返回值类型不一定和父类的方法相同。并且推荐在代码中进行重写方法时给子类的方法前显式加上 @Override 注解,有了这个注解能帮我们进行一些合法性校验。
※区别重载和重写:重载是在同一个作用域(一个类)中,方法名称相同且参数的类型及个数不同;重写是在继承关系中,方法名称、(返回值类型如果和父类的返回值类型也是父子关系也可以在重写方法时不与父类的返回值类型完全相同)、参数的类型及个数完全相同,并且被覆写的方法不能拥有比父类更严格的访问控制权限。
四、理解多态
有了面的向上转型、动态绑定、方法重写之后,我们就可以使用 多态(polypeptide) 的形式来设计程序了。多态就是一个引用能对应到多种形态(即不同类型的实例)。
※使用多态的好处:
1. 类调用者对类的使用成本进一步降低。封装是让类的调用者不需要知道类的实现细节,多态能让类的调用者连这个类的类型是什么都不必知道,只需要知道这个对象具有某个方法即可。因此, 多态可以理解成是封装的更进一步,让类调用者对类的使用成本进一步降低。
2. 可扩展能力更强。如果要新增一种新的类,使用多态的方式代码改动成本也比较低。因为对于类的调用者来说只需要创建一个新类的实例就可以了,改动成本很低。
//不基于多态实现打印多个形状:
public static void drawShapes() {
Rect rect = new Rect();
Cycle cycle = new Cycle();
Flower flower = new Flower();
String[] shapes = {"cycle", "rect", "cycle", "rect", "flower"};
for (String shape : shapes) {
if (shape.equals("cycle")) {
cycle.draw();
} else if (shape.equals("rect")) {
rect.draw();
} else if (shape.equals("flower")) {
flower.draw();
}
}
}
//使用多态实现:
public static void drawShapes() {
Shape[] shapes = {new Cycle(), new Rect(), new Cycle(),
new Rect(), new Flower()};
for (Shape shape : shapes) {
shape.draw();
}
}
五、向下转型
//向上转型:
Animal animal = new Dog("小汪");
//向下转型:
Dog dog = (Dog)animal;
向下转型是向上转型的可逆操作,如果当前需要调用的方法只在子类中而不在父类中存在,那么就可以先向下转型成子类对象再调用该方法。但要注意对象之间的类型一定要匹配才可以,不然运行时就会报错。可以用 instanceof 判定一个引用是否是某个类的实例。如果是则返回 true,这时再进行向下转型就比较安全了。
!注意:尽量不要在构造方法中调用方法(如果这个方法被子类重写过,就会触发动态绑定,但是此时子类对象还没构造完成),可能就会出现一些隐藏的但是又极难发现的问题。