0x01概念
多态从字面上理解,就是多种状态的意思。在面向对象的语言中,接口的多种不同实现方式即为多态。
多态性是允许你将父对象设置成为一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。
也就是说,多态是通过指向父类的指针来调用在不同子类中实现的方法。
多态的优点
1、消除类型之间的耦合关系
2、可替换性
3、可扩充性
4、接口性
5、灵活性
6、简化性
0x02前提条件
继承关系下,每一个子类都定义有一个同名的成员方法。
继承:在多态中必须存在有继承关系的子类和父类
重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
向上转型(父类引用指向子类对象):在多态中需要将子类的引用赋给父类的对象,然后转型后的对象通过动态绑定机制自动调用转型前所属子类同名的方法。
0x03示例展示
public class Test { public static void main(String[] args) { show(new Cat()); // 以 Cat 对象调用 show 方法 show(new Dog()); // 以 Dog 对象调用 show 方法 Animal a = new Cat(); // 向上转型 a.eat(); // 调用的是 Cat 的 eat Cat c = (Cat)a; // 向下转型 c.work(); // 调用的是 Cat 的 work } public static void show(Animal a) { a.eat(); // 类型判断 if (a instanceof Cat) { // 猫做的事情 Cat c = (Cat)a; c.work(); } else if (a instanceof Dog) { // 狗做的事情 Dog c = (Dog)a; c.work(); } } } abstract class Animal { abstract void eat(); } class Cat extends Animal { public void eat() { System.out.println("吃鱼"); } public void work() { System.out.println("抓老鼠"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } public void work() { System.out.println("看家"); } }
执行的结果将会输出
吃鱼
抓老鼠
吃骨头
看家
吃鱼
抓老鼠
重写
重写是子类对父类的允许访问的方法的实现过程进行重新编写,返回值和形参都不能改变。
重写的好处在于子类可以根据需要,定义特定于自己的行为。也就是说,子类能够根据需要实现父类的方法。
重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。
在面向对象原则里,重写意味着可以重写任何现有方法,比如
class Animal{ public void move(){ System.out.println("动物可以移动"); } } class Dog extends Animal{ public void move(){ System.out.println("狗可以跑和走"); } } public class TestDog{ public static void main(String args[]){ Animal a = new Animal(); // Animal 对象 Animal b = new Dog(); // Dog 对象 a.move();// 执行 Animal 类的方法 b.move();//执行 Dog 类的方法 } }
以上编译大的运行结果为
动物可以移动
狗可以跑和走
在上面的例子中可以看到,尽管 b 属于 Animal 类型,但是它的运行的是 Dog 类的 move 方法。
这是由于在编译阶段,只是检查参数的引用类型。
然而在运行时,Java虚拟机指定对象的类型并且运行该对象的方法。
方法的重写规则在于
参数列表与被重写方法的参数列表必须完全相同。
返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
父类的成员方法只能被它的子类重写。
声明为 final 的方法不能被重写。
声明为 static 的方法不能被重写,但是能够被再次声明。
子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
构造方法不能被重写。
如果不能继承一个类,则不能重写该类的方法。
接口
接口,在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口要包含类要实现的方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须要实现接口内所描述的所有方法,否则就必须声明为抽象类。例外,在 Java 中,接口类型可以来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
实例
/* 文件名 : MammalInt.java */ public class MammalInt implements Animal{ public void eat(){ System.out.println("Mammal eats"); } public void travel(){ System.out.println("Mammal travels"); } public int noOfLegs(){ return 0; } public static void main(String args[]){ MammalInt m = new MammalInt(); m.eat(); m.travel(); } }
执行结果为
Mammal eats
Mammal travels
抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。
实例如下
/* 文件名 : Employee.java */ public abstract class Employee { private String name; private String address; private int number; public Employee(String name, String address, int number) { System.out.println("Constructing an Employee"); this.name = name; this.address = address; this.number = number; } public double computePay() { System.out.println("Inside Employee computePay"); return 0.0; } public void mailCheck() { System.out.println("Mailing a check to " + this.name + " " + this.address); } public String toString() { return name + " " + address + " " + number; } public String getName() { return name; } public String getAddress() { return address; } public void setAddress(String newAddress) { address = newAddress; } public int getNumber() { return number; } }
多态的实现方式
方式一:重写
方式二:接口
方式三:抽象类
(水文)