曰:“白马非马,可乎?”
曰:“可。”
曰:“何哉?”
曰:“马者,所以命形也。白者,所以命色也。命色者,非命形也,故曰白马非马。”
什么是多态
1. 本态
一个对象的本类形态就是本态.
2. 多态
一个对象的多种父类形态就是多态
态的使用1. 多态引用
将子类对象赋值于父类类型的引用变量就是多态引用, 在这里对象其实还是子类对象, 只不过是被看作是一个父类类型的对象.
Person person = new Student();
l 多态性,是面向对象中最重要的概念,在java中有两种体现:
1. 方法的重载(overload)和重写(overwrite)。
2. 对象的多态性 ——可以直接应用在抽象类和接口上。
l Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。
注意:若编译时类型和运行时类型不一致,就出现多态(Polymorphism)
2. 多态带来的问题
l 一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法
Student m = new Student();
m.school = “pku”; //合法,Student类有school成员变量
Person e = new Student();
e.school = “pku”; //非法,Person类没有school成员变量
属性是在编译时确定的,编译时e为Person类型,没有school成员变量,
因而编译错误。
虚拟方法调用(Virtual Method Invocation)1. 什么是虚拟方法
正常的方法调用:
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
虚拟方法调用(多态情况下):
Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法
编译时类型和运行时类型:
编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。
——动态绑定
2. 多态小结
前提:
需要存在继承或者实现关系
要有覆盖操作
成员方法:
编译时:要查看引用变量所属的类中是否有所调用的方法。
(编译时检查父类类型)
运行时:调用实际对象所属的类中的重写方法。
(运行时执行子类类型)
成员变量:
不具备多态性,只看引用变量所属的类。
多态的应用场景1. 多态数组
当创建多个不同的子类对象, 而又想统一处理这批对象时, 就可以使用多态数组.
Person[] arr = {new Student(), new Teacher()};
public class Person {
private String name;
private int age;
private String gender;
public Person(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getGender() {
return gender;
}
public String say() {
return "姓名:" name ",年龄:" age ",性别:" gender;
}
public void sayHello() {
System.out.println("打个招呼");
}
}
public class Chinese extends Person {
private String shuxiang;
public Chinese(String name, int age, String gender, String shuxiang) {
super(name, age, gender);
this.shuxiang = shuxiang;
}
public void setShuxiang(String shuxiang) {
this.shuxiang = shuxiang;
}
public String getShuxiang() {
return shuxiang;
}
public void spring() {
System.out.println("过大年");
}
@Override
public String say() {
return super.say() ",属相:" shuxiang;
}
@Override
public void sayHello() {
System.out.println("吃了吗?");
}
}
public class American extends Person {
private boolean hasGun;
public American() {}
public American(String name, int age, String gender, boolean hasGun) {
super(name, age, gender);
this.hasGun = hasGun;
}
public void setHasGun(boolean hasGun) {
this.hasGun = hasGun;
}
public boolean isHasGun() {
return hasGun;
}
public void christmas() {
System.out.println("Merry Christmas!!");
}
@Override
public void sayHello() {
System.out.println("How are you?");
}
}
public class PersonTest {
public static void main(String[] args) {
Person[] ps = new Person[5]; // 多态数组, 可以保存任意类型的子类对象
ps[0] = new Chinese("张三", 30, "男", "牛");
ps[1] = new American("Jack", 25, "male", true);
ps[2] = new Person("某人", 15, "未知");
ps[3] = new American("Rose", 32, "female", false);
ps[4] = new Chinese("李四", 40, "女", "羊");
for (Person p : ps) {
System.out.println(p.say());
}
System.out.println("-------------------------");
// 因为在同一个数组中, 并且所有对象都有age属性, 所以就可以对所有元素进行冒泡排序
for (int i = 0; i < ps.length - 1; i ) {
for (int j = 0; j < ps.length - 1 - i; j ) {
if (ps[j].getAge() > ps[j 1].getAge()) {
Person tmp = ps[j];
ps[j] = ps[j 1];
ps[j 1] = tmp;
}
}
}
for (Person p : ps) {
System.out.println(p.say());
}
}
}
2. 多态参数
在设计方法时, 有的方法内部需要用到另外的类的对象, 但是在设计方法时, 又不需要关心究竟是哪个对象, 只关心这些实参对象都是某个类型的, 此时, 方法的参数的类型就可以写成父类类型的参数,这样的方法的参数就是多态参数.
public void test(Person p) { // 这个方法的参数p的具体类型未知
System.out.println(p.say());
}
main() {
test(new Chinese()); // 在实际调用多态参数方法时, 可以传入任意本类或子类类型的对象
test(new American());
}
x instanceof A:检验x是否为类A的对象,返回值为boolean型。
Ø 要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
Ø 如果x属于类A的子类B,x instanceof A值也为true。
public class Person extends Object {…}
public class Student extends Person {…}
public class Graduate extends Person {…}
-------------------------------------------------------------------
public void method1(Person e) {
if (e instanceof Person)
// 处理Person类及其子类对象
if (e instanceof Student)
//处理Student类及其子类对象
if (e instanceof Graduate)
//处理Graduate类及其子类对象
}
l 基本数据类型的Casting:
Ø 自动类型转换:小的数据类型可以自动转换成大的数据类型
如long g=20; double d=12.0f
Ø 强制类型转换:可以把大的数据类型强制转换(casting)成小的数据类型
如 float f=(float)12.0; int a=(int)1200L
l 对Java对象的强制类型转换称为造型
Ø 从子类到父类的类型可以自动进行
Ø 从父类到子类的类型转换必须通过造型(强制类型转换)实现
Ø 无继承关系的引用类型间的转换是非法的
Ø 在造型前可以使用instanceof操作符测试一个对象的类型
class PersonTest5 {
public static void test(Person p) { // 多态参数, 提高了兼容性!!
p.sayHello(); //方法中父类中定义,所以直接调用,
//p.spring(); // 多态副作用, 子类特有成员不能访问
// 必须对对象的真实身份进行检测
if (p instanceof Chinese) { // 造型有风险, 使用须谨慎!!!
Chinese ch = (Chinese)p; // 造型: 对象是什么类型还原成什么类型
ch.spring(); // 子类特有的成员的访问,必须要经过造型.
} else if (p instanceof American) {
((American)p).christmas();
} else if (p instanceof Person){
System.out.println("普通人一枚");
}
}
public static void main(String[] args) {
Person[] ps = new Person[5]; // 多态数组, 可以保存任意类型的子类对象
Chinese c1 = new Chinese("张三", 30, "男", "牛");
American a1 = new American("Jack", 25, "male", true);
Person p1 = new Person("某人", 15, "未知");
American a2 = new American("Rose", 32, "female", false);
Chinese c2 = new Chinese("李四", 40, "女", "羊");
ps[0] = c1;
ps[1] = a1;
ps[2] = p1;
ps[3] = a2;
ps[4] = c2;
System.out.println("-------------------------");
for (Person p : ps) {
test(p); // 不同的子类对象作为实参调用方法, 方法的执行也不一样.
}
}
}
了解更多内容:
JavaSE基础之多态的应用场景
super关键字
this关键字和对象关系
volatile关键字详解
JS实现继承有几种方法
,