文章目录
一、概述
- 面对对象程序设计的三大支柱是封装、继承和多态。
- 多态性是对象多种表现形式的体现。
- 多态类型:
-
静态多态性:
- 包括变量的隐藏、方法的重载(指同一个类中,方法名相同但方法的参数类型、个数、次序不同,本质上是多个不同的方法)
-
动态多态性(运行时多态):
- 指子类在继承父类(或实现接口)时,重写了父类(或接口)的方法,程序中用父类(或接口)引用去指向子类的具体实例,从代码形式上看时父类(或接口)引用去调用父类(接口)的方法
- 但是在实际运行时,JVM能够根据父类(或接口)引用所指的具体子类,去调用对应子类的方法,从而表现为不同子类对象有多种不同的形态。
- 不过,程序代码在编译时还不能确定调用的哪一个类的方法,只有在运行时才能确定,故又称为运行时的多态性。
-
静态多态性:
-
多态实现的前提条件
-
继承
-
重写
-
以下例子中,首先定义父类
Shape
及其方法draw()
,子类Circle
,继承并重写了父类中的draw()方法。class Shape{ void draw(){} } class Circle extends Shape{ void draw(){ System.out.println("Circle.draw()"); } }
-
-
父类引用指向子类对象:
Parent p = new Child();
-
二、多态的对象转换
多态的对象转换分为向上转换(向上转型)和向下转换(向下转型)
-
向上转型:将一个子类的实例转换为一个父类的变量
-
语法
Father f = new Son();
-
注意:
-
上转型对象是由子类创建的,但上转型对象会失去子类的一些属性和方法。
-
上转型对象调用方法时,就是调用子类继承和重写过的方法。而不会是新增的方法,也不是父类原有的方法。
-
上转型对象可以操纵父类原有的属性和功能,无论这些方法是否被重写。
-
上转型对象可以再强制转换到一个子类对象,强制转换过的对象具有子类所有属性和功能。
public class Person{ // 父类 int age = 10; void getAge(){ // 父类原有的方法 System.out.println(age); } public static void main(String[] args){ Person p = new Male(); p.getAge(); // 向上转型对象调用子类重写方法 p.getName(); // 编译报错,向上转型对象失去子类的属性和方法 } } class Male extends Person{ // Male子类继承Person父类 void getAge(){ // 子类重写父类方法 System.out.println(age+1); } void getName(){ // 子类自己的方法 System.out.println("Li wei"); } }
-
-
-
向下转型:将一个父类的实例转换为它的子类变量(强制类型转换)
-
必须使用转换标记 ”(子类名)” 进行显示转换。
-
必须确保要转换的对象时子类的一个实例。如果父类对象不是子类的一个实例,出现运行时异常
ClassCastException
-
语法
Father f = new Son(); Son s = (Father)f; // 错误示范(编译不报错,但运行报错), Father f = new Son(); Son s = Son(f); // 上述错误示范可以用instanceOF判断(类型防护) Person f = new Person(); if (f instanceof Son){ // 因为f不是Son的实例,所以不执行if内部代码 Son s = (Male)f; s.getName(); }
-
注意:
- 向下转型必须先向上转型,否则会发生异常
- 下转型对象可以引用子类和父类的属性和方法
-
-
实例:
public class Human { public void sleep() { System.out.println("Human sleep.."); } public static void main(String[] args) { // (1)向上转型 Male m1 = new Male(); Human h1 = m1; h1.sleep(); //h1.speak(); // 此时需要向下转型,否则不能调用speak方法。 // (2)向下转型 Male m2 = new Male(); Human h2 = m2; m2 = (Male) h2; m2.speak(); } } class Male extends Human { @Override public void sleep() { System.out.println("Male sleep.."); } public void speak() { System.out.println("I am Male"); } }
三、instanceof关键字
-
作用:用于判断一个对象是否为一个类的实例
-
语法:
boolean result = obj instanceof Class
- 如果obj为Class的对象,或者是其直接或间接子类,或者是其接口的实现类,则结果result返回true,否则返回false
Person p1 = new Person(); Person p2 = new Man(); Man m1 = new Man(); System.out.println("p1 instanceof Man); // false System.out.println("p2 instanceof Man); // true System.out.println("m1 instanceof Man); // true
- 注意:编译器会检查 obj 是否能转换成右边的class类型,如果不能转换则直接报错,如果不能确定类型,则通过编译,具体看运行时定。
四、多态中成员特点
-
成员变量:编译运行看父类
由下述例子可以看出,在父类Human中定义的成员变量 i 被赋值为10,在子类中定义的成员变量 i 被赋值为5,程序运行结果为10,而当父类中没有定义成员变量 i 时,出现编译报错,说明多态中成员变量的编译运行看父类
package multistate; public class Human { int i = 10; // 向上转型后输出值为10,只能取到父类的值 public void sleep() { System.out.println("Human sleep.."); } public void main(String[] args) { Human n = new Male(); // 向上转型 System.out.println(n.i); } } class Male extends Human { int i = 5; // 若父类中未定义i,则编译错误 public void sleep() { System.out.println("Male sleep.."); } }
-
成员方法:编译看父类,运行看子类
- 由下述例子可以看出,父类Human中调用的是子类Male中的sleep()方法,而当父类中删除sleep()方法后,出现编译错误,说明多态中成员方法的编译看父类,运行看子类。即:
- 首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
- 如果子类覆盖了该方法,就调用子类的方法,否则调用父类方法。
package multistate; public Human { public void sleep() { System.out.println("Human sleep.."); } public static void main(String[] args) { Male m = new Male(); m.sleep(); Human h = new Male(); //(1)向上转型 h.sleep(); //(2)动态绑定,调用的是子类中的sleep方法 } } class Male extends Human { @Override public void sleep() { System.out.println("Male sleep.."); } }