java-多态

多态的作用是消除类型之间的耦合性,它允许将多种类型(从同一个基类导出的)视为同一类型来处理。让代码只操纵基类,这样,如果要添加一个新类,也不影响到原来的代码

一、向上转型
把某个对象的引用视为对其基类型的引用的做法称作向上转型

class Food{
     public void cook(){
         System.out.println("Food is cooking.");
     }
}

class Cake extends Food{
    public void cook() {
        System.out.println("Cake is cooking.");
    }
}

public class Restaurant {

    public static void cook(Food food){
        food.cook();
    }

    public static void main(String[] args) {
        Cake cake = new Cake();
        cook(cake);
    }
}

Restaurant.cook()方法接受Food的引用,同时也接受任何导出自Food的类。Cake从Food继承而来,所以Food的方法存在在Cake中,所以可以将Cake的引用传递给Food。如果cook()方法直接接受一个Cake作为引用,之后每次添加新的子类的时候,都需要重新编写一个cook()方法。

我们可以根据自己的需求对系统添加多个新类型,但是却不需要改变cook()方法。只有基类接口通信,操纵基类接口的方法不需要改动就可以应用新类。

二、方法调用绑定
接受基类引用的时候,编译器无法知道这是基类还是引用。
默认的绑定方法为前期绑定,java当中的方法只有final,static,private和构造方法是前期绑定

后期绑定(动态绑定)
在运行时根据对象的类型进行绑定,编译器一直不知道对象的类型,但是方法调用机制能找到正确的方法体,并加以调用。

class Food{
     public void cook(){
         System.out.println("Food is cooking.");
     }
}

class Cake extends Food{
    public void cook() {
        System.out.println("Cake is cooking.");
    }
}

class Biscuit extends Food{
    public void cook(){
        System.out.println("biscuit is cooking.");
    }
}

class Apple extends Food{
    public void cook(){
        System.out.println("apple is cooking.");
    }
}

class FoodGenerator{
//是一个工厂,随机的返回一个引用
    private Random random = new Random(47);
    //调用next()方法时无法知道具体类型是什么
    public Food next(){
        switch(random.nextInt(3)){
        default:
            case 0:return new Cake();
            case 1:return new Biscuit();
            case 2:return new Apple();
            //在return时产生向上转型
        }
    }
}

public class Restaurant {
    private static FoodGenerator foodGenerator = new FoodGenerator();

    public static void main(String[] args) {
        Food[] foods = new Food[5];
        for (int i = 0; i < foods.length; i++) {
            foods[i] = foodGenerator.next();
        }
        for (Food food : foods) {
            food.cook();
        }
    }
}

输出:

apple is cooking.
apple is cooking.
biscuit is cooking.
apple is cooking.
biscuit is cooking.

编译器不需要获得任何特殊信息就能进行正确的调用,对cook()方法的调用都是动态绑定进行的

三、缺陷
1、private方法被自动认为是final方法,而且对导出类是屏蔽的,所以不能被重载

public class DefectDemo {
    private void f(){
        System.out.println("private f().");
    }
    public static void main(String[] args) {
        DefectDemo defectDemo = new Defect1();
        defectDemo.f();
    }
}

class Defect1 extends DefectDemo{
    public void f(){
        System.out.println("public f().");
    }
}

输出

private f().

Defect1类中的f()方法被认为是一种全新的方法。所以调用的仍然是基类的f().

2、域和静态方法
任何域访问操作都由编译器解析,因此不是多态的。
如果某个方法是静态的,它的行为就不具有多态的,静态方法是与类,而并非与单个的对象相关联的。

四、构造器和多态

调用构造器遵循的顺序
1)在其他任何事物发生之前,将分配给对象的储存空间初始化成二进制的零。
2)调用基类构造器
3)按照声明的顺序调用成员的初始化方法
4)调用导出类的构造器主体

class Meal{
    void cook(){
        System.out.println("Meal is cooking");
    }

    public Meal() {
        System.out.println("Meal before cook");
        cook();
        System.out.println("Meal after cook");
    }
}

class Fish extends Meal{
    private int num = 2;

    public Fish(int num) {
        this.num = num;

        System.out.println("Fish.Fish(), num = " + num);
    }
    void cook(){
        System.out.println(num + " Fishes is cook");
    }
}

public class PolyMethod {
    public static void main(String[] args) {
        new Fish(3);
    }
}

结果:

Meal before cook
0 Fishes is cook
Meal after cook
Fish.Fish(), num = 3

在一个构造器的内部调用了正在构造的对象的某个动态绑定方法,用到了被覆盖后的方法,但是它还没有初始化,所以才会输出0 Fishes is cook一个动态绑定的方法调用会向外深入到继承层次结构内部,它可以调用导出类里的方法。

编写构造器时的一条准则

用尽可能简单的方法是对象进入正常状态,如果可以的话,避免调用其他方法

在清理时,如果某个子对象要依赖于其他对象,销毁的顺序应该和初始化的顺序相反,先对导出类清理,然后才是基类。可以引入计数来跟踪仍旧访问着共享对象的对象数量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值