Java面向对象(四)
多态
什么是多态
多态:理解为一个事物的多种形态
比如在现实生活中每个人都会扮演着不同的身份,比如张三可以是学生身份,可以是一个孩子身份,可以是教师身份,可以是父亲的身份。
Java中多态的体现
子类对象多态性:父类的引用指向子类的对象(或者子类的对象赋给父类引用)
举个例子:
1 public class Test { 2 public static void main(String[] args) { 3 //通过多态的形式,将父类引用指向子类的对象 4 Person person=new Student(); 5 } 6 } 7 8 class Person{ 9 10 } 11 class Student extends Person{ 12 13 }
在多态场景下,调用方法时分为编译时和执行时两种。
①编译时,认为声明的对象是一个父类的对象(实际上生成的是子类对象),看父类中有什么属性成员和方法成员(即被重写的方法)。
②执行时,实际上执行的方法是子类重写父类的方法。
如果出现子类没有重写相对应的方法时,才会去执行父类的方法。
简称:编译看左边,运行看右边。
举个例子:
父类:
1 class Person { 2 public void eat() { 3 System.out.println("父亲吃饭"); 4 } 5 public void show(){ 6 System.out.println("这是一个show方法"); 7 } 8 }
子类:
1 class Student extends Person { 2 @Override 3 public void eat() { 4 System.out.println("儿子吃饭"); 5 } 6 }
测试类:
1 public class Test { 2 public static void main(String[] args) { 3 //通过多态的形式,将父类引用指向子类的对象 4 Person person=new Student(); 5 //子类重写了eat方法 6 person.eat(); 7 //子类没有重写eat方法 8 person.show(); 9 } 10 }
效果展示:
执行时会先执行子类重写父类的方法,如果没有重写方法,才调用父类的方法。
多态的缺点
①在使用多态时,子类的特有方法是会被屏蔽掉的,因为编译的时候父类并没有子类的方法所以是没办法调用的。
②创建子类对象的特有属性和特有方法也会加载到内存中,从而导致内存的浪费。
举个例子:
父类:
1 class Person { 2 public void eat() { 3 System.out.println("父亲吃饭"); 4 } 5 public void show(){ 6 System.out.println("这是一个show方法"); 7 } 8 }
子类:
1 class Student extends Person { 2 @Override 3 public void eat() { 4 System.out.println("儿子吃饭"); 5 } 6 public void play(){ 7 System.out.println("儿子打游戏"); 8 } 9 }
测试类:
1 public class Test { 2 public static void main(String[] args) { 3 //通过多态的形式,将父类引用指向子类的对象 4 Person person=new Student(); 5 //儿子中独特的play()方法 6 //person.play() 由于父类并没有这个play()方法,所以编译时无法调用play()方法 7 } 8 }
由于父类并没有这个方法,无法调用play()方法。
多态的优点
①提高代码的维护性,比如一个饲养员有一个饲养的方法,不可能每有一种动态就写一个饲养方法这样不合理,而多态的实现只需要写一个方法里面调用不同对象的方法,代码维护性提高。
②提高代码的扩展性,只需要改变每个对象的方法即可,不需要修改饲养员的饲养方法。
多态的前提
多态的两个前提条件是:
①存在继承关系:多态是基于继承关系建立的,即一个类可以派生出另一个类。在多态中,存在一个父类和多个子类,子类继承了父类的属性和方法。
②存在重写关系:多态要求子类能够重写父类的方法。子类在重写方法时可以根据自己的需求对方法进行不同的实现,从而实现实现多态的效果。
向上转型和向下转型
举个图例:
向上转型
多态就是向上转型:将子类的对象赋值给父类(程序是自动实现的,缺点是失去子类的特有的属性和方法)
语法:
父类 对象名 = new 子类();
向下转型
把父类的对象转为子类的对象(需要强制转换)
语法:
子类类型 对象名 = (子类类型) 父类引用
举个例子:
1 public class Test { 2 public static void main(String[] args) { 3 //向上转型 4 Animal animal=new Cat(); 5 //向下转型,需要强制转换 6 Cat cat = (Cat) animal; 7 cat.eat(); 8 } 9 } 10 11 class Animal{ 12 13 } 14 class Cat extends Animal { 15 public void eat(){ 16 System.out.println("小猫吃鱼"); 17 } 18 } 19 class Dog extends Animal{ 20 public void eat(){ 21 System.out.println("小狗吃骨头"); 22 } 23 }
效果展示:
代码上是没有任何问题的,但是当两者之间没有继承关系时是没办法强转的,否则会ClassCastException类型转换异常。
举个例子:
1 public class Test { 2 public static void main(String[] args) { 3 //向上转型 4 Animal animal=new Cat(); 5 Animal animal1=new Dog(); 6 //向下转型,需要强制转换 7 Dog dog=(Dog) animal;//发现现在编译是没出错哦 8 } 9 } 10 11 class Animal{ 12 13 } 14 class Cat extends Animal { 15 public void eat(){ 16 System.out.println("小猫吃鱼"); 17 } 18 } 19 class Dog extends Animal{ 20 public void eat(){ 21 System.out.println("小狗吃骨头"); 22 } 23 }
发现编译时并没有任何问题,但是效果展示:
就是因为上面代码中,animal是猫的对象它能变成小狗对象吗?很明显,小猫怎么可能变成小狗对吧。
所以运行时会出现类型转换异常。
instanceof关键字
正因为上面向下转型可能会出现异常,所以这里用到instanceof关键字来判断左边的对象是否为右边的类。
语法:
a instanceof A :判断对象a是否为A类的实例
当对象a是类A的实例:返回true
当对象a不是类A的实例: 返回false
举个例子:
1 public class Test { 2 public static void main(String[] args) { 3 //向上转型 4 Animal animal=new Cat(); 5 Animal animal1=new Dog(); 6 //向下转型,需要强制转换 7 if(animal instanceof Dog){ 8 Dog dog=(Dog) animal;//发现现在编译是没出错哦 9 }else{ 10 System.out.println("animal不是Dog类的实例"); 11 } 12 } 13 } 14 15 class Animal{ 16 17 } 18 class Cat extends Animal { 19 public void eat(){ 20 System.out.println("小猫吃鱼"); 21 } 22 } 23 class Dog extends Animal{ 24 public void eat(){ 25 System.out.println("小狗吃骨头"); 26 } 27 }
效果展示:
判断出来animal并不是Dog类的实例所以不会执行
这句代码:Dog dog=(Dog) animal直接输出"animal不是Dog类的实例"