多态
多态基本介绍
方法或对象具有多种形态。是面向对象的三大特征,多态是建立在封装和继承基础之上的。
多态的具体体现
1、方法的多态: 重写和重载就能体现多态。
2、对象的多态:【背下来,记住】
-
一个对象的编译类型和运行类型可以不一致
-
编译类型在定义对象时,就已经被确定,不能再改变
-
运行类型是可以变化的
-
编译类型看定义时 = 号的左边,运行类型看 = 号的右边
// 可以让父类的引用指向子类的类型。
Animal animal = new Dog(); //animal编译类型是Animal, 运行类型是Dog
animal = new Cat(); // animal的运行类型变成了Cat,编译类型仍然为Animal
多态的注意事项和细节讨论
多态的前提是:
1、两个对象(类)存在继承关系。
2、多态的向上转型
3、多态的向下转型
4、类的属性没有重写之说,当调用类的属性时,属性的值为编译类型对应的值,如果在编译类型中没有查到,则去父类中查找属性
package com.encap.poly;
public class PolyDetail {
public static void main(String[] args) {
Animal animal = new Dog();
// 判断运行结果
System.out.println(animal.count);
}
}
class Animal{
int count = 10;
}
class Dog extends Animal{
int count = 20;
}
5、instanceOf比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX类型的子类型
String str = "hello";
str isstanceOf Object; // true。 判断对象的运行类型【String】 是否为 XXX(Object)类型或 XX(Object)类型的子类型。
什么叫向上转型:
1、本质:父类的引用指向了子类的对象
2、语法:父类类型 引用名 = new 子类类型();
Animal animal = new Cat();
3、使用特点:
-
编译类型看左边,运行类型看右边。
-
可以调用父类中的所有成员(需遵循父类的访问权限)
-
不能调用子类中的特有成员
对上面两句的解释:在编译阶段,能调用哪些成员,是由编译类型来决定的。javac指令
- 最终运行效果看运行类型的具体实现。 即调用成员时,按照从子类(运行类型)开始查找成员,找到则调用,找到没权限则报错。 java指令
向下转型
1、语法: 子类类型 引用名 = (子类类型) 父类引用;
Cat cat = (Cat)animal;
2、只能强转父类的引用,不能强转父类的对象。
3、要求父类的引用必须指向的是当前目标类型的对象
4、当向下转型后,可以调用子类类型中的所有成员
Animal animal = new Cat(); // 向上转型,子类的对象,指向父类的引用名
Cat cat = (Cat)animal; // 向下转型, 将父类的引用进行强转,转成 当前目标类型的对象。
// animal此引用名指向堆中的Cat类,这是向上转型,此时编译类型为Animal,但运行类型为 Cat
// 向下转型则为,将堆中的Cat类重新创建一个引用,所以要求父类的引用必须指向的是当前目标类型的对象。
动态绑定机制
1、当调用对象方法的时候,该方法会对该对象的内存地址/运行类型进行绑定。即为方法从运行类型开始查找。
2、当调用对象属性时,则没有动态绑定机制,即为,属性哪个编译类调用,就用哪个编译类的属性
第二点是在方法中使用到属性,所以是对应着声明类。
「多态的前提」是,「类的属性没有重写之说,当调用类的属性时,属性的值直接对应编译类型得到,如果编译类型中没有,则去父类中查找属性」 。属性没有重写机制,则不需要从子类向父类查找属性,记住这两种情况即可。
测试题
package com.encap.test;
public class Test {
public static void main(String[] args) {
A a = new B();
System.out.println(a.getSum());
System.out.println(a.getSum1());
}
}
class A {
public int i = 10;
public int getI() {
return i;
}
public int getSum() {
return getI() + 10;
}
public int getSum1() {
return i + 10;
}
}
class B extends A{
public int i = 20;
public int getI() {
return i;
}
// public int getSum() {
// return getI() + 20;
// }
// public int getSum1() {
// return i + 10;
// }
}
多态数组
多态数组的定义类型为父类类型,里面保存的实际元素类型为子类类型。
package com.encap.extend_.Exercise03;
public class Test {
public static void main(String[] args) {
PC pc = new PC("inter", 16, 256, "IBM");
// 创建多态数组方法一:
// Computer[] c = new Computer[2];
// c[0] = pc;
// c[1] = new Computer("Mac", 256, 512);
// 创建多态数组方法二:
Computer[] c = {pc, new Computer("Mac", 256, 512)};
for (int i = 0; i < c.length; i++) {
if (c[i] instanceof PC){
// 向下转型方法一:
((PC)c[i]).printInfo();
// 向下转型方法二:
// PC p1 = (PC)c[i];
// p1.printInfo();
} else{
System.out.println(c[i].getDetail());
}
}
}
}
class PC extends Computer{
private String brand;
public PC(String CPU, int memory, int disk, String brand) {
super(CPU, memory, disk);
this.brand = brand;
}
public void printInfo(){
System.out.println("PC信息为:");
System.out.println(getDetail() + "品牌为: " + brand);
}
}
class Computer {
private String CPU;
private int memory;
private int disk;
public Computer(String CPU, int memory, int disk) {
this.CPU = CPU;
this.memory = memory;
this.disk = disk;
}
public String getDetail(){
return "电脑的详细信息:" + "CPU: " + CPU + " 内存为: " + memory + " 硬盘为:" + disk;
}
}
多态参数
方法定义的形参类型为父类类型,实参类型则允许传入子类类型。