Java 之旅

初级09 - 面向对象:多态

2019-08-03  本文已影响0人  晓风残月1994

面向对象的三大基本原则之一 ——多态。多态赋予了你的程序无与伦比的强大功能,是面向对象的灵魂!


面向对象的三大特征:


1. 什么是多态?

这不是多态:

Person p1 = new Person();

下面是多态!Man 和 Woman 都是 Person 的子类:

Person p2 = new Man();
Person p3 = new Woman();

这就体现了对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
也因为一个子类类型的对象永远也是一个父类类型的对象,因此,当声明一个父类型时,总可以传递一个子类型对象。

多态性可以理解为一个事务的多种形态,= 赋值的右边有多种形态的对象来满足左边的需求。

2. 多态的使用

有了对象的多态性以后,在编译期,只能调用父类中声明的方法,但在运行期,实际执行的是子类重写父类后的方法。

假设子类分别覆盖了父类的eat()walk()

Person p2 = new Man();
p2.eat();
p2.walk();

那么上面这样写是ok的,在编译期,这两个方法指的仍是父类Person的实例方法,运行时,子类对象接收到了方法调用的消息(同时可能还有方法的参数),其作为接收者要进行响应,发现子类进行了重写,于是动态调用重写后的方法。

接着这个思路,如果对p2调用只有子类Man中才存在的实例方法,在编译阶段会报错,因为编译时认为p2 属于赋值操作左边的类型,认为是Person类型,而Person并没有定义站着尿尿这个方法。

p2.站着尿尿();

结论

3. 虚拟方法调用(多态的情况下)

子类中重写了父类方法后,在多态情况下,将此时父类的方法称为虚拟方法,在运行时,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的,所以比较虚。

在编译时是父类类型,运行时成了对子类类型上的方法调用,这个过程也称为动态绑定

借用一张尚硅谷免费资源中的PPT:


多态

4. 设计模式实战:策略模式

策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。

使用了工厂方法的User(注意这个类不是策略模式必须的,或者说无关):

    private String name;
    private boolean vip;

    private User(String name, boolean vip) {
        this.name = name;
        this.vip = vip;
    }

    public static User vip(String name) {
        return new User(name, true);
    }

    public static User dios(String name) {
        return new User(name, false);
    }

    public String getName() { return name; }

    public boolean isVip() { return vip; }
}

接下来属于策略模式。

一个需要调用这组算法策略的使用者PriceCalculator

public class PriceCalculator {
    // 使用策略模式实现三个策略:
    // NoDiscountStrategy 不打折
    // Discount95Strategy 全场95折
    // OnlyVipDiscountStrategy 只有VIP打95折,其他人保持原价

    public static int calculatePrice(DiscountStrategy strategy, int price, User user) {
         return strategy.discount(price, user); // 实际执行的是子类重写后的方法
    }

    public static void main(String[] args) {
        User user = User.dios("wangpeng");
        System.out.println("用户名:" + user.getName());
        // 使用什么策略就传入什么策略
        int price = calculatePrice(new Discount95Strategy(), 10000, user);
        System.out.println("实际价格:" + price);
    }
}

策略的父类DiscountStrategy

public class DiscountStrategy {
    // 虽然正常情况下这个基类中的策略都会被某个具体策略给Override,但我认为目的有二:
    // 1. 被子类覆盖从而实现多态调用
    // 2. 用来兜底,抛出个异常
    public int discount(int price, User user) {
        throw new UnsupportedOperationException();
    }
}

没有折扣的NoDiscountStrategy

public class NoDiscountStrategy extends DiscountStrategy {
    @Override
    public int discount(int price, User user) {
        return price;
    }
}

打95折的Discount95Strategy

public class Discount95Strategy extends DiscountStrategy {
    @Override
    public int discount(int price, User user) {
        return (int)(price * 0.95);
    }
}

Vip独享的打折策略OnlyVipDiscountStrategy

public class OnlyVipDiscountStrategy extends DiscountStrategy {
    @Override
    public int discount(int price, User user) {
        if (user.isVip()) {
            return (int) (price * 0.95);
        } else {
            return price;
        }
    }
}

策略模式和模板方法模式有些类似,二者的界限在于:父类中是否包含“骨架”逻辑

策略模式的父类中几乎没有代码(大多数情况是个抽象方法),但是模版方法模式的父类中有一个“模板方法”,包含很多“骨架”逻辑代码。


参考文章:
Java中为什么静态方法不能被重写?为什么静态方法不能隐藏实例方法?

上一篇 下一篇

猜你喜欢

热点阅读