多态
1. 向上/下转型(前提:两种类型之间有继承关系)
1. 向上转型 子转向父
public class Animal {//父类
public void move() {
System.out.println("动物在移动");
}
public static void main(String [] args) {
Animal a1=new Bird();
Animal a2=new Cat();
a1.move();//鸟在飞翔
a2.move();//猫在走猫步
}
}
class Bird extends Animal {
public void move() {
System.out.println("鸟在飞翔");
}
}
class Cat extends Animal {
public void move() {
System.out.println("猫在走猫步");
}
}
Animal a1=new Bird();
其中,Animal是父类, Bird是子类,满足继承关系的前提
a1是父类型的引用,new Bird()是子类型的对象
此时,多态中是父类型的引用指向子类型的对象
分析:a1.move();
java程序分为编译阶段和运行阶段
- 编译阶段:对于编译器,只知道a1的类型是Animal,所以编译器在检查语法时,会去Animal.class字节码文件中去找move()方法,找到了,就绑定上move()方法,编译通过,静态绑定成功
- 运行阶段:实际上在堆内存中创建的java对象是 Bird对象,所以在执行move()方法时,真正执行move()方法的对象是Bird对象,属于运行阶段绑定,动态绑定
**多态体现在:编译的时候一种形态,运行的时候是另一种形态
多态指的是是父类型的引用指向子类型的对象,包括编译阶段和运行阶段,编译阶段静态绑定父类的方法,运行阶段动态绑定子类型对象的方法 **
2. 向下转型 父转向子
当想要访问子类对象特有的方法,要向下转型
public class Animal {//父类
public void move() {
System.out.println("动物在移动");
}
public static void main(String [] args) {
Animal a1=new Cat();
//a1.zhua();不执行,为什么?
/*分析程序要分编译阶段静态绑定和运行阶段的动态绑定
* 在编译阶段,编译器,只知道a1的类型是Animal,
* 会去Animal.class字节码文件中去找zhua()方法,没有找到,编译不通过
* 要想可以执行,使用向下转型,类似于强制类型转换
* 当想要访问子类对象特有的方法,要向下转型*/
Cat x = (Cat)a1;
x.zhua();//猫在抓老鼠
/*分析:a1的类型是Animal,Animal和a1满足继承关系,可以向下转型*/
}
}
class Cat extends Animal {
public void move() {//父类方法重写
System.out.println("猫在走猫步");
}
//子类独有的方法
public void zhua() {
System.out.println("猫在抓老鼠");
}
}
instanceof运算符
- instanceof可以在运行阶段动态判断引用指向的对象的类型
- 语法: (引用 intanceof 类型)
- 运算结果是true或false
- (引用 instanceof 类型)若为true,表明引用所保存的内存地址指向的堆中的对象是属于此类型
public class Animal {//父类
public void move() {
System.out.println("动物在移动");
}
public static void main(String [] args) {
Animal a1= new Bird();
// Cat y = (Cat)a1;
// y.zhua();
/*分析:a1的类型是Animal,Animal和a1满足继承关系,可以向下转型,编译通过
* 运行阶段:堆内存中实际上创建的对象是 Bird对象,要将 Bird对象转换为Cat,
* 二者之间没有继承关系,就不行了,
* 运行报错 java.lang.ClassCastException称为类型转换异常
* 以上称为向下转型的风险/
* 避免异常的发生?用到ins/tanceof运算符*/
if(a1 instanceof Cat) {
Cat y = (Cat)a1;
y.zhua();
}//在以后进行向下转型时,要写的判断语句
else {
System.out.println("a1指向的对象不是猫类型");
}
}//a1指向的对象不是猫类型
}
class Bird extends Animal {
public void move() {
System.out.println("鸟在飞翔");
}
}
class Cat extends Animal {
public void move() {
System.out.println("猫在走猫步");
}
public void zhua() {
System.out.println("猫在抓老鼠");
}
}
- 虽然自己肉眼可以观察到底层是 new Bird()还是new Cat(),但以后程序交接给别人时,不知道,所以要习惯上用instanceof运算符去判断
public class Animal {//父类
public void move() {
System.out.println("动物在移动");
}
public static void main(String [] args) {
Animal x=new Bird();
if(x instanceof Bird) {
Bird b= (Bird)x;
b.sing();
}
else if(x instanceof Cat) {
Cat a=(Cat)x;
a.zhua();
}
}
class Bird extends Animal {
public void move() {
System.out.println("鸟在飞翔");
}
public void sing() {
System.out.println("猫在抓老鼠");
}
}
class Cat extends Animal {
public void move() {
System.out.println("猫在走猫步");
}
public void zhua() {
System.out.println("猫在抓老鼠");
}
}
多态在开发中的作用
- 在软件开发中有一个原则:OCP(开闭原则):
对扩展开放(可以额外添加程序)
对修改关闭(最好少修改现有的程序)
开发项目在满足客户需求的同时,还要考虑软件的扩展性 - 例如:在主人喂养宠物的案例中,在程序主人源程序中不写具体的宠物,可以写一个宠物父类,
public class Test {
public static void main(String[] args) {
//创建主人对象
Master zhang = new Master();
//创建宠物对象
Cat xiao = new Cat();
Dog huang = new Dog();
//主人喂宠物
zhang.feed(xiao);
zhang.feed(huang);
}
}
class Master{
public void feed(Pet p) {
p.eat();
}
}
class Pet{//所有宠物的父类
//包含了吃的行为
public void eat(){
}
}
//具体的宠物对象继承父类Pet,重写父类中的方法
class Cat extends Pet{
public void eat(){
System.out.println("猫吃鱼");
}
}
class Dog extends Pet{
public void eat(){
System.out.println("狗吃骨头");
}
}
多态体现在zhang.feed(xiao)中,对象xiao传参给(Pet p)相当于
Pet p = new Cat()
编译时:编译器会知道 p是属于Pet类,会去Pet这个父类中去查找 eat()方法,找到了,编译通过
运行时:底层实际对象是什么,就自动调用到该实际对象的 eat()方法上
这就是多态的使用
以后客户有了新的需求,不需要修改客户类,就继承宠物父类,对它的方法进行重写,重新创建一个新的宠物对象就行了
多态可以降低程序的耦合度,提高成序的扩展力
补:
- 方法覆盖常常和多态联合起来,而多态就强调对象,而静态方法的调用不需要对象来调用,所以常说方法覆盖只是针对实例方法,静态方法的覆盖无意义
- 私有方法不能覆盖 静态不谈覆盖
面向对象的三大特征:
封装, 继承, 多态
有了封装,就有了整体的概念之后,对象和对象之间产生了继承,之后才有了方法覆盖和多态