所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
定义
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作。
同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。
简单的说:就是用基类的引用指向子类的对象。
多态的优点
我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了代码重用。
而多态除了代码的复用性外,还可以解决项目中紧偶合的问题,提高程序的可扩展性。
如果项目耦合度很高的情况下,维护代码时修改一个地方会牵连到很多地方,会无休止的增加开发成本,降低耦合度可以保证程序的拓展性。
- 1. 消除类型之间的耦合关系
- 2. 可替换性
- 3. 可扩充性
- 4. 接口性
- 5. 灵活性
- 6. 简化性
多态存在的三个必要条件
- 继承:在多态中必须存在有继承关系的子类和父类。
- 重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
- 父类引用指向子类对象(向上转型):在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。
例如:
Parent p = new Student();
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
以下是一个多态实例的演示:
public class Demo {
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 ("看家");
}
}
运行结果
吃鱼
抓老鼠
吃骨头
看家
吃鱼
抓老鼠
向上转型与向下转型
要转型,首先要有继承。
向上转型:子类对象转为父类,父类可以是接口。公式:Father f = new Son();Father是父类或接口,son是子类。
向下转型:父类对象转为子类。公式:Son s = (Son)f;
向上转型
Father f = new Son();
子类对象当成父类对象,只能调用父类的成员,如果子类重写了父类的方法就根据这个引用指向调用子类重写的这个方法(这个方法就是覆盖override)。这个调用过程就称为“动态绑定”。
这里定义了一个Father 类型的f,它指向对象实例。由于Son是继承于Father,所以Son可以自动向上转型为Father,所以f是可以指向Son实例对象的。这样做的好处:在继承中子类是父类的扩展,它可以提供比父类更多的功能,定义了一个指向子类的父类引用类型,它既可以引用父类的共性外,还能使用子类里的功能。
但是向上转型存在一些缺憾,就是它必定会导致一些方法和属性的丢失(子类单独定义的方法会丢失),导致我们无法获取它们。所以父类类型的引用可以调用父类中定义的所有属性和方法,对于只存在于子类中的方法和属性它就无法调用了。
转型需要注意的问题:
向上转型时,父类指向子类引用对象会遗失除与父类对象共有的其他方法,也就是在转型过程中,子类的新有的方法都会遗失掉,在编译时,系统会提供找不到方法的错误。
向下转型
在向下转型过程中,分为两种情况:
情况一:如果父类引用的对象如果引用的是指向的子类对象,那么在向下转型的过程中是安全的。也就是编译是不会出错误的。
情况二:如果父类引用的对象是父类本身,那么在向下转型的过程中是不安全的,编译不会出错,但是运行时会出现java.lang.ClassCastException错误。它可以使用instanceof来避免出错此类错误。
总结:
1、把子类对象直接赋给父类引用叫upcasting向上转型,向上转型不用强制转型。
如Father father = new Son();
2、把指向子类对象的父类引用赋给子类引用叫向下转型(downcasting),要强制转型,要向下转型,必须先向上转型为了安全可以用instanceof判断。
如father就是一个指向子类对象的父类引用,把father赋给子类引用son 即Son son =(Son)father;
其中father前面的(Son)必须添加,进行强制转换。
3、upcasting 会丢失子类特有的方法,但是子类overriding 父类的方法,子类方法有效,向上转型只能引用父类对象的属性,要引用子类对象属性,则要写getter函数。
4、向上转型的作用,减少重复代码,父类为参数,调有时用子类作为参数,就是利用了向上转型。这样使代码变得简洁。体现了JAVA的抽象编程思想。
5、父类引用可以指向子类对象,子类引用不能指向父类对象。
多态的实现方式
1.重写
2.接口
3.方式三:抽象类和抽象方法