多态
面向对象的计算机编程语言的三大特点:
继承,封装,多态
- 多态:一个引用,能表现出多种形态。
- 多态的条件:
- 继承重写 即子类实现父类的同名方法,且参数类型与参数个数与父类同名方法完全相同。
①. 重写注意事项:
重写的方法不能是private
重写中子类的方法的访问权限不能低于父类的方法访问权限.
普通方法可以重写, static 修饰的静态方法不能重写.
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println("我是一只小动物");
System.out.println(this.name + "正在吃" + food);
}
}
// Bird.java
public class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void eat(String food) {
System.out.println("我是一只小鸟");
System.out.println(this.name + "正在吃" + food);
}
}
//Bird中eat方法即是对父类eat方法的重写
②.重写与重载的区别:
重写 | 重载 |
方法名称、参数列表、返回值类型完全相同 | 方法名称相同,参数列表不同,返回值不做要求 |
作用在子类与父类之间 | 作用在同一个类 |
访问控制权限不可比父类严格 | 访问控制权限不做要求 |
- 向上转型
①.向上转型与向下转型
向上转型:把子类对象转化为父类对象。
向下转型:把父类对象转化为子类对象。
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println("我是一只小动物");
System.out.println(this.name + "正在吃" + food);
}
}
// Bird.java
public class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void eat(String food) {
System.out.println("我是一只小鸟");
System.out.println(this.name + "正在吃" + food);
}
}
public class Test {
public void static main(String[] args){
Animal a=new Bird();//向上转型
Bird b=(Brid)a;//向下转型
Animal a1=new Animal();
Bird b1=(Brid)a1;//错误示范
}
}
该代码中Animal a=new Bird();
即是向上转型,使父类的引用a指向子类对象Bird;而Bird b=(Brid)a;
即是向下转型:将父类对象转为子类对象。但由于此时父类对象a是指向子类对象的,因此父类对象a则可转化为子类对象b。但底下错误示范中,父类对象a1是指向父类对象的,此时子类引用b1不能直接指向父类对象a1。
②.注意事项:
父类引用可以直接指向子类对象,而子类引用不可直接指向父类对象;即向上转型可直接发生,向下转型不可。
发生向下转型前,必须要先发生向上转型。且需要强制转换,即a前边必须要加上(Bird)。
- 多态的实现——动态绑定
①. 动态绑定与静态绑定:
在Java中,当你调用一个方法时,可能会在编译时期(compile time)解析(resolve),也可能实在运行时期(runtime)解析,这全取决于到底是一个静态方法(static method)还是一个虚方法(virtual method)。如果是在编译时期解析,那么就称之为静态绑定(static binding),如果方法的调用是在运行时期解析,那就是动态绑定(dynamic binding)或者延迟绑定(late binding)。
多态使得父类型的引用变量可以引用子类型的对象。如果调用子类型对象的一个虚方法(非private,final or static),编译器将无法找到真正需要调用的方法,因为它可能是定义在父类型中的方法,也可能是在子类型中被重写(override)的方法,这种情形,只能在运行时进行解析,因为只有在运行时期,才能明确具体的对象到底是什么。这也是我们俗称的运行时或动态绑定(runtime or dynamic binding)。
另一方面,private static和final方法将在编译时解析,因为编译器知道它们不能被重写,所有可能的方法都被定义在了一个类中,这些方法只能通过此类的引用变量进行调用。这叫做静态绑定或编译时绑定(static or compile time binding)。所有的private,static和final方法都通过静态绑定进行解析。这两个概念的关系,与“方法重载”(overloading,静态绑定)和“方法重写”(overriding,动态绑定)类似。动态绑定只有在重写可能存在时才会用到,而重载的方法在编译时期即可确定(这是因为它们总是定义在同一个类里面)
class Shape {
public void draw() {
//没有操作
}
}
class Cycle extends Shape {
@Override //重写标识符
public void draw() {
System.out.println("○");
}
}
class Rect extends Shape {
@Override
public void draw() {
System.out.println("□");
}
}
class Flower extends Shape {
@Override
public void draw() {
System.out.println("♣");
}
}
public class Test {
public static void main(String[] args) {
Shape shape1 = new Flower();
Shape shape2 = new Cycle();
Shape shape3 = new Rect();
drawMap(shape1);
drawMap(shape2);
drawMap(shape3);
}
// 打印单个图形
public static void drawShape(Shape shape) {
shape.draw();
}
}
当类的调用者在编写 drawShape 这个方法的时候, 参数类型为 Shape (父类), 此时在该方法内部并不知道, 也不关注当前的 shape 引用指向的是哪个类型(哪个子类)的实例. 此时 shape 这个引用调用 draw 方法可能会有多种不同的表现(和 shape 对应的实例相关), 这种行为就称为多态.
- 多态的优点
①.可替换性(substitutability)。多态对已存在代码具有可替换性。
②.可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。
③.接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。
④.灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。
⑤.简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。