1.什么是多态?
多态,通俗来讲,就是多种状态。具体来说就是当不同的对象去完成某种行为时会产生不同的状态。比如猫和狗叫时会发出不同的声音;黑白打印机和彩色打印机打印出的颜色不同...
总的来说就是:同一件事发生在不同对象身上就会产生不同的结果。
2.多态的实现条件
Java中要实现多态必须满足以下条件,缺一不可:
1.必须在继承的体系下
2.子类必须对父类的方法进行重写
3.通过父类的引用调用重写的方法
多态的体现:当代码运行时,当传递不同的类对象时,会调用对应类中的方法。
举例如下:
public 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 Cat extends Animal{ //继承Animal父类
public Cat(String name,int age){
super(name,age);
}
@Override
public void eat(){ //对父类方法进行重写
System.out.println(name+"吃鱼");
}
}
public class Dog extends Animal{
public Dog(String name,int age){
super(name,age);
}
@Override
public 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) {
Cat cat=new Cat("卷卷",2);
Dog dog=new Dog("咕咕",3);
eat(cat);
eat(dog);
//上述代码是类的实现者编写的
执行结果如下:以下代码是类的调用者编写的。
3.重写(Override)
重写:也称为覆盖。重写是子类对父类非静态、非private修、非构造方法等的实现过程进行重新编写,返回值和形参都不能改变,即外壳不变,核心重写!
重写的好处在于,子类可以根据自己的需求,定义特定属于自己的行为。即子类可以根据需要实现父类的方法。
方法重写的规则如下:
1.子类再重写父类方法时,一般必须与父类原型保持一致:返回值类型、方法名、(参数列表)要完全一致。
2.被重写的方法返回值类型可以不同,但必须是具有父子关系的。
3.子类访问权限不能比父类中被重写的方法访问权限低。比如:父类方法被public修饰,子类中重写该方法就不能声明为protected。
4.父类被static、private修饰的方法、构造方法不能被重写。
5.重写的方法可以用@Override注解来显式指定。这个注解可以帮助我们进行一些合法性检验。
4.向上转型和向下转型
4.1向上转型
向上转型就是创建一个子类,将它当作父类对象来使用。
语法格式:父类类型 对象名=new 子类类型()
Animal animal=new Cat("卷卷",2);
Animal时父类类型,但可以引用一个子类对象,原因是从小范围向大范围的转换。
猫和狗都是动物,因此将子类对象转化为父类引用是合理的。大范围可以囊括小范围。
使用场景:方法传参、做返回值、直接赋值。
public class TestAnimal {
//方法传参:形参为父类型引用,可以接受任意子类的对象
public static void eat(Animal a){
a.eat();
}
//做返回值:返回任意子类对象
public static Animal buyAnimal(String var){
if("狗".equals(var)){
return new Dog("修狗",1);
}
else if ("猫".equals(var)){
return new Cat("猫咪",1);
}
else {
return null;
}
}
public static void main(String[] args) {
Animal cat=new Cat("卷卷",2); //直接赋值:子类对象赋值给父类对象
Dog dog=new Dog("咕咕",3);
eat(cat);
eat(dog);
Animal animal=buyAnimal("狗");
animal.eat();
animal=buyAnimal("猫");
animal.eat();
}
}
【向上转型优缺点】
优点:让代码实现更加灵活简单。
缺点:不能调用子类特有的方法。
4.2向下转型
将一个子类对象经过向上转型后当成父类方法使用,就无法调用子类中特有的方法,但当恰恰需要这些子类特有方法时,这是将父类引用在还原成子类对象即可,即向下转型。
public class TestAnl {
public static void main(String[] args) {
Cat cat=new Cat("小米",2);
Dog dog=new Dog("小七",1);
//向上转型
Animal animal=cat;
animal.eat();
animal=dog;
animal.eat();//编译失败,编译器将animal当成Animal对象处理
//但是Animal类中没有bark方法
cat=(Cat)animal;//向上转型,程序可通过编译但抛出异常
//animal实际指向是狗,现在要强制还原为猫,无法正常还原
cat.mew();
//animal本来指向就是狗,因此将animal还原成狗是安全的
dog=(Dog)animal;
dog.bark();
}
}
向下转型用的比较少也不安全,为了提高向下转型的安全性,引入了instanceof,如果该表达式为true,则可以安全转换。
public class Testanl2 {
public static void main(String[] args) {
Cat cat=new Cat("小米",2);
Dog dog=new Dog("小七",1);
//向上转型
Animal animal=cat;
animal.eat();
animal=dog;
animal.eat();
if(animal instanceof Cat){
cat=(Cat)animal;
cat.mew();
}
if(animal instanceof Dog){
dog=(Dog) animal;
dog.bark();
}
}
}
5.多态的优缺点
【优点】
1. 能够降低代码的“圈复杂度”(代码中条件语句、循环语句出现的次数),避免大量使用if-else;
2.可拓展能力更强,想要增加新的子类对象直接New一个新类就行,不用再原代码基础上添加if-else改动太多很不方便且成本高。
【缺点】
1.属性没有多态性,当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性。
2.构造方法没有多态性。