多态

多态

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程序分为编译阶段和运行阶段

  1. 编译阶段:对于编译器,只知道a1的类型是Animal,所以编译器在检查语法时,会去Animal.class字节码文件中去找move()方法,找到了,就绑定上move()方法,编译通过,静态绑定成功
  2. 运行阶段:实际上在堆内存中创建的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运算符

  1. instanceof可以在运行阶段动态判断引用指向的对象的类型
  2. 语法: (引用 intanceof 类型)
  3. 运算结果是true或false
  4. (引用 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("猫在抓老鼠");
	 }
}
  1. 虽然自己肉眼可以观察到底层是 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("猫在抓老鼠");
	 }
}

多态在开发中的作用

  1. 在软件开发中有一个原则:OCP(开闭原则):
    对扩展开放(可以额外添加程序)
    对修改关闭(最好少修改现有的程序)
    开发项目在满足客户需求的同时,还要考虑软件的扩展性
  2. 例如:在主人喂养宠物的案例中,在程序主人源程序中不写具体的宠物,可以写一个宠物父类,

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()方法上
这就是多态的使用
以后客户有了新的需求,不需要修改客户类,就继承宠物父类,对它的方法进行重写,重新创建一个新的宠物对象就行了
多态可以降低程序的耦合度,提高成序的扩展力
补:

  1. 方法覆盖常常和多态联合起来,而多态就强调对象,而静态方法的调用不需要对象来调用,所以常说方法覆盖只是针对实例方法,静态方法的覆盖无意义
  2. 私有方法不能覆盖 静态不谈覆盖

面向对象的三大特征:

封装, 继承, 多态
有了封装,就有了整体的概念之后,对象和对象之间产生了继承,之后才有了方法覆盖和多态

上一篇:linux中查看命令的用途(help)


下一篇:2021.11.6 Java 多态、instanceof运算符、多态在实际开发中的作用