1.1 多态的模式
多态是继封装、继承之后,面向对象的第三大个性。
多态是呈现在继承或者实现关系中的。
多态体现的格局:
父类类型 变量名 = new 子类/实现类结构器;变量名.办法名();
多态的前提:有继承关系,子类对象是能够赋值给父类类型的变量。例如Animal是一个动物类型,而Cat是一个猫类型。Cat继承了Animal,Cat对象也是Animal类型,天然能够赋值给父类类型的变量。
1.2 多态的应用场景
如果没有多态,在下图中register办法只能传递学生对象,其余的Teacher和administrator对象是无奈传递给register办法办法的,在这种状况下,只能定义三个不同的register办法别离接管学生,老师和管理员。
有了多态之后,办法的形参就能够定义为独特的父类Person。
要留神的是:
- 当一个办法的形参是一个类,咱们能够传递这个类所有的子类对象。
- 当一个办法的形参是一个接口,咱们能够传递这个接口所有的实现类对象(前面会学)。
- 而且多态还能够依据传递的不同对象来调用不同类中的办法。
代码示例:
父类:public class Person { private String name; private int age; 空参结构 带全副参数的结构 get和set办法 public void show(){ System.out.println(name + ", " + age); }}子类1:public class Administrator extends Person { @Override public void show() { System.out.println("管理员的信息为:" + getName() + ", " + getAge()); }}子类2:public class Student extends Person{ @Override public void show() { System.out.println("学生的信息为:" + getName() + ", " + getAge()); }}子类3:public class Teacher extends Person{ @Override public void show() { System.out.println("老师的信息为:" + getName() + ", " + getAge()); }}测试类:public class Test { public static void main(String[] args) { //创立三个对象,并调用register办法 Student s = new Student(); s.setName("张三"); s.setAge(18); Teacher t = new Teacher(); t.setName("王建国"); t.setAge(30); Administrator admin = new Administrator(); admin.setName("管理员"); admin.setAge(35); register(s); register(t); register(admin); } //这个办法既能接管老师,又能接管学生,还能接管管理员 //只能把参数写成这三个类型的父类 public static void register(Person p){ p.show(); }}
1.3 多态的定义和前提
多态: 是指同一行为,具备多个不同表现形式。
从下面案例能够看出,Cat和Dog都是动物,都是吃这一行为,然而呈现的成果(表现形式)是不一样的。
前提【重点】
- 有继承或者实现关系
- 办法的重写【意义体现:不重写,无意义】
父类援用指向子类对象【格局体现】
父类类型:指子类对象继承的父类类型,或者实现的父接口类型。
1.4 多态的运行特点
调用成员变量时:编译看右边,运行看右边
调用成员办法时:编译看右边,运行看左边
代码示例:
Fu f = new Zi();//编译看右边的父类中有没有name这个属性,没有就报错//在理论运行的时候,把父类name属性的值打印进去System.out.println(f.name);//编译看右边的父类中有没有show这个办法,没有就报错//在理论运行的时候,运行的是子类中的show办法f.show();
1.5 多态的弊病
咱们曾经晓得多态编译阶段是看右边父类类型的,如果子类有些独有的性能,此时多态的写法就无法访问子类独有性能了。
class Animal{ public void eat(){ System.out.println("动物吃货色!") }}class Cat extends Animal { public void eat() { System.out.println("吃鱼"); } public void catchMouse() { System.out.println("抓老鼠"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } }class Test{ public static void main(String[] args){ Animal a = new Cat(); a.eat(); a.catchMouse();//编译报错,编译看右边,Animal没有这个办法 }}
1.6 援用类型转换
1.6.1 为什么要转型
多态的写法就无法访问子类独有性能了。
当应用多态形式调用办法时,首先查看父类中是否有该办法,如果没有,则编译谬误。也就是说,不能调用子类领有,而父类没有的办法。编译都谬误,更别说运行了。这也是多态给咱们带来的一点"小麻烦"。所以,想要调用子类特有的办法,必须做向下转型。
回顾根本数据类型转换
- 主动转换: 范畴小的赋值给范畴大的.主动实现:double d = 5;
- 强制转换: 范畴大的赋值给范畴小的,强制转换:int i = (int)3.14
多态的转型分为向上转型(主动转换)与向下转型(强制转换)两种。
1.6.2 向上转型(主动转换)
- 向上转型:多态自身是子类类型向父类类型向上转换(主动转换)的过程,这个过程是默认的。
当父类援用指向一个子类对象时,便是向上转型。
应用格局:
父类类型 变量名 = new 子类类型();如:Animal a = new Cat();
起因是:父类类型绝对与子类来说是大范畴的类型,Animal是动物类,是父类类型。Cat是猫类,是子类类型。Animal类型的范畴当然很大,蕴含所有动物。所以子类范畴小能够间接主动转型给父类类型的变量。
1.6.3 向下转型(强制转换)
- 向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。
一个曾经向上转型的子类对象,将父类援用转为子类援用,能够应用强制类型转换的格局,便是向下转型。
应用格局:
子类类型 变量名 = (子类类型) 父类变量名;如:Aniaml a = new Cat(); Cat c =(Cat) a;
1.6.4 案例演示
当应用多态形式调用办法时,首先查看父类中是否有该办法,如果没有,则编译谬误。也就是说,不能调用子类领有,而父类没有的办法。编译都谬误,更别说运行了。这也是多态给咱们带来的一点"小麻烦"。所以,想要调用子类特有的办法,必须做向下转型。
转型演示,代码如下:
定义类:
abstract class Animal { abstract void eat(); } class Cat extends Animal { public void eat() { System.out.println("吃鱼"); } public void catchMouse() { System.out.println("抓老鼠"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } public void watchHouse() { System.out.println("看家"); } }
定义测试类:
public class Test { public static void main(String[] args) { // 向上转型 Animal a = new Cat(); a.eat(); // 调用的是 Cat 的 eat // 向下转型 Cat c = (Cat)a; c.catchMouse(); // 调用的是 Cat 的 catchMouse } }
1.6.5 转型的异样
转型的过程中,一不小心就会遇到这样的问题,请看如下代码:
public class Test { public static void main(String[] args) { // 向上转型 Animal a = new Cat(); a.eat(); // 调用的是 Cat 的 eat // 向下转型 Dog d = (Dog)a; d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】 } }
这段代码能够通过编译,然而运行时,却报出了 ClassCastException
,类型转换异样!这是因为,明明创立了Cat类型对象,运行时,当然不能转换成Dog对象的。
1.6.6 instanceof关键字
为了防止ClassCastException的产生,Java提供了 instanceof
关键字,给援用变量做类型的校验,格局如下:
变量名 instanceof 数据类型 如果变量属于该数据类型或者其子类类型,返回true。如果变量不属于该数据类型或者其子类类型,返回false。
所以,转换前,咱们最好先做一个判断,代码如下:
public class Test { public static void main(String[] args) { // 向上转型 Animal a = new Cat(); a.eat(); // 调用的是 Cat 的 eat // 向下转型 if (a instanceof Cat){ Cat c = (Cat)a; c.catchMouse(); // 调用的是 Cat 的 catchMouse } else if (a instanceof Dog){ Dog d = (Dog)a; d.watchHouse(); // 调用的是 Dog 的 watchHouse } } }
1.6.7 instanceof新个性
JDK14的时候提出了新个性,把判断和强转合并成了一行
//新个性//先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d//如果不是,则不强转,后果间接是falseif(a instanceof Dog d){ d.lookHome();}else if(a instanceof Cat c){ c.catchMouse();}else{ System.out.println("没有这个类型,无奈转换");}
1.7 综合练习
需要:依据需要实现代码: 1.定义狗类 属性: 年龄,色彩 行为: eat(String something)(something示意吃的货色) 看家lookHome办法(无参数)2.定义猫类 属性: 年龄,色彩 行为: eat(String something)办法(something示意吃的货色) 逮老鼠catchMouse办法(无参数)3.定义Person类//饲养员 属性: 姓名,年龄 行为: keepPet(Dog dog,String something)办法 性能:喂养宠物狗,something示意喂养的货色 行为: keepPet(Cat cat,String something)办法 性能:喂养宠物猫,something示意喂养的货色 生成空参有参结构,set和get办法 4.定义测试类(实现以下打印成果): keepPet(Dog dog,String somethind)办法打印内容如下: 年龄为30岁的老王养了一只黑色彩的2岁的狗 2岁的黑色彩的狗两只前腿死死的抱住骨头猛吃 keepPet(Cat cat,String somethind)办法打印内容如下: 年龄为25岁的老李养了一只灰色彩的3岁的猫 3岁的灰色彩的猫眯着眼睛侧着头吃鱼5.思考: 1.Dog和Cat都是Animal的子类,以上案例中针对不同的动物,定义了不同的keepPet办法,过于繁琐,是否简化,并领会简化后的益处? 2.Dog和Cat尽管都是Animal的子类,然而都有其特有办法,是否想方法在keepPet中调用特有办法?
画图剖析:
代码示例:
//动物类(父类)public class Animal { private int age; private String color; public Animal() { } public Animal(int age, String color) { this.age = age; this.color = color; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public void eat(String something){ System.out.println("动物在吃" + something); }}//猫类(子类)public class Cat extends Animal { public Cat() { } public Cat(int age, String color) { super(age, color); } @Override public void eat(String something) { System.out.println(getAge() + "岁的" + getColor() + "色彩的猫眯着眼睛侧着头吃" + something); } public void catchMouse(){ System.out.println("猫抓老鼠"); }}//狗类(子类)public class Dog extends Animal { public Dog() { } public Dog(int age, String color) { super(age, color); } //行为 //eat(String something)(something示意吃的货色) //看家lookHome办法(无参数) @Override public void eat(String something) { System.out.println(getAge() + "岁的" + getColor() + "色彩的狗两只前腿死死的抱住" + something + "猛吃"); } public void lookHome(){ System.out.println("狗在看家"); }}//饲养员类public class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } //豢养狗 /* public void keepPet(Dog dog, String something) { System.out.println("年龄为" + age + "岁的" + name + "养了一只" + dog.getColor() + "色彩的" + dog.getAge() + "岁的狗"); dog.eat(something); } //豢养猫 public void keepPet(Cat cat, String something) { System.out.println("年龄为" + age + "岁的" + name + "养了一只" + cat.getColor() + "色彩的" + cat.getAge() + "岁的猫"); cat.eat(something); }*/ //想要一个办法,能接管所有的动物,包含猫,包含狗 //办法的形参:能够写这些类的父类 Animal public void keepPet(Animal a, String something) { if(a instanceof Dog d){ System.out.println("年龄为" + age + "岁的" + name + "养了一只" + a.getColor() + "色彩的" + a.getAge() + "岁的狗"); d.eat(something); }else if(a instanceof Cat c){ System.out.println("年龄为" + age + "岁的" + name + "养了一只" + c.getColor() + "色彩的" + c.getAge() + "岁的猫"); c.eat(something); }else{ System.out.println("没有这种动物"); } }}//测试类public class Test { public static void main(String[] args) { //创建对象并调用办法 /* Person p1 = new Person("老王",30); Dog d = new Dog(2,"黑"); p1.keepPet(d,"骨头"); Person p2 = new Person("老李",25); Cat c = new Cat(3,"灰"); p2.keepPet(c,"鱼");*/ //创立饲养员的对象 Person p = new Person("老王",30); Dog d = new Dog(2,"黑"); Cat c = new Cat(3,"灰"); p.keepPet(d,"骨头"); p.keepPet(c,"鱼"); }}