Java--多态
1.概念:父类引用指向子类对象,从而产生多种形态。
前边学继承的时候 定义了父类 Animal,子类Dog,Bird (继承了父类Animal)
如果定义 Animal a = new Dog(); Animal a 就是父类引用(引用类型) new Dog()子类对象(子类类型) new Dog() 改为 new Bird () 也是成立的。
- 二者具有直接或间接的继承关系时,父类引用可指向子类对象,即形成多态
- 父类引用仅可以调用父类所声明的属性和方法,不可调用子类独有的属性和方法。
实例:定义 Animal a1 = new Dog(); a1只能调用父类Animal 中的属性和方法,不能调用子类Dog中的属性和方法。
2.多态中的方法重写
如果子类中重写了父类中的方法,以父类类型引用调用此方法时,优先执行父类中的方法还是子类中的方法?
在实际运行过程中,依旧遵循重写原则,如果子类重写了父类中的方法,执行子类中重写后的方法,否则执行父类中的方法。
package com.monv.plat; /** * 动物类 * @author Administrator * */ public class Animal { //品种 String breed; //年龄 int age; //性别 String Sex; //吃 public void eat() { System.out.println("吃......"); } //睡 public void sleep() { System.out.println("睡......"); } } ----------------------------------------------------------------------- package com.monv.plat; public class Dog extends Animal { //毛色--狗狗类定义的属性 String furColor; //跑--狗狗类定义的方法 public void run() { System.out.println(breed+"跑......"); } @Override //重写了父类中eat()方法 public void eat() { System.out.println(breed+"在吃狗粮!"); } } -------------------------------------测试-------------------------------- package com.monv.plat; public class TestDog { public static void main(String[] args) { // Animal animal = new Animal(); 通常实例化变量的写法 //理解:1.逻辑上来讲 Dog is Animal 是成立的 2.语法上来讲 隐含自动类型转换 Dog类的范围小于Animal类的范围 Animal a1 = new Dog();//用父类 Animal 创建变量a1 实例化Dog子类 //调用属性和方法 a1只能调用父类的属性和方法 a1.age = 2; a1.breed = "柯基"; a1.Sex ="公"; a1.eat();//在Dog中重写了父类中的eat方法 所以这里调用的是子类Dog的eat方法 a1.sleep(); } } 结果: 柯基在吃狗粮! 睡...... -------------------------------------------------------------------------
3.多态的应用
- 使用父类作为方法形参实现多态,使方法参数的类型更为宽泛
例如:小明有很多动物,现在小明来喂动物,根据动物的类型,来执行不同动物eat的方法
package com.monv.plat; /** * 动物类 * @author Administrator * */ public class Animal { //品种 String breed; //年龄 int age; //性别 String Sex; //吃 public void eat() { System.out.println("吃......"); } //睡 public void sleep() { System.out.println("睡......"); } } ----------------------------------------------- package com.monv.plat; public class Dog extends Animal { //毛色--狗狗类定义的属性 String furColor; //跑--狗狗类定义的方法 public void run() { System.out.println(breed+"跑......"); } @Override //重写了父类中eat()方法 public void eat() { System.out.println(breed+"在吃狗粮!"); } } ----------------------------------------------------- package com.monv.plat; public class Bird extends Animal{ //毛色 String furColor; //飞 public void fly(){ System.out.println("鸟在飞!"); } @Override//重新父类中eat方法 public void eat() { System.out.println("早起的鸟儿有虫吃"); } } --------------------------------------------------------- package com.monv.plat; /** * 主人类---喂食
* 利用方法重载在类中添加不同喂食方法 喂狗狗的 喂小鸟的 如果要添加新的喂食方法 需要再重写feed方法传递不同的参数 比较麻烦 导致重载的方法过多 而且需要多次修改Master这个类
* 由此 这里可以用多态优化 方法的形参不定义具体子类的参数类型 用父类Animal来定义 这样的话 用一个方法就可以执行不同的喂食方法 * @author Administrator * */ public class Master { String name; // //喂狗方法 // public void feed(Dog dog){ // System.out.println(this.name+"给狗狗喂食"); // dog.eat(); // } // //喂鸟的方法 // public void feed (Bird bd) { // System.out.println(this.name+"给小鸟喂食"); // bd.eat(); // }
//使用多态优化 父类作为方法的参数 public void feed(Animal animal){ System.out.println(this.name+"喂食"); animal.eat();//由于子类把父类的方法重写了 所以这里调用的是子类的方法 } } -----------------------------测试--------------------------------------- package com.monv.plat; public class TestMaster { public static void main(String[] args) { Master ma = new Master(); ma.name = "小明"; //定义狗狗 柯基 Dog keji = new Dog(); keji.breed ="柯基"; ma.feed(keji); //定义鸟儿 Bird bd = new Bird(); ma.feed(bd); } }
结果: 小明喂食 柯基在吃狗粮! 小明喂食 早起的鸟儿有虫吃 -------------------------------------------------------------------------
- 使用父类作为方法返回值实现多态,使方法可以返回不同子类对象
package com.monv.plat; /** * 主人类---喂食和购买 * @author Administrator * */ public class Master { String name; // //喂狗方法 // public void feed(Dog dog){ // System.out.println(this.name+"给狗狗喂食"); // dog.eat(); // } // //喂鸟的方法 // public void feed (Bird bd) { // System.out.println(this.name+"给小鸟喂食"); // bd.eat(); // } //使用多态 多态的第一种用法 作为方法的形参 public void feed(Animal animal){ System.out.println(this.name+"喂食"); animal.eat(); } //买 根据传递的type值 来判断购买的是狗狗 还是小鸟(多态的第二种用法 作为方法的返回值) public Animal buy(int type){ Animal animal = null; if (type == 1){ animal = new Dog(); } else if (type == 2){ animal = new Bird(); } return animal; } } ---------------------------测试----------------------------------------- package com.monv.plat; import java.util.Scanner; public class TestBuy { public static void main(String[] args) { System.out.println("--------欢迎来到格兰动物市场--------"); System.out.println("-------1.购买狗狗 2.购买小鸟-------"); System.out.println("请选择:"); Scanner input = new Scanner(System.in); int choice = input.nextInt(); Master master = new Master(); Animal animal = master.buy(choice);//animal用来接收 传入不同choice值返回的类型 if (animal!=null){ System.out.println("购买成功"); }else { System.out.println("输入错误,购买失败!"); } } } ------------------------------------------------------------------------
4.类型转换 上例中 master.buy返回的类型是Animal类 只能访问Animal类中的属性和方法 如果想要访问子类Dog中的方法,怎么办?------用类型转换
- 向上转型(装箱):
Animal a = new Dog(); 父类引用中保存真实子类对象,称为向上转型(即多态核心概念)
注意:a仅可调用Animal中的方法和属性。
- 向下转型(拆箱) 前提:先向上转型
Animal a = new Dog(); (前提)
Dog keji = (Dog)a;---将父类引中的真实子类对象,强转回子类本身类型,称为向下转型。
注意:只有转回子类真实类型,才可以调用子类独有的属性和方法。keji可调用Dog类中独有的属性和方法。
package com.monv.plat; import java.util.Scanner; public class TestBuy { public static void main(String[] args) { System.out.println("--------欢迎来到格兰动物市场--------"); System.out.println("-------1.购买狗狗 2.购买小鸟-------"); System.out.println("请选择:"); Scanner input = new Scanner(System.in); int choice = input.nextInt(); Master master = new Master(); Animal animal = master.buy(choice);//animal用来接收 传入不同choice值返回的类型 if (animal!=null){ System.out.println("购买成功"); Dog keji =(Dog)animal;//向下类型转换 keji.run();//调用的是Dog类中的方法 }else { System.out.println("输入错误,购买失败!"); } } } ------------------------------------------------ 输入1 则返回结果; 如果输入 2 则会报错 为什么? 因为:输入2 animal接收的是Bird类,把Bird类向下类型转换为Dog类则会类型转换异常的错误
错误信息(Exception in thread "main" java.lang.ClassCastException: com.monv.plat.Bird cannot be cast to com.monv.plat.Dog at com.monv.plat.TestBuy.main(TestBuy.java:16))
5.类型转换异常
向下转型时,如果父类引用中的子类对象类型和目标类型不匹配,则会发生类型转换异常。
如何处理这种异常,用关键字 instanceof
向下转型前,应判断引用中的对象真实类型,保证类型转换的正确性。
语法:引用 instanceof 类型 //返回boolean类型结果
package com.monv.plat; import java.util.Scanner; public class TestBuy { public static void main(String[] args) { System.out.println("--------欢迎来到格兰动物市场--------"); System.out.println("-------1.购买狗狗 2.购买小鸟-------"); System.out.println("请选择:"); Scanner input = new Scanner(System.in); int choice = input.nextInt(); Master master = new Master(); Animal animal = master.buy(choice);//animal用来接收 传入不同choice值返回的类型 if (animal!=null){ System.out.println("购买成功"); if(animal instanceof Dog){//如果animal是Dog类型 则转换为Dog类型 Dog keji =(Dog)animal;//向下类型转换 keji.run();//调用的是Dog类中的方法 }else if (animal instanceof Bird){//如果animal是Bird类型 则转换为Bird类型 Bird yw = (Bird)animal; yw.fly(); } }else { System.out.println("输入错误,购买失败!"); } }
6.总结
多态的两种应用场景:
- 使用父类作为方法的形参,实现多态。(feed方法)
调用方法时,可传递的实参类型包括:本类对象 + 其所有的子类对象。
- 使用父类作为方法的返回值,实现多态。(buy方法)
调用方法后,可得到的结果类型包括:本类对象 + 其所有的子类对象。
多态作用:
- 屏蔽子类间的差异
- 灵活,耦合度低