这期我们来学习java面向对象中的多态,我将用最简单明了的方式讲清楚,希望大家看完能有所收获后,能够自信的面对未来的机遇和挑战。其实多态在我们生活中就有非常多的例子,比如我们家里的空调,一台空调可以有多种功能可以制冷也可以制热,那么我们可以思考一下,一台空调能实现两种功能,还有其他一种东西可以实现多种功能的例子吗?前几天我买了一款榨汁机,这款榨汁机可以榨果汁,居然还可以做冰淇淋,我感到非常nice,终于实现了冰激凌自由。
空调:一机两用
榨汁机:一机两用
举完两个例子,大家是不是对多态有个大概的了解了呢?多态的意义就是为了复用,为了简化,为了方便,大家一定要多思考,任何一项技术和知识的出现都是为了能够高效、简便的实现某种目的,大家以后可能是研发人员,那么作为研发人员最终目的是什么?就是解决技术难题和客户需求问题,往大了说就是推动技术不断迭代,进而推动世界的高效发展。
拉回正题,要实现多态,就要有前提条件,那么前提条件就是子类继承父类、子类重写父类方法、父类对象能够引用子类对象的属性和方法,有了这三条,我们就可以实现多态,让程序能变得简单、高效、便捷。
满足上面三个条件:我们用空调的例子来举例一下吧,假设空调(AirConditioner)是父类,那么空调里有两个子类分别是制冷机(Chiller)和制热机(Heater):
下边我们创建三个类,来满足刚刚实现多态的三个条件:
这是空调类父类:
public class AirConditioner {
public void Command() {
System.out.println("控制空调模式开启");
}
}
这是制冷机类子类:
public class Chiller extends AirConditioner {
@Override
public void Command() {
System.out.println("制冷机开始工作,制冷模式开启!");
}
}
这是制热机子类:
public class Heater extends AirConditioner {
@Override
public void Command() {
System.out.println("制热机开始工作,制热模式开启!");
}
}
我们来运行一下代码,看看效果如何:
public class StartDemo {
public static void main(String[] args) {
AirConditioner a = new Chiller();
AirConditioner b = new Heater();
a.Command();
b.Command();
}
}
从上面运行的结果,我们可以看出执行的结果是程序运行时实际对象中的方法,前边我们提到父类引用指向子类对象,那么运行代码的时候就是运行子类重写父类后的方法,由此可以看出不论要扩展多少子类,只要父类指向了子类对象而且满足继承和重写规则,都可以运行子类方法,在多态中父类担任的角色就是统筹全局的任务,只要外部需要局部需求,这个全局统筹官就会合理调度局部任务去满足外部的各项需求。
再简单点理解,我们可以把父类理解成大脑,子类理解成人体各个器官,大脑作为最高首脑,控制着各个器官正常工作,来维持整个生命系统的平衡。
我们再来举个代码例子,深入理解一下多态怎么运用的:
假如一个水果店要统计香蕉和苹果在某天的利润,我们就可以用多态实现。
这是利润类父类:
public class Profit {
protected double weight;//总重
protected double price;//成本单价
protected double amount;//售出单价
public Profit(double weight,double price,double amount) {
this.weight = weight;
this.price = price;
this.amount = amount;
}
public double getProfit() {
return weight*(amount - price); //利润
}
}
这是苹果利润类子类:
public class AppleProfit extends Profit{
public AppleProfit(double weight,double price,double amount) {
super(weight,price,amount);
}
@Override
public double getProfit() {
//不易损员工提成占总利润的0.1
return (amount - price) * weight - (amount - price) * weight*0.1;
}
}
这是香蕉利润类子类:
public class BananaProfit extends Profit{
public BananaProfit(double weight,double price,double amount) {
super(weight,price,amount);
}
@Override
public double getProfit() {
//易损品员工提成总利润的0.3
return (amount - price) * weight - (amount - price) * weight*0.3;
}
}
运行下边计算苹果香蕉的总利润:
public class StartDemo {
public static void main(String[] args) {
Profit[] profits = new Profit[] {
new AppleProfit(200,2.5,7.5),
new BananaProfit(150,0.9,4.99)
};
//输出苹果和香蕉的总利润
System.out.println("水果总利润:"+sumProfit(profits));
}
//计算出苹果和香蕉的总利润
public static double sumProfit(Profit... profits) {
double totalProfit = 0;
for (Profit profit: profits) {
totalProfit = totalProfit + profit.getProfit();
}
return totalProfit;
}
}
我们看看运行效果:
从上边运行的结果可以看出,苹果和香蕉的总利润已经计算出来了,利润类(Profit)当了父类,苹果利润类(AppleProfit)当了子类,香蕉利润类(BananaProfit)当了子类,苹果利润类(AppleProfit)和香蕉利润类(BananaProfit)继承了利润类(Profit),并重写了利润类(Profit)的 getProfit() 方法,利润类(Profit)的getProfit() 方法是总价直接减去总成本价获取利润,而苹果利润类(AppleProfit)和香蕉利润类(BananaProfit)重写的getProfit() 方法是总价减去成本价与员工提成的和获得利润,并且苹果和香蕉的利润计算方法里的公式也不相同,即:苹果利润类(AppleProfit)和香蕉利润类(BananaProfit)重写的getProfit()也各不相同,啰嗦了这么多,就是为了讲清楚,讲明白,下面看图更清晰。
那么大家是不是能明显的看出他们的关系,父类引用指向子类对象,并通过子类各自重写的方法实现各自计算功能,最后实现总的计算功能。
上边这张图,我们分析一下,两个父类引用分别指向了两个子类,相当于下图的代码,但不建议下图这样写,不好扩展,只是为了让大家加深理解:
那么,我们要深度思考一下了,要实现水果店里香蕉、苹果、橙子、葡萄、草莓的利润总和,我们应该怎么实现呢?首先我们要新增3个类分别是橙子、葡萄、草莓利润类,然后在实现方法中新增3个父类引用指向3个子类对象,就可以对这5个进行利润总和的计算了,实现代码如下。
public class StartDemo {
public static void main(String[] args) {
Profit[] profits = new Profit[] {
new AppleProfit(200,2.5,7.5),//苹果利润类
new BananaProfit(150,0.9,4.99),//香蕉利润类
new OrangeProfit(130,2.3,3.89),//橙子利润类
new GrapeProfit(50,5,8.6),//葡萄利润类
new StrawberryProfit(30,7,12)//草莓利润类
};
//输出苹果和香蕉的总利润
System.out.println("水果总利润:"+sumProfit(profits));
}
//计算出苹果和香蕉的总利润
public static double sumProfit(Profit... profits) {
double totalProfit = 0;
for (Profit profit: profits) {
totalProfit = totalProfit + profit.getProfit();
}
return totalProfit;
}
}
上边代码实现了水果店里5种水果总利润的计算,如果店里水果种类越来越多,各自计算利润的方式又不一样,要想实现所有种类水果的总利润计算是不是就非常nice了,那么我们可以创建所有水果利润类,并重写利润类的方法,然后我们在上边计算的代码中只需加要计算种类的利润类即可,可以实现自由的计算不同水果的总利润,这就是多态,读到这了大家可能对多态了解应该更深一点了。
自由实现不同种类水果总利润的代码,我放下边,大家可以看看,分别是1、3、4种不同水果利润总和的计算代码:
public class StartDemo1 {
public static void main(String[] args) {
Profit[] profits = new Profit[] {
new AppleProfit(200,2.5,7.5),//苹果利润类
};
//输出苹果和香蕉的总利润
System.out.println("水果总利润:"+sumProfit(profits));
}
//计算出苹果和香蕉的总利润
public static double sumProfit(Profit... profits) {
double totalProfit = 0;
for (Profit profit: profits) {
totalProfit = totalProfit + profit.getProfit();
}
return totalProfit;
}
}
public class StartDemo2 {
public static void main(String[] args) {
Profit[] profits = new Profit[] {
new BananaProfit(150,0.9,4.99),//香蕉利润类
new OrangeProfit(130,2.3,3.89),//橙子利润类
new StrawberryProfit(30,7,12)//草莓利润类
};
//输出苹果和香蕉的总利润
System.out.println("水果总利润:"+sumProfit(profits));
}
//计算出苹果和香蕉的总利润
public static double sumProfit(Profit... profits) {
double totalProfit = 0;
for (Profit profit: profits) {
totalProfit = totalProfit + profit.getProfit();
}
return totalProfit;
}
}
public class StartDemo3 {
public static void main(String[] args) {
Profit[] profits = new Profit[] {
new AppleProfit(200,2.5,7.5),//苹果利润类
new OrangeProfit(130,2.3,3.89),//橙子利润类
new GrapeProfit(50,5,8.6),//葡萄利润类
new StrawberryProfit(30,7,12)//草莓利润类
};
//输出苹果和香蕉的总利润
System.out.println("水果总利润:"+sumProfit(profits));
}
//计算出苹果和香蕉的总利润
public static double sumProfit(Profit... profits) {
double totalProfit = 0;
for (Profit profit: profits) {
totalProfit = totalProfit + profit.getProfit();
}
return totalProfit;
}
}
上期我们讲继承时,说到过祖先类Object,Object类中有几个常用的方法也可以在咱们自己创建的许多子类中以这种多态的形式实现,比如:toString()、equals(Object obj)、hashCode()、clone(),注意:equals(Object obj)和hashCode()必须一起重写,规定两个对象equals为true,那么hashCode必须相等。
public class Profit implements Cloneable{
protected double weight;//总重
protected double price;//成本单价
protected double amount;//售出单价
protected String profitTitle;//利润类别
public Profit(double weight,double price,double amount,String profitTitle) {
this.weight = weight;
this.price = price;
this.amount = amount;
this.profitTitle = profitTitle;
}
public double getProfit() {
return weight*(amount - price); //利润
}
@Override
public String toString() {
return "总利润:" + weight*(amount - price);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()){
return false;
}
Profit p = (Profit) o;
return Objects.equals(this.profitTitle,p.profitTitle)
&& this.price == p.price
&& this.weight == p.weight
&& this.amount == p.amount;
}
@Override
public int hashCode() {
// 使用相同的字段计算哈希码
return Objects.hash(weight, price, amount,profitTitle);
}
@Override
public Profit clone() {
try {
return (Profit) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
运行一下测试代码:
public class StartDemo {
public static void main(String[] args) {
Profit p = new Profit(100,23,78,"父类");
Profit p1 = new Profit(100,23,78,"父类");
Profit p2 = new Profit(103,13,78,"父类1");
// 测试toString()
System.out.println("p测试toString():"+p);
System.out.println("p1测试toString():"+p1);
System.out.println("p2测试toString():"+p2);
System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
// 测试equals()
System.out.println("p和p1测试equals(): " + p.equals(p1));
System.out.println("p和P2测试equals(): " + p.equals(p2));
System.out.println("P1和P2测试equals(): " + p1.equals(p2));
System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
// 测试hashCode()
System.out.println("p hashCode: " + p.hashCode());
System.out.println("p1 hashCode: " + p1.hashCode());
System.out.println("p2 hashCode: " + p2.hashCode());
System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
// 测试clone()
Profit p1Clone = p1.clone();
System.out.println("p1 clone: " + p1Clone);
System.out.println("p1 equals clone: " + p1.equals(p1Clone));
System.out.println("p1 == clone: " + (p1 == p1Clone));
System.out.println("p1 hashCode: " + p1.hashCode());
System.out.println("p1Clone hashCode(): " + p1Clone.hashCode());
System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
}
}
运行上边代码结果如下:
讲到这,大家已经知道多态怎么用了,多态中其实最重要的就是实现子类对父类方法的重写,导致能够个性化扩展父类方法,实现程序的扩展性、简化性、灵活性、通用性。
最后,谢谢大家能够耐心的看到最后,希望大家看完有所收获,如果大家有所收获,不忘赞一赞或关注下,也是给我继续分享干货的动力。