赞
踩
多态是什么?
例如:打印机分为黑白打印机和彩色打印机,在黑白打印机情况下打出来为黑白,在彩色打印机情况下打印出来为彩色
多态就是事物的多种形态,一个对象在不同条件下所表现的不同形式
多态存在的三个必要条件
- 父类类型 变量名 = new 子类类型();
- 然后通过 变量名.方法名()调用在子类中重写的方法
- 多态体现为父类引用变量可以指向子类对象:定义了一个父类类型的引用,指向新建的子类类型的对象,由于子类是继承他的父类的,所以父类类型的引用是可以指向子类类型的对象的
此处举例Animal是父类,Dog是子类
- Animal dog = new Dog(); //Animal是引用类型,Dog是实际类型
- System.out.println(dog.age) //dog的引用类型是Animal,所以取到的是父类Animal中的值,说白了dog是属于Animal类,Animal中变量的值是多少就通过对象就取得多少
父类Animal:
- //父类
- public class Animal {
-
- public int age = 11;
-
- }
子类Dog:
- //子类
- public class Dog extends Animal {
-
- public int age = 33;
-
- }
启动项:
- public class DemoApplication {
-
- public static void main(String[] args) {
-
- //父类类型 对象 = new 子类类型()
- Animal dog = new Dog();
- System.out.println(dog.age);
- }
- }
控制台打印输出:父类中定义的age
11
此处举例Animal是父类,Dog是子类
- Animal dog = new Dog(); //Animal是引用类型,Dog是实际类型
- dog.eat(); //变量dog的实际类型是Dog,即是由Dog 这个实际类型new出来的,因此dog.eat() 调用的应该是子类Dog中重写的方法
父类Animal:
- //父类
- public class Animal {
-
-
-
- public void eat() {
- System.out.println("午餐吃狗粮");
- }
-
- }
子类Dog:
- //子类
- public class Dog extends Animal {
-
- @Override
- public void eat() {
- System.out.println("晚餐吃狗粮");
- }
- }
启动项:
- public class DemoApplication {
-
- public static void main(String[] args) {
-
- //父类类型 对象 = new 子类类型()
- Animal dog = new Dog();
-
- dog.eat();
- }
- }
控制台打印输出:调用的是子类中重写的方法
晚餐吃狗粮
在子类Dog中右键选择Generate
选择Override Methods然后点击生成
- 多态情况下,子类和父类存在同名的成员变量时,访问的时父类的成员变量
- 多态情况下,子父类存在同名的非静态成员方法时,访问的是子类中重写的方法
- 多态情况下,子父类存在同名的静态成员变量成员方法时,访问的是父类的成员函数
- 多态情况下,不能访问子类独由的方法
对于子类独有的方法,父类无法访问,
父类Animal保持不变:
- //父类
- public class Animal {
-
- public void eat() {
- System.out.println("午餐吃狗粮");
- }
-
- }
子类Dog:增加子类读有的方法walk()
- //子类
- public class Dog extends Animal {
-
-
- public void walk(){
- System.out.println("子类独有的方法");
- }
-
-
- @Override
- public void eat() {
- System.out.println("晚餐吃狗粮");
- }
- }
启动项:walk()方法爆红,即编译报错,
根据多态成员方法中编译看左边,运行看右边的原理
Animal dog = new Dog();
可知 左边的Animal引用类型中没有walk()这个方法,故编译不通过,编译爆红
-
- public class DemoApplication {
-
- public static void main(String[] args) {
-
- //父类类型 对象 = new 子类类型()
- Animal dog = new Dog();
-
- dog.eat(); //访问的是子类中重写的方法
-
-
- dog.walk(); //walk方法爆红,即编译报错,编译看左边,dog类的实例对象Animal没有walk()这个方法,所以编译报错
-
-
- }
- }
那么想要直接访问子类独有的方法,该如何解决呢,由此引出了引用类型转换
先了解什么向上转型(儿子变父亲)
向上转型:
多态本身是子类向父类向上转换(自动转换)的过程,这个过程是默许的,当父类引用指向一个子类对象时,就是向上转型
Animal dog = new Dog()
左边的Animal是引用类型,而dog是由右边的Dog实例对象new出来的,在上面这个等式中,左边的引用Animal指向了子类的对象dog,原本是子类对象的dog完成了向上转型
对于父类和子类的关系,直接用图来描述
Animal父类是大范围的类型,而Cat和Dog类均属于动物类的子类,所以对于子类这种范围小的,我们可以自动转型给父类的变量,儿子向上转型,父亲是唯一的,因此是自动转换
使用格式:
父类类型 变量名 = new 子类类型();
Animal dog = new Dog()
通过由实例变量Dog类new出来的变量dog作为中介,使得引用变量Animal有所指向,从而完成了向上转型
相当于是
Animal dog = (Animal) new Dog()
向上转型是一个子类变成父类的过程,下面介绍向下转型
向下转型:
向下转型是父类向子类转换的过程,这个过程需要强制转换(父亲变儿子肯定是需要条件的),一个可以将父类对象转换为子类对象,可以使用强制类型转换的格式,这便是向下转型
继续拿图说话
对于Dog、Cat这些子类来说,他们只是父类Animal的一部分,而对于父类来说。他拥有更多的子类 牛、羊等,所以一旦父类要转换成子类,就必须指定要变成哪个子类,必须有指向性,所以向下转型才是强制转换
使用格式:
向上转型
父类类型 变量名 = new 子类类型();
Animal dog = new Dog()
向下转型
子类类型 子类变量名 = (子类类型) 父类变量名
Dog dog1 = (Dog) dog;
dog1.walk; //此时可以使用子类独有的方法了
代码示例如下:
父类Animal:
- //父类
- public class Animal {
-
- public void eat() {
- System.out.println("午餐吃狗粮");
- }
-
- }
子类Dog:包含有子类独有的方法walk()
- //子类
- public class Dog extends Animal {
-
-
- public void walk(){
- System.out.println("子类独有的方法");
- }
-
-
- @Override
- public void eat() {
- System.out.println("晚餐吃狗粮");
- }
- }
启动项:
通过
Dog dog1 = (Dog) dog;完成向下转型再利用向下转型成功的子类对象dog1调用子类中独有的方法walk()
-
- public class DemoApplication {
-
- public static void main(String[] args) {
-
- //父类类型 对象 = new 子类类型()
- Animal dog = new Dog();
- //向下转型
- //子类类型 子类变量名 = (子类类型) 父类变量名
- Dog dog1 = (Dog) dog;
-
- dog.eat(); //访问的是子类中重写的方法
-
- //通过向下转型的子类对象调用子类独有的方法
- dog1.walk(); //walk方法爆红,即编译报错,编译看左边,dog类的实例对象Animal没有walk()这个方法,所以编译报错
-
-
- }
- }
控制台打印输出:
- 晚餐吃狗粮
- 子类独有的方法
所以对于多态中,无法使用子类特有的方法也通过向下转型,将父类类型强制转换为某个子类类型后,再进行方法的调用
虽然可以通过向下转型可以调用子类独有的方法,但也会产生下面的问题
增加一个子类Cat类,
Cat类中有其独有的方法sleep()
-
- //Cat类通过extends关键字继承父类Animal
- public class Cat extends Animal {
-
-
-
- public void sleep(){
- System.out.println("Cat类独有的方法");
- }
-
- @Override
- public void eat() {
- System.out.println("晚餐吃猫粮");
- }
- }
父类Animal:
- //父类
- public class Animal {
-
- public void eat() {
- System.out.println("午餐吃狗粮");
- }
-
- }
子类Dog类:
- //子类
- public class Dog extends Animal {
-
-
- public void walk(){
- System.out.println("Dog类独有的方法");
- }
-
-
- @Override
- public void eat() {
- System.out.println("晚餐吃狗粮");
- }
- }
启动项:
- public class DemoApplication {
-
- public static void main(String[] args) {
-
- //向上转型
- //父类类型 对象 = new 子类类型()
- Animal cat = new Cat();
- //向下转型
- //子类类型 子类变量名 = (子类类型) 父类变量名
- Dog dog1 = (Dog) cat;
-
-
- //通过向下转型的子类对象调用子类独有的方法
- dog1.walk(); //walk方法爆红,即编译报错,编译看左边,dog类的实例对象Animal没有walk()这个方法,所以编译报错
-
- }
- }
控制台打印输出:爆出异常ClassCastException,即类型转换异常
分析:为什么会爆出类型转换异常
- 因为 在启动项中,向上转型的过程,Animal cat = new Cat(); cat对象是由子类Cat构造出来的
- 而向下转型的过程中 Dog dog1 = (Dog) cat; 却将其变成了Dog 类的对象,
- Dog类和Cat类都是Animal类的儿子类 ,
- 上面的步骤中的第二步将子类Cat的对象cat变成了兄弟类的对象dog,这就不是向下转型了,因此会报类型转换异常
那么如何避免这种异常呢 ,就需要使用instanceof关键字
Java为我们提供了一个关键字instanceof,它可以帮助我们避免出现ClassCastException这样的异常,
格式:
变量名 instanceof 数据类型
解释:
直接拿启动项来进行说明,
代码示例如下:
-
- public class DemoApplication {
-
- public static void main(String[] args) {
-
- //向上转型
- //父类类型 对象 = new 子类类型()
- Animal animal = new Cat();
-
-
- //向下转型
- //子类类型 子类变量名 = (子类类型) 父类变量名
- if ( animal instanceof Cat){
- Cat cat = (Cat) animal;
- cat.sleep();
- }else if(animal instanceof Dog){
- Dog dog = (Dog) animal;
- dog.walk();
- }
-
- }
- }
在进行了向上转型之后,在向下转型的过程中利用if语句来进行判断,而判断条件正是向上转型产生的变量与子类之间的关系
控制台打印输出:
Cat类独有的方法
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。