目录
多态 是面向对象三大特征之一。
同一个对象,在不同的时刻表现出来的不同形态。
举例:狗
狗就是狗 狗 dog = new 狗();
我们也可以说 动物 animal = new 狗();
这里狗在不同的时刻表现出来的形态,这就是多态。
多态的前提和体现
- 有继承 / 实现关系
- 方法重写
- 有父类引用指向子类对象
AnimalParent.java
- package com.面向对象.Demo22;
-
- public class AnimalParent {
- // 父类
- public void eat(){
- System.out.println("这是动物类 都有eat 方法");
- }
- }
Dog.java
- package com.面向对象.Demo22;
-
- public class Dog extends AnimalParent {
- // 子类
- /**
- * 多态的基本的条件
- * 1.有继承 或 实现(后面会学习到接口的概念)的关系
- * 2.方法的重写——子类重写父类的方法
- * 3.有父类的引用指向子类
- */
- @Override
- public void eat() {
- System.out.println("子类(狗类)重写了父类 eat 方法");
- }
- }
AnimalDemo.java
- package com.面向对象.Demo22;
-
- public class AnimalDemo {
- public static void main(String[] args) {
- // 本身的类型指向引用 new 本身的对象
- Dog dog = new Dog();
- dog.eat();
- // 有父类的引用指向子类对象 多态
- AnimalParent animalParent = new Dog();
- animalParent.eat();
- }
- }
- 成员变量:编译看左边,执行看左边;
- 成员方法:编译看左边,执行看右边。
为什么成员变量和成员方法的访问不一样呢?
∵ 成员方法有重写,而成员变量是没有的。
AnimalParent.java
- package com.面向对象.Demo22;
-
- public class AnimalParent {
- public int age = 20;
-
- // 父类
- public void eat() {
- System.out.println("这是动物类 都有eat 方法");
- }
- }
Dog.java
- package com.面向对象.Demo22;
-
- public class Dog extends AnimalParent {
- public int age = 10;
- public int weight = 20;
- // 子类
-
- /**
- * 多态的基本的条件
- * 1.有继承 或 实现(后面会学习到接口的概念)的关系
- * 2.方法的重写——子类重写父类的方法
- * 3.有父类的引用指向子类
- */
- @Override
- public void eat() {
- System.out.println("子类(狗类)重写了父类 eat 方法");
- }
- public void show(){}
- }
AnimalDemo.java
- package com.面向对象.Demo22;
-
- public class AnimalDemo {
- /**
- * AnimalParent animalParent = new Dog();
- * 成员属性 编译阶段是看左边(父类animalParent)执行也是看左边(父类animalParent);
- * 成员方法 编译阶段是看左边(父类animalParent)执行是看右边(子类Dog);
- * @param args
- */
- public static void main(String[] args) {
- /**
- * 本身的类型指向引用 new 本身的对象
- * 非多态
- */
- Dog dog = new Dog();
- dog.eat();
- System.out.println(dog.age);
- System.out.println(dog.weight);
-
-
- /**
- * 有父类的引用指向子类对象
- * 多态
- */
- AnimalParent animalParent = new Dog();
- animalParent.eat();
-
- System.out.println(animalParent.age);//20,这个age 是父类animalParent里的 age
- // System.out.println(animalParent.weight);//报错,Cannot resolve symbol 'weight'(无法解析weight)
- //成员属性 编译阶段是看左边(父类animalParent),父类没有这个属性,所以报错
-
- // animalParent.show();//报错,Cannot resolve method 'show' in 'AnimalParent'
- // 成员方法 编译阶段是看左边(父类animalParent),父类没有show这个方法,所以报错,
- //执行虽然看的右边 子类有show(),但是这个show()没有重写父类的show()
- // (父类没有show())所以,在编译阶段都报错,何谈执行阶段
-
-
- animalParent.eat(); // 输出:子类(狗类)重写了父类 eat 方法
- //父类和子类都有eat()方法,但是 执行是看右边的(子类Dog)里的eat()
- // Ctrl + Alt + 鼠标点击eat,可以看到都有那些类里面有eat()方法
- }
- }
- 多态的好处:提高了程序的扩展性
- 具体体现:定义方法的时候,使用父类型作为参数,将来在使用的时候,使用具体的子类型参与操作
- 多态的弊端:不能使用子类的特有功能
AnimalParent.java
- package com.面向对象.Demo23;
-
- public class AnimalParent { //显示AnimalParent的类图关系,Ctrl+Alt+点击AnimalParent
- public void eat(){
- System.out.println("父类-AnimalParent-eat()");
- }
- }
Cat.java
- package com.面向对象.Demo23;
-
- public class Cat extends AnimalParent{
- @Override
- public void eat() {
- System.out.println("子类-Cat-eat()");
- }
- }
Dog.java
- package com.面向对象.Demo23;
-
- public class Dog extends AnimalParent {
- @Override
- public void eat() {
- System.out.println("子类-Dog-eat()");
- }
- }
AnimalOperate.java
- package com.面向对象.Demo23;
-
- // 动物操作类 操作 狗和猫类
- public class AnimalOperate {
- /**
- * 1.方法1
- * 方法传递参数 猫类 调用 猫类的 eat 方法
- */
- // public void userCatEat(Cat cat) {
- // cat.eat();
- // }
-
- // public void userDogEat(Dog dog) {
- // dog.eat();
- // }
-
- /**
- * 2.方法2
- */
-
- // public void userAnimal(){
- // 多态的访问特点 编译看左边 执行看右边
- // AnimalParent cat = new Cat();
- // cat.eat();
- // Dog dog = new Dog();
- // AnimalParent dog = new Dog();
- // dog.eat();
- //
- // }
-
- // 上面的两部分代码冗余 优化为下面代码
-
- /**
- * 3.方法3————多态
- * 该方法 传递的类型 是子类 但是该方法接受的参数 是父类型
- * animalOperate.userAnimal(new Dog());
- * 后面如果 在增加子类,这个方法就不用去动了,只需要写增加的子类里面的方法即可
- */
- public void userAnimal(AnimalParent animalParent) {
- /**
- * 调用animalOperate.userAnimal(new Dog());这个方法,传递的是子类 new Dog(),
- * 就<=> animalParent = new Dog();
- */
- // animalParent = new Dog(); 既然等价,可以省略
- // 成员方法 编译看左边 执行看右边,编译:父类中有eat(),执行:调用的Dog()下的eat()
- animalParent.eat();
- }
- /**
- * 多态机制 优缺点
- * 优点:提高程序的扩展性
- * 缺点:不能使用子类的特有功能
- */
- }
AnimalDemo.java
- package com.面向对象.Demo23;
-
- public class AnimalDemo {
- public static void main(String[] args) {
- // 1. new 动物操作类
- AnimalOperate animalOperate = new AnimalOperate();
- // 2. 调用猫类中的 Eat()
- // animalOperate.userCatEat(new Cat()); //子类-Cat-eat() //使用每个子类都创建一个方法,扩展性差
- // 3. 调用狗类中的 Eat()
- // animalOperate.userDogEat(new Dog()); //子类-Dog-eat() //使用每个子类都创建一个方法,扩展性差
-
-
- /**
- * 因此使用下面的多态机制
- * 传递的类型 是子类 但是该方法接受的参数 是父类型
- */
- animalOperate.userAnimal(new Cat()); //子类-Cat-eat()
- animalOperate.userAnimal(new Dog()); //子类-Dog-eat()
- }
- }
1. 向上转型 (多态机制)
从子到父
父类引用指向子类对象
2. 向下转型 (强转)
从父到子(可能发生类型转换异常java.lang.ClassCastException)
父类引用转为子类对象
Dog.java
- package com.面向对象.Demo23;
-
- public class Dog extends AnimalParent {
- @Override
- public void eat() {
- System.out.println("子类-Dog-eat()");
- }
- public void showDog(){
- System.out.println("Dog-Dog类中独有的方法");
- }
- }
AnimalDemo02.java
- package com.面向对象.Demo23;
-
- public class AnimalDemo02 {
- public static void main(String[] args) {
- // 多态中 调用成员方法 编译阶段是看左边
- Dog dog = new Dog();
- System.out.println("直接 通过 dog 类型接收");
- dog.showDog();
-
- //多态中 编译阶段是看左边 执行是看右边 向上转型 从子到父
- AnimalParent parent = new Dog();
- // parent.showDog(); //报错,父类没有showDog()
- parent.eat(); // 向上转型 从子到父
-
- // 访问 parent 对象中 showDog() 向下转型 从父到子
- System.out.println("通过 向下转型");
- Dog dog1 = (Dog) parent; //这里为什么可以转,∵父类本身就是Dog()
- dog1.showDog();
-
- // 将父转换成子———— 运行时 编译阶段程序不报错,∵ 程序不清楚这里的parent是 cat 还是 dog
- Cat cat = (Cat) parent; // 将parent(Dog) 强转 Cat 报错
- // 异常 ,java.lang.ClassCastException 类型转换异常
-
- //是否可以直接将dog1 强转 cat ?
- // Cat cat1 = (Cat) dog1; //报错,∵ 在17行已经明确的将 父类parent 转为 dog1
- }
- }
下一篇文章:抽象类