思维导图:
1. 多态的概念
多态通俗的讲就是多种形态,同一个动作,作用在不同对象上,所产生不同的形态。
例如下图:
2. 多态的实现条件
Java中,多态的实现必须满足以下几个条件:
1. 必须在继承体系下
2. 子类必须重写父类中的方法
3. 通过父类的引用调用重写方法
多态体现:在代码运行时,当传递不同对象时,会调用对应类中的方法
看以下代码:
public abstract class Animal {String name;int age;public Animal(String name,int age){this.name = name;this.age = age;}public void eat(){System.out.println(name+"吃粮食");}
}public class Dog extends Animal {public Dog(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println(name+"爱吃骨头");}
}
///public class Cat extends Animal {public Cat(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println(name+"爱吃鱼");}
}/public class TestAnimal {//编译器在编译代码时,并不知道调用Dog还是Cat中的eat方法//等程序运行起来后,形参a引用的具体对象确定后,才知道调用哪个方法//注意:此处的形参类型必须是父类类型才可以public static void eat(Animal a){a.eat();}public static void main(String[] args) {Dog dog = new Dog("旺财",2);Cat cat = new Cat("糯米",1);eat(dog);eat(cat);}
}
3. 重写
重写(override)也称为覆盖,重写是子类对父类非静态,非private修饰,非final修饰,非构造方法等的实现过程进行重新编写,返回值和形参都不能改变,核心重写。
方法重写规则:
1:子类重写父类方法时,一般必须与父类方法原型保持一致:修饰符 返回值类型 方法名(参数列表)要完全一致
2:JDK7以后,被重写的方法返回值类型可以不同,但是必须具有父子关系
3: 访问权限不能比父类父类中被重写的方法的访问权限低。
4:父类中被static private 修饰的方法,构造方法都不能被重写
5:重写的方法可以用@Override注解来显示指定
重写和重载的区别:
即:方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现
静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型确定具体调用哪个方法。典型代表:重载
动态绑定:也成为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时才能够确定调用哪个类的方法
4:向上转型和向下转型
向上转型:一个父类对象的引用指向一个子类对象的实例。
语法格式:父类类型 对象名 = new 子类类型();如:Animal animal = new Cat("小七",1);
animal是父类类型,但可以引用一个子类对象,即:可以将一个子类对象当成父类对象来引用。因此:向上转型是安全的,因为是从小范围向大范围的转换。
使用场景:1:直接赋值 2:方法传参 3:方法返回
请看如下代码:
public class TestAnimal {//方法传参:形参为父类类型引用,可以接受任意子类对象public static void eat(Animal a){a.eat();}//作为返回值:返回任意子类对象public static Animal buyAnimal(String var){if("狗" == var){return new Dog("小七",2);}else if("猫" == var){return new Cat("汤圆",1);}else{return null;}}public static void main(String[] args) {Animal dog = new Dog("旺财",2); //直接赋值:子类对象赋给父类对象Aniaml cat = new Cat("糯米",1);eat(dog);eat(cat);Animal animal = buyAnimal("狗");animal.eat();animal = buyAnimal("猫");animal.eat();}
}
向上转型的优点:让代码实现更简单灵活
向上转型的缺陷:不能调用到子类特有的方法
向下转型
将一个子类对象向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转型。
public class TestAnimal {public static void eat(Animal a){a.eat();}public static void main(String[] args) {Dog dog = new Dog("旺财",2);Cat cat = new Cat("糯米",1);Animal animal = dog;animal.eat();// animal.bark(); 编译器报错,Animal类中没有bark方法dog = (Dog)animal;dog.bark();animal = cat;animal.eat();// animal.mew(); 编译器报错,Animal类中没有mew方法cat = (Cat)animal;cat.mew();}
}
向下转型不安全,万一转型失败,运行就会抛异常。Java中为了提高向下转型的安全性,引入了instanceof,如果该表达式为true,则可以安全转换
public class TestAnimal {public static void eat(Animal a){a.eat();}public static void main(String[] args) {Dog dog = new Dog("旺财",2);Cat cat = new Cat("糯米",1);Animal animal = dog;animal.eat();animal = cat;animal.eat();if(animal instanceof Dog){dog = (Dog)animal;dog.bark();}if(animal instanceof Cat){cat = (Cat)animal;cat.mew();}}
}
多态的优点
1:能够降低代码的“圈复杂度”,避免使用大量的if-else
假如我们现在要打印多个形状而不是一个形状
public static void drawShapes(){Ret ret = new Rect();Cycle cycle = new Cycle();Flower flower = new Flower();String[] shapes = {"cycle","ret","flower","cycle","flower"};for(String shape : shapes){if(shape.equals("cycle")){cycle.draw();}if(shape.equals("ret")){ret.draw();}if(shape.equals("flower")){flower.draw();}}}
如果使用多态,代码更简单:请看如下代码:
public static void drawShapes(){Shape[] shapes = {new Cycle(),new Rect(),new Cycle(),new Flower()};for(Shape shape : shapes){shape.draw;}}
2. 可扩展能力强
增加一种新的形状,使用多态的方式代码改动成本低
class Tringle extends Shape{@Overridepublic void draw(){System.out.println("三角形");}
}