总结:
0、编译类型与运行类型可以不一致,编译类型在定义时即确定,不能更改;运行类型可以改变,使用 getClass() 方法可以查看该引用的运行类型;
1、编译类型看左边,运行类型看右边
2、属性的值看编译类型
3、方法调用看运行类型
4、向上转型:将父类引用指向子类对象
5、向下转型:将原来指向子类对象的父类引用,转成指向子类对象的子类引用
package mypoly;
/* 多态基本介绍
1、方法或对象具有多种形态,多态建立在封装和继承的基础之上
2、方法多态:重载和重写就体现多态
3、对象的多态(多态的核心):
1)一个对象的编译类型和运行类型可以不一致
2)编译类型在定义对象时,就确定了,不能改变
3)运行类型可以变化
4)编译类型看定义时 = 号的左边,运行类型看 = 的右边;
Animal animal = new Dog(); //animal的编译类型是Animal,运行类型是Dog
animal = new Cat(); //animal的运行类型变成了Cat, 编译类型仍然是 Animal
多态注意事项:
1、多态的前提是: 两个对象(类)存在继承关系
2、多态的向上转型:
1) 本质: 父类的引用指向了子类的对象
2) 语法: 父类类型 引用名 = new 子类类型();
3) 特点: 编译类型看左边,运行类型看右边
可以调用父类中的所有成员(遵守修饰符的访问权限)
不能调用子类中的特有成员
- 编译阶段,根据定义的变量类型进行编译,父类中没有子类的特有成员,若调用子类中特有成员,编译时会报错
最终运行效果看子类的具体实现
- 运行阶段,根据对象类型来运行(调用方法),因为引用实际上是指向子类的实例化对象
- 所以使用 引用名.方法名() 调用方法时,会先从子类开始查找
3、多态的向下转型:
1)语法: 子类类型 引用名 = (子类类型)父类引用;
2)只能强转父类的引用(地址重新赋值),不能强转父类的对象
3)要求父类的引用必须指向的是向下转型的目标类型的对象
4)可以调用子类类型中的所有成员
4、属性没有重写之说,属性的值看编译类型
5、instanceOf 比较操作符,用于判断对象的 运行类型 是否为XX类型,或XX类型的子类型
*/
public class MyPoly {
public static void main(String[] args) {
BB bb = new BB(); // 运行类型是 BB
System.out.println(bb instanceof BB); // true
System.out.println(bb instanceof AA); // true
AA aa = new BB(); // 运行类型是 BB
System.out.println(aa instanceof BB); // true
System.out.println(aa instanceof AA); // true
Object obj = new Object();
System.out.println(obj instanceof AA); // false
String str = "hello";
// System.out.println(str instanceof obj); // 编译报错
}
}
class AA{}
class BB extends AA{}
package mypoly.polydetail;
public class Animal {
public void eat(){
System.out.println("吃");
}
public void sleep(){
System.out.println("睡");
}
public void run(){
System.out.println("跑");
}
public void say(){
System.out.println("Hi~");
}
}
//
package mypoly.polydetail;
public class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
public void cathMouse(){
System.out.println("猫抓老鼠");
}
}
//
package mypoly.polydetail;
public class Dog extends Cat{
}
//
package mypoly.polydetail;
public class Test {
public static void main(String[] args) {
// 向上转型: 父类的引用指向了子类的对象
Animal animal = new Cat();
// Object obj = new Cat(); //可以编译通过 Object也是Cat的父类
// 可以调用父类中的成员(访问范围内) 但不能调用子类的特有成员
// animal.catchMouse(); // 报错 编译阶段能调用哪些成员 由编译类型决定
// 最终运行效果看子类中的具体实现 调用方法时 从子类的成员中开始查找调用 规则与继承一致
animal.eat();
animal.run();
animal.sleep();
animal.say();
// 向下转型
Cat cat = (Cat) animal;
cat.cathMouse();
// 要求父类的引用必须指向的是向下转型的目标类型的对象 - 即animal原来本身就是指向Cat的
// 即使 Dog 类继承Cat类,也会运行时报错
// Dog dog = (Dog)animal; // 运行时报错
}
}