多态:父类引用指向子类对象,从而产生多种状态
例如:
创建一个父类:动物类(Animal),创建两个子类:狗类(Dog)和猫类(Cat)
Animal dog = new Dog();
Animal cat = new Cat();
例中本来应该为 Dog类型的 dog对象,此时在上述定义时也被类型提升至 Animal类型,类似于语句
double num = 10;
10位 int类型数据存入 double类型中类型提升后存入,称之为向上转型
有了向上转型,自然也有向下转型
由于 dog被定义为了 Animal类型,导致 dog只能调用父类中存在方法,无法访问到子类中独有的属性和方法,
这时就可以在父类中写一个抽象方法,将父类定义为一个抽象类,关键字:abstract
也可以使用向下转型,将父类类型的对象转化为子类类型的对象,调用子类中的特有的方法,类似于 double强制转换为 int类型
形式:
Dog d = (Dog)dog;
d.子类方法名(实参列表);
多态,抽象类具体使用,从代码入手:
// 在一个类中存在抽象方法,这个类也必须是抽象类
public abstract class Animal {
private String name;
private int age;
int num = 10;
public abstract void eat();
public void sleep() {
System.out.println("动物睡觉");
}
public static void show(){
System.out.println("父类中的show方法");
}
}
public class Dog extends Animal {
// 方法重写
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
public class Cat extends Animal {
int num = 20;
// 方法重写
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public static void show() {
System.out.println("子类中的show方法");
}
}
public class Person {
// 人喂狗
public void feedDog(Dog dog) {
dog.eat();
}
// 人喂猫
public void feedCat(Cat cat) {
cat.eat();
}
// 人喂动物
public void feedAnimal(Animal animal) {
System.out.print("人喂");
animal.eat();
}
}
public class UseAnimal {
public static void main(String[] args) {
Person person = new Person();
Dog dog1 = new Dog();
Cat cat1 = new Cat();
person.feedDog(dog1);
person.feedCat(cat1);
/*定义两个子类类型 Dog类型和Cat类型的对象,对于每一个人喂食动物都需要定义一个对应对象的喂食,导致代码冗杂,不利于代码扩展*/
Animal dog2 = new Dog();// 类似于int类型数据放入double类型中,父类可以接受任意子类
Animal cat2 = new Cat();
// 父类中有抽象方法 eat,子类中有重写方法 eat
dog2.eat();// 在父类中找到了他的抽象方法,进而在子类中找到了他的重写方法
cat2.eat();
// 使用多态,可以接受任意子类,且只需要定义一个方法,提高程序的扩展性,只需要添加其他的类
person.feedAnimal(dog2);// 扩展性更好,同一个父类,在不同的时刻表示不同的状态(子类)
person.feedAnimal(cat2);
/*
运行时,以下代码找子类中的 sleep方法
编译时,以下代码找父类中的 sleep方法
运行时,因为父类中含有 sleep方法,所以子类继承后也有 sleep方法,但是在不能说运行时找父类中的 sleep方法,而编译时,如果父类中没有 sleep方法则会报错
为了方便记忆,可以理解为:编译在左边,运行在右边
*/
dog2.sleep();
cat2.sleep();
/*
对于静态方法如果父类和子类中都存在调用时调用的是父类中的方法
对应静态成员的方法: 编译在左边,运行在左边
*/
cat2.show();// 打印结果:父类中的show方法
// 成员变量也是以父类中的为主
System.out.println(cat2.num);// 打印出来的是父类中的 num的值 10
}
}
代码运行结果:
接下来为运行结果一一分析:
对①定义两个子类类型 Dog类型和Cat类型的对象,定义一个Person类的对象,通过传入不同子类型的参数来打印结果
对②定义两个父类类型 Animal类型的对象,通过访问父类中的抽象方法 eat再去找子类中的重写方法.故父类中若有抽象方法,则父类类型的对象可以调用子类中的重写方法
对③定义两个父类类型 Animal类型的对象,定义一个Person类的对象,通过调用Animal的 eat方法自动对子类进行区分调用不同子类中的方法,减少代码量,方便后期修改
对④父类类型的对象只能调用父类类型的方法,若 sleep位于子类中,则编译器会报错
对⑤对于静态方法如果父类和子类中都存在调用时调用的是父类中的方法
对⑥对于静态成员变量也是父类中的优先