Java多态实例详解三步走(一)【具体类多态】

多态的概念:多态其实是同一个对象在不同时刻体现出来的不同状态

多态分为三种:

本文讲解为具体类多态,也是基础。

多态的前提:
1、有继承或者实现关系。
2、有方法重写(因为多态就是靠方法重写来体现不同状态的)。
3、要有父类或者父接口引用指向子类对象。
格式:父 f = new 子()

多态中的成员访问特点:
1、成员变量
编译看左边(父类),运行看左边(子类)。

2、构造方法
创建子类对象的时候,访问父类的构造方法,对父类的数据进行初始化

3、成员方法
编译看左边(父类),运行看右边(子类)
(因为成员方法存在方法重写,所以成员方法访问的时候访问的是子类)。

4、静态方法
编译看左边(父类),运行看左边(子类)
(静态和类相关,算不上重写,所以,访问还是左边)

多态的好处:
1、提高了代码的维护性(由继承来保证)
2、提高了代码的扩展性(由多态来保证)

对于下面的代码,是没有用多态来写的,如果创建一个新的子类,就要在工具类中多增加一个方法,如果创建很多个,那么同样的代码要重复好多遍,如果用多态,就可以保证在多增加一个类的同时,工具类中不用再增加一个方法。这就提高了代码的扩展性。

代码1:


public class Main {
    public static void main(String[] args) {
        cat c = new cat();
        dog d = new dog();
        AnimalTool.useCat(c);
        AnimalTool.useDog(d);
    }
}

//创建工具类
class AnimalTool {
    private AnimalTool() {}

    //调用猫的功能
    public static void useCat(cat c) {
        c.eat();
        c.sleep();
    }

    //调用狗的功能
    public static void useDog(dog d) {
        d.eat();
        d.sleep();
    }

    //如果我创建一个新的子类,就要在这里多增加一个方法,如果创建很多个,那么同样的代码要重复好多遍

}

//创建父类
class Animal {
    public void eat() {
        System.out.println("eat");
    }

    public void sleep() {
        System.out.println("sleep");
    }
}

//创建子类
class cat extends Animal {
    public void eat() {
        System.out.println("猫吃鱼");
    }

    public void sleep() {
        System.out.println("猫卧着睡");
    }
}

//创建子类
class dog extends Animal {
    public void eat() {
        System.out.println("狗吃肉");
    }

    public void sleep() {
        System.out.println("狗趴着睡");
    }
}

如果用多态,就会节省很多代码。因为只需要修改AnimalTool类,所以只给出了AnimalTool修改后的代码。

代码2:


class AnimalTool {
    private AnimalTool() {}
    //运用多态就能很好的解决代码重复的问题
    public static void useAnimal(Animal a) {
        a.eat();
        a.sleep();
    }
}

多态的弊端:不能使用子类的特有功能。

如果想要解决就涉及到对象的转型问题

向上转型:Fu f = new Zi();
向下转型:Zi z = (Zi)f; //要求f必须是能够转换为Zi类的

例如下面的代码:


public class Main {
    public static void main(String[] args) {
        Animal a = new Dog();//向上转型

a.eat();
//这里调用eat方法是没有问题的
//需注意的是,这里调用的eat方法是Dog中的方法,而不是Animal中的eat方法。
//也就是说,这里调用的是重写后的eat方法
//所以这里输出的应该是 狗吃东西

//这里需要注意,此时输出成员变量flag,输出的是父类中的值
System.out.println(a.flag);//输出的值为78

//调用Dog中的特有方法会报错,可以把注释去掉试一试
//a.lookDoor();

//出现以上两种情况是因为,a 是 Animal 类型的。
//这就对应了上边说的,编译看左边,运行看右边。

//但是,如果我现在就想用Dog中的lookDoor方法,怎么办

//那就把父类的引用强制转换为子类的引用(向下转型)。
Dog d = (Dog)a;
//这时再使用lookDoor方法就没有问题了
d.lookDoor();

//可能有的小伙伴要说了,我直接Dog d = new Dog() 不就行了。
//这样做也可以,但是会浪费内存

a = new Cat()//我把 a 的地址换为指向Cat,内存中就是猫的
a.eat();//输出 猫吃东西
//所以,我将a转化为Cat是没问题的
Cat c = (Cat)a;

// Dog dd = (Dog)a;
//因为此时内存中是猫,所以将此时的a转化为Dog是会报错
//ClassCastException(类型转换异常),一般在向下转型时出现

//以上也就解释了在向下转型时,要求f必须是能够转换为Zi类的这个条件。
}
}

class Animal {
int flag = 78;
public void eat() {}

}

class Dog extends Animal {
int flag = 23;
public void eat() {
System.out.println(“狗吃东西”);
}

public void lookDoor() {
System.out.println(“狗看门”);
}

}

class Cat extends Animal {
int flag = 99;
public void eat() {
System.out.println(“猫吃东西”);
}

public void playGame() {
System.out.println(“猫玩游戏”);
}
}