- 概念:父类引用可以存储不同的子类对象,子类对象不同呈现出不同的状态,固称为多态。
- 在程序中的体现:
package com.txw.test;
import java.util.Scanner;
public class TestPolymorphism{
public static void main(String[]args){
Animal d = new Dog();
// 使用的属性与方法,都是由对象负责存储或调用的
d.name = "森森";
d.eat(); // 调用覆盖后的方法
d.sleep(); // 调用父类的
/*
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
Animal a1 = null;
// 编译时不能确定a1所存储的对象
if( n==1 )
a1 = new Dog();
else
a1 = new Fish();
// 所有Animal子类对象都具备eat,sleep可以调用
a1.eat();
a1.sleep();
*/
// 不是所有对象都具备swim,shout,run方法
// a1.swim();
// a1.shout();
// a1.run();
// 强类型语言,数据类型与变量类型 必须一致
//父类引用 子类对象
// Animal a1 = new Dog();
// Animal a2 = new Cat();
// Animal a3 = new Fish();
// Animal a4 = new Bird();
// Worker not is a Animal
// Animal a5 = new Worker(); Error
// Dog d = new Dog(); // 创建Dog对象
// Pet p = d;
// Animal a = d;
// System.out.println( d );
// System.out.println( p );
// System.out.println( a );
}
}
class Worker{}
class Animal{
String name;
int age;
public void eat(){
System.out.println("eat() in Animal ");
}
public void sleep(){
System.out.println("sleep() in Animal ");
}
}
class Pet extends Animal{} // 宠物 is a 动物
// 在程序中说明 Dog is a Pet
class Dog extends Pet{
// 覆盖父类中的方法
public void eat(){
System.out.println("eat() in Dog ");
}
public void swim(){}
public void shout(){}
public void run(){}
}
class Cat extends Animal{
public void shout(){}
public void run(){}
}
class Fish extends Animal{
public void swim(){}
}
class Bird extends Animal{
public void shout(){}
public void fly(){}
}
在父类引用中存储子类对象。
1 多态的特点
1.1对象类型始终不变
演示的代码如下:
class Animal{}
class Pet extends Animal{} // 宠物 is a 动物
class Dog extends Pet{} // 狗 is a 宠物
Dog d = new Dog(); // 创建Dog对象
Pet p = d;
Animal a = d;
System.out.println( d ); // classes.Dog@15db9742
System.out.println( p ); // classes.Dog@15db9742
System.out.println( a ); // classes.Dog@15db9742
1.2 编译时只能调用引用类型中的方法
演示的代码如下:
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
Animal a1 = null;
// 编译时不能确定a1所存储的对象
if( n==1 )
a1 = new Dog();
else
a1 = new Fish();
// 所有Animal子类对象都具备eat,sleep可以调用
a1.eat();
a1.sleep();
// 不是所有对象都具备swim,shout,run方法
// a1.swim();
// a1.shout();
// a1.run();
1.3 如果子类覆盖了父类的方法,执行覆盖后的方法
演示的代码如下:
Animal d = new Dog();
// 使用的属性与方法,都是由对象负责存储或调用的
d.name = "森森";
d.eat(); // 调用覆盖后的方法 打印 eat() in Dog
d.sleep(); // 调用父类的 打印 sleep() in Animal
class Animal{
public void eat(){
System.out.println("eat() in Animal ");
}
public void sleep(){
System.out.println("sleep() in Animal ");
}
}
// 在程序中说明 Dog is a Animal
class Dog extends Animal{
// 覆盖父类中的方法
public void eat(){
System.out.println("eat() in Dog ");
}
public void swim(){}
public void shout(){}
public void run(){}
}
2 向下转型
- 作用:
演示的代码如下:由于多态存在,父类引用中存储子类对象,只能调用引用类型(父类)中的方法,如果需要使用子类中的方法,必须将引用类型转换为子类,由父类引用转换为子类引用称为向下转型。
package com.txw.test;
public class TestPoly{
public static void main(String[]args){
Animal a = new Dog();
// 只能调用引用类型中的方法
a.eat();
a.sleep();
// 调用子类中的方法,必须更换引用类型
// 将父类的引用转换为子类引用(向下转型)
if(a instanceof Dog){
Dog d =(Dog)a; // 将a引用中存储的对象付给d引用
d.swim();
d.shout();
d.run();
}
System.out.println("程序结束");
// 强制类型转换有可以会出现
// java.lang.ClassCastException:类型转换异常 classes.Cat cannot be cast to classes.Dog
}
}
class Animal{
String name;
int age;
public void eat(){
System.out.println("eat() in Animal ");
}
public void sleep(){
System.out.println("sleep() in Animal ");
}
}
// 在程序中说明 Dog is a Pet
class Dog extends Animal{
// 覆盖父类中的方法
public void eat(){
System.out.println("eat() in Dog ");
}
public void swim(){System.out.println("swim() in Dog");}
public void shout(){}
public void run(){}
}
class Cat extends Animal{
public void shout(){}
public void run(){}
}
class Fish extends Animal{
public void swim(){}
}
class Bird extends Animal{
public void shout(){}
public void fly(){}
}
注意:
由于引用类型为父类型可以存储任意类型子类对象,所以向下转型时可能失败。
比如:
Animal a = new Dog();
Cat c = a;
编译时编译器认为类型转换可能会出现问题,所以出现编译错误。
-
强制类型转换(拒绝编译器干预)
语法如下:引用类型 引用名 =(类名) 引用;
比如:
Animal a = new Dog(); Dog d =(Dog) a; // 将Animal类型的引用a,强制类型转换为Dog类型引用d(向下转型)
编译器不再干预转换过程,如果运行时对象类型与引用类型不兼容,则出现: 类型装换异常
java.lang.ClassCastException:类型转换异常
。 -
向下转型后即可使用子类引用访问子类对象中的方法。
Animal a = new Dog(); // 只能调用引用类型中的方法 a.eat(); a.sleep(); // 调用子类中的方法,必须更换引用类型 // 将父类的引用转换为子类引用(向下转型) Dog d =(Dog)a;//将a引用中存储的对象付给d引用 // 使用子类引用访问子类对象中的方法 d.swim(); d.shout(); d.run();
3 instanceof 关键字
- 作用:用于判断引用类型中存储的对象是否与某种类型兼容。
语法如下:引用名 instanceof 类名 。 结果为boolean,兼容返回true,不兼容返回 false。
- 应用场景:
- 找出某种类型的对象。
package com.txw.test;
public class TestAnimalArray{
public static void main(String[]args){
int i1 = 1;
int i2 = 200;
int i3 = 400;
int[] is = {i1,i2,i3};
for(int i=0; i<is.length; i++){
// 从数组中提取数据
int x = is[i];
}
Animal a1 = new Dog();
Animal a2 = new Cat();
Animal a3 = new Fish();
Animal a4 = new Bird();
Animal a5 = new Cat();
Animal a6 = new Fish();
Animal a7 = new Dog();
// 对象数组,
Animal[] as = {a1,a2,a3,a4,a5,a6,a7};
// 统计Dog对象的数量
int count = 0;
// 遍历数组
for(int i=0; i<as.length; i++){
// 从数组中提取数据
Animal a = as[i];
// 判断如果a引用中存储的对象为Dog类型
if(a instanceof Dog){
System.out.println( a );
count++;
}
}
System.out.println(count);
}
}
- 强制类型前进行判断,避免类型转换异常的发生
演示的代码如下:Animal a = new ?(); // 只能调用引用类型中的方法 a.eat(); a.sleep(); // 将父类的引用转换为子类引用(向下转型) // a instanceof Dog:判断a引用中存储的对象是否与Dog类型兼容 if(a instanceof Dog){ Dog d =(Dog)a; // 将a引用中存储的对象付给d引用 d.swim(); d.shout(); d.run(); }
4 多态的应用场景
- 多态的作用:父类引用类型屏蔽子类类型之间的差异。
演示1:自动贩卖机。
package com.txw.test;
import java.util.Scanner;
public class Test{
public static void main(String[]args){
System.out.println("===欢迎使用阿森自动售货机===");
System.out.println("请输入商品编号:");
System.out.println("1:红牛");
System.out.println("2:奶茶");
System.out.println("3:可乐");
System.out.println("4:冰红茶");
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
// 创建自动售货机对象
VendingMachine vm = new VendingMachine();
Beverage rb= vm.sell( n ); // 调用贩卖方法,传入商品编号
System.out.println("您购买了"+rb.name+"饮料,花费:"+rb.price);
}
}
// 自动售货机
class VendingMachine{
// 贩卖方法,int n表示商品编号
public Beverage sell(int n){
switch( n ){
case 1 : Beverage rb = new RedBull("红牛维生素饮料",6D); // 创建红牛对象
return rb;
case 2 : Beverage mt = new MilkTea("优乐美奶茶",5D);
return mt;
case 3 : Beverage co = new Cola("可口可乐",3D);
return co;
case 4 : Beverage ibt = new IceBlackTea("康师傅冰红茶",3D);
return ibt;
default:System.out.println("没有该类商品!");return null;
}
}
}
// 饮料
class Beverage{
String name;
double price;
public Beverage(){}
public Beverage(String name,double price){
this.name = name;
this.price = price;
}
}
// 红牛
// 红牛 is a 饮料
class RedBull extends Beverage{
public RedBull(){}
public RedBull(String name,double price){
super(name,price);
}
}
// 奶茶
// 奶茶 is a 饮料
class MilkTea extends Beverage{
public MilkTea(){}
public MilkTea(String name,double price){
super(name,price);
}
}
// 可乐
// 可乐 is a 饮料
class Cola extends Beverage{
public Cola(){}
public Cola(String name,double price){
super(name,price);
}
}
// 冰红茶
// 冰红茶 is a 饮料
class IceBlackTea extends Beverage{
public IceBlackTea(){}
public IceBlackTea(String name,double price){
super(name,price);
}
}
演示2:士兵作战。
package com.txw.test;
public class TestSoldier{
public static void main(String[]args){
Fiream f1 = new Rifle("AK-47","7.62mm");
Fiream f2 = new Rifle("M4A1","5.56mm");
Fiream f3 = new Snipe("AWM","马格南");
Fiream f4 = new Snipe("Bletter","8.53mm");
Fiream f5 = new Shoutgun("S12K","12mm");
Fiream f6 = new Shoutgun("DBS12","12mm");
Fiream[] fs = {f1,f2,f3,f4,f5,f6};
Soldier s = new Soldier("阿森",18);
for(int i=0; i<fs.length; i++){
s.attack( fs[i] );
}
}
}
// 士兵
class Soldier{
String name;
int age;
public Soldier(){}
public Soldier(String name,int age){
this.name = name;
this.age = age;
}
// 攻击敌人
public void attack(Fiream f){
System.out.print(name+"\t"+age);
System.out.println(" 士兵正在使用 "+f.name+" 攻击敌人");
f.fire(); // 使用武器开火
}
}
// 枪械类
abstract class Fiream{
String name;
String ammo;
public Fiream(){}
public Fiream(String name,String ammo){
this.name = name;
this.ammo = ammo;
}
public abstract void fire();
}
// 步枪
class Rifle extends Fiream{
public Rifle(){}
public Rifle(String name,String ammo){
super(name,ammo);
}
public void fire(){//开火
System.out.println("dadadadadada~~~~~");
}
}
//狙击枪
class Snipe extends Fiream{
public Snipe(){}
public Snipe(String name,String ammo){
super(name,ammo);
}
public void fire(){
System.out.println("duang~~~~~~~");
}
}
// 散弹枪 霰弹枪
class Shoutgun extends Fiream{
public Shoutgun(){}
public Shoutgun(String name,String ammo){
super(name,ammo);
}
public void fire(){
System.out.println("peng~~~~peng~~~~~peng~~~~~");
}
}
总结如图所示: