多态的应用
Animal父类
public class Animal
{
public void move(){
System.out.println("动物正在移动!!!!");
}
}
Bird子类
public class Bird extends Animal
{
public void move(){
System.out.println("鸟儿在飞翔!!!!");
}
public void sing(){
System.out.println("鸟儿在歌唱");
}
}
Cat子类
public class Cat extends Animal
{
public void move(){
System.out.println("猫儿在走猫步!!!!");
}
public void catMouse(){
System.out.println("猫在抓老鼠");
}
}
测试类
public class Test1
{
public static void main(String[] args){
Animal a1=new Animal();
Animal a2=new Bird();//向上转型
Animal a3=new Cat();//向上转型
a1.move();
a2.move();
a3.move();
// 错误: 找不到符号
//a4.sing();
//类型为Animal的变量 a4
/*
Animal a4=new Bird();
a4.sing();
这里sing方法是子类所特有的方法,不能a4.sing();
因为这样的话过不了编译阶段,编译阶段是静态绑定,
a4是Animal引用,编译阶段在Animal中找不到sing方法
*/
//要真的实在是想用子类中特有的方法的话就需要向下转型
//如下
Animal a5=new Bird();
Bird x=(Bird)a5;//这里就是向下转型
x.sing();//这样就可以访问子类中特有的方法
//这样向下转型会不会有风险?
//当然是会有风险的
//比如下面情况;
/*
Animal a6=new Cat();
Bird y=(Bird)a6;
*/
//虽然能过编译,但是不能过运行阶段,会报异常java.lang.ClassCastException类转换异常
// java.lang.ClassCastException: Cat cannot be cast to Bird
//at Test1.main(Test1.java:34)
//避免java.lang.ClassCastException这个异常需要使用instanceof运算符
Animal a7=new Cat();
if(a7 instanceof Bird){
Bird n=(Bird)a7;
n.sing();
}else if(a7 instanceof Cat){
Cat m=(Cat)a7;
m.catMouse();
}//分支判断,a7引用指向堆内存中的java对象是Cat
}
}
经过测试可知java中支持这样的一个语法:父类型的引用允许指向子类型的对象
多态:
1、父类型的引用指向子类型的对象
2、包括编译阶段和运行阶段
3、编译阶段:绑定父类的方法(静态绑定)
4、运行阶段:绑定子类型对象的方法(动态绑定)运行阶段(堆内存当中创建对象)
5、多种形态
java中允许向上转型也允许向下转型
**ps:**无论是向上转型还是向下转型,两种类型之间必须有继承关系,没有继承关系的话编译器会报错
子类--------->父类
例如:
Animal是父类
Cat是子类
Animal a1=new Cat();//这个就是向上转型,父类型的引用指向子类型的对象
a1.方法(这个方法是父类有的,而不能是子类所特有的)
如果引用的对象是子类特有的话,编译器会报错,错误:找不到符号
new Cat()就是子类型的对象
Animal a1就是父类型的引用
这里在编译阶段,会先去找父类中的有没有这个方法,然后在运行阶段就会再动态的绑定子类型对象中的方法
编译不报错,没有风险
什么时候使用向下转型:
如果想访问的方法是子类所特有的,就需要使用向下转型
例如
Cat a2=new Cat();
Cat x=(Cat)a2;(这里就是进行了强制类型转换也称向下转型)
x.方法(这个方法就是子类Cat所特有的方法)
有风险
Animal a3=new Bird();//这里的Animal是一个父类,Bird是继承Animal的子类,这里是向上转型
//这里编译阶段不会报错,但是运行阶段会报错,在运行阶段中,堆内存实际创建的对象是Bird对象,在实际运行中,拿着Bird对象转化成Cat对象就不行了。因为Bird对象和Cat对象没有继承关系。
//运行出现异常:这个异常和空指针异常一样非常重要,也非常经典
//java.lang.ClassCaseException:类型转换异常
Cat y=(Cat)a3;
y.catMouse();
运算符:instanceof(主要是在运行阶段动态判断引用指向的对象类型)、
语法:(引用 instanceof 类型)这个的返回结果是true/false
假设c是一个引用,c变量保存的内存地址指向堆中对象
1、假设(c instanceof Cat)为true时,表示:c引用指向的堆内存中的java对象是一个Cat
2、假设(c instanceof Cat)为false时,表示的是:c引用指向的堆内存中的java对象不是一个Cat
在任何时候,任何地点,对类型进行向下转型时,一定要使用instanceof运算符进行判断,(这是java规范中要求的)
这样可以很好的避免:ClassCaseException异常
因为有些时候程序员编写程序不是一个人而是分工完成的,所以当一个程序员在编程方法的时候他是不知道类的继承关系,以及进行向下转型会不会出错的,这个时候就需要使用instanceof来进行判断。
就例如;
一个人编写AnimalTest类
public class AnimalTest
{
//这个方法里面的参数是引用数据类型
//a就是相当于 Animal a =new Cat();/new Bird();
public void test(Animal a){
//为什么要用instanceof?
//因为这个程序时多人编写,别人不一定能知道传进来的参数继承的是哪个类,
//可不可以进行向下转换,并调用独有方法
if(a instanceof Cat){
Cat x=(Cat)a;
x.catMouse();
}else if(a instanceof Bird){
Bird y=(Bird)a;
y.sing();
}
}
}
一个人编写Test2类
public class Test2
{
public static void main(String[] args){
//new出AnimalTest对象
AnimalTest a1=new AnimalTest();
AnimalTest a2=new AnimalTest();
//对象调用方法,传的是引用数据类型
a1.test(new Cat());//猫在抓老鼠
a2.test(new Bird());//鸟儿在歌唱
}
}
一个好的软件就是要尽可能的减少对类的修改(除了添加类)要遵循ocp原则。
而多态就是为了能够降低程序的耦合度,提高程序的扩展力
例子:
编写程序模拟“主人”喂养“宠物”的场景:
提示1:
主人类:Master
宠物类:Pet
宠物类子类:Dog、Cat、YingWu
提示2:
主人应该有喂养的方法:feed()
宠物应该有吃的方法:eat()
只要主人喂宠物,宠物就吃。
要求:主人类中只提供一个喂养方法feed(),要求达到可以喂养各种类型的宠物。
编写测试程序:
创建主人对象
创建各种宠物对象
调用主人的喂养方法feed(),喂养不同的宠物,观察执行结果。
类Master
public class Master{
public void feed(Pet pet){
pet.eat();
}
}
类Pet
public class Pet{
public void eat(){}
}
类Dog
public class Dog extends Pet
{
public void eat(){
System.out.println("狗狗喜欢吃骨头,吃的很香!!!");
}
}
类Cat
public class Cat extends Pet
{
public void eat(){
System.out.println("猫猫喜欢吃小鱼干,吃的很香!!!");
}
}
类YingWu
public class YingWu extends Pet
{
public void eat(){
System.out.println("鹦鹉喜欢吃小虫子,吃的很香!!!");
}
}
测试类Test1
public class Test1
{
public static void main(String[] args){
//创建类的对象
Master xiaoming=new Master();
Dog heibei=new Dog();
Cat xiaomao=new Cat();
YingWu yingWu=new YingWu();
xiaoming.feed(heibei);
xiaoming.feed(xiaomao);
xiaoming.feed(yingWu);
}
}