【7】Java多态:向上转型、向下转型、instanceof运算符
一、概念
多态,是允许不同类的对象对同一消息做出不同的响应,是面向对象最核心的特征。
举个栗子,打印机,有黑白的打印机,可以打印黑白效果,有彩色打印机,可以打印彩色效果。
再举个栗子,上课铃响了,上体育课的学生跑到操场上站好,上语文课的学生在教室里坐好一样。
再再举个栗子,动物们都会叫,猫咪叫声是喵喵喵,狗叫声是汪汪汪。
二、分类
1)编译时多态(也叫设计时多态,举例如方法重载)
2)运行时多态(程序运行时决定调用哪个方法)【一般情况下在Java中提到多态指的是运行时多态。】
三、多态存在的三个必要条件
1)要有继承关系
2)子类要重写父类的方法
3)父类引用指向子类对象
四、简单理解
如果不去理解多态的原理,就从使用的角度来讲,可以总结出了多态无非就是三句话:
比如我们有一个父类Father,有一个子类Children
1、向上转型是自动的。即Father f = new Children()是自动的,不需要强转
2、向下转型要强转。即Children c = new Father()是无法编译通过的,必须要Children c = (Children)new Father(),让父类知道它要转成具体哪个子类
3、父类引用指向子类对象,子类重写了父类的方法,调用父类的方法,实际调用的是子类重写了的父类的该方法。即Father f = new Children(),f.toString()实际上调用的是Children中的toString()方法
参考资料:https://www.cnblogs.com/zhilu-doc/p/5338264.html
五、向上转型&向下转型&instanceof运算符
父类Animal
public class Animal { //属性:昵称、年龄 private String name; private int month; //无参构造 public Animal(){ } //带参构造 public Animal(String name, int month){ this.name=name; this.month=month; } //get、set方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } //方法:吃东西 public void eat(){ System.out.println("动物都有吃东西的能力"); }; // 方法:静态方法say public static void say(){//static静态方法,只能被继承,不能被重写 System.out.println("动物间打招呼"); } }
子类Cat
public class Cat extends Animal { //属性:体重 private double weight; //无参构造 public Cat() { } //带参构造 public Cat(String name, int month, double weight) { super(name, month);//调用父类构造,也可以这个写法this.setMonth(month) this.weight = weight; } //get、set方法 public double getWeight() { return weight; } public void setWeight(double weight) { this.weight = weight; } //方法:跑动(cat独有的方法) public void run() { System.out.println("小猫快乐的奔跑"); } //方法:吃东西(重写父类方法) @Override//快速生成重写方法快捷键CTRL+O public void eat() { System.out.println("猫吃鱼~~"); } //方法:子类的静态方法,不加@Override,与父类无重写关系 public static void say(){ System.out.println("小猫碰胡须"); } }
子类Dog
public class Dog extends Animal{ //属性:性别 private String sex; public Dog(){ } public Dog(String name,int month,String sex){ this.setMonth(month); this.setName(name); this.setSex(sex); } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } //方法:睡觉 public void sleep(){ System.out.println("小狗有午睡的习惯"); } //方法:吃东西(重写父类方法) @Override public void eat() { System.out.println("狗吃肉~~"); } }
测试类Test
public class Test { public static void main(String[] args) { /*向上转型、隐式转型、自动转型 Animal two=new Cat(),父类引用指向子类实例, 可以调用子类重写父类的方法two.eat()以及父类派生的方法two.setMonth(2), 无法调用子类独有方法two.run() */ // 应用多态的实现,多种编码方式 Animal one= new Animal() {};//1第一种,常规方法 Animal two=new Cat();//2第二种Animal指向Cat实例 Animal three=new Dog();//3第三种Animal指向Dog实例 one.eat(); two.eat(); three.eat(); two.setMonth(2); two.getMonth(); // two.run();//无法调用 /* 注意:父类中的静态static方法say无法被子类重写,所以向上转型之后,只能调用到父类原有的静态方法say 如果想调用子类的say方法,可以通过向下转型转回来 小类转型为大类 */ Cat cat=(Cat)two; cat.say(); System.out.println("===========向上转型&向下转型分割线============"); /*向下转型、强制类型转换 * 子类引用指向父类对象,此处必须进行强转Cat temp=two-->Cat temp=(Cat)two, * 可以调用子类特有的方法 * 必须满足转型条件才能进行强转 * instanceof运算符:返回true/false,可以规避异常的产生 * 判断左边的对象A是否为右边B的实例,或者对象A是否有B类的特征 */ if(two instanceof Cat){//如果two是Cat的实例 Cat temp=(Cat)two;//向下转型 temp.eat(); temp.run();//可以调用子类独有方法 temp.getMonth(); System.out.println("two可以转换为Cat类型"); } if(two instanceof Dog){ Dog temp2=(Dog)two;//cat和dog不兼容,所以这里不能强转 temp2.eat(); temp2.sleep(); temp2.getSex(); System.out.println("two可以转换为Dog类型"); } if(two instanceof Animal){ System.out.println("Animal"); } if(two instanceof Object){ System.out.println("Object"); } } }