前言
首先看到这个标题,大家可能觉得很无聊,这三个词可以说基本上随便一搜,就一大堆文章解释的头头是道,但是即便如此,我为啥还是要写这篇文章呢,是因为今天在给大家准备后续内容时,无意看到了这三个词,然后顺带想了一下各自的含义,发现封装和继承还比较好理解,结果另外一个多态琢磨了半天,竟然一时忘了这个词的含义,突然有点愕然,自己才工作了一年,居然很多基础概念都快记不清了,oh,太可怕了!于是才有了这篇文章,一方面提醒自己,一方面也分享出来给大家,让大家也不能忘记哦。
正文
1、封装
封装在这三个词里面是最容易理解的,专业术语我就不多说了,因为说了你们也记不住,我也记不住,在我看来,最容易理解的方法就是举例,OK,那我们就举个例子,比如现在我希望有一个类,这个类有一些属性和功能,并且可以完成一些,比如
- 有眼睛,属性
- 有嘴巴,属性
- 可以吃饭,功能
- 可以睡觉,功能
- 心上人,属性
- ······
属性就相当于是这个类拥有的成员变量,这个变量的值就对应具体的属性,比如眼睛,有单眼皮、双眼皮,这个很好理解。功能就相当于是这个类拥有的方法,比如吃饭、睡觉,都是在执行某个动作,也就是类的某个方法的执行。然后还有一些访问权限控制,比如心上人这个属性,这个类可能不希望其它对象知道,所以这个属性就是私有的,也就相当于java里的private访问权限。
通过上面说的一些要素,成员变量、方法、访问控制等,我们就可以拼凑出一个简单的类来了,不用我说,大家应该也知道了这个类就是:可爱的你。而定义这些要素以及拼凑的过程就是封装。
现在我们明白了封装的意思,但是这样做可以带来哪些好处呢,为啥无缘无故要封装这个封装那个的,这是因为封装后,这个类对象,外界只会知道它暴露出来的属性或者方法,此外,对于方法,外界只知道该类对象有这个方法,比如可以吃饭,但是并不知道该方法的细节,比如吃的什么、在哪里吃的等这些细节。那隐藏这些细节或者属性有啥用呢,藏着掖着的有啥好处吗,当然是有好处的,比如吃饭这个方法,里面需要用到筷子这个属性,但是假设由于我没有隐藏筷子这个属性,导致该属性被外界修改了,现在只有一只筷子了,所以这饭也没法吃了,最终吃饭失败。
2、继承
这个词也比较简单,很好理解,同样的方法,我们还是通过举例来简单说明一下,比如现在想吃点水果,但是不知道吃什么,平常吃的比较多的可能就是苹果、香蕉、橘子、荔枝等,一番纠结后选择了口感最好的荔枝。OK,在这个场景下,就可以抽取一个典型的继承模型出来,对于水果,可以理解为它有一个属性:可以被吃。对于苹果、香蕉、橘子、荔枝,它们也和水果一样,有一个同样的属性:可以被吃,但是它们除了这个属性之外,还拥有一些不同的属性,比如苹果是青色,香蕉是黄色、橘子是橙色、荔枝是深红色等,这些属性是水果这个对象所没有的,所以我们可以将特有的属性定义在苹果、香蕉、橘子、荔枝中,对于公共的属性:可以被吃,我们选择去继承水果这个对象,来让苹果、香蕉、橘子、荔枝通过继承的方式获取属性:可以被吃。这就是继承。
那说了半天,好像确实明白了继承的意思,那这样做的意义何在呢,意义就在于对于一些有公共属性的对象,无需重复在各自内部定义属性,只需要定义一个父类对象,然后在父类对象里定义这些公共属性,然后其它的类就只需要负责各自特有的属性定义,公共的属性通过继承的方式一次搞定。
3、多态
这个词相对前两个,看上去就感觉高级一点,也没那么容易一下子就理解,但是如果扩展为:多种形态,这样的描述方式是不是就容易理解一点,那再扩展一下描述:一个类可能存在多种形态,是不是又清晰了一点,好像就是在说父子类的关系,所以这里不难想到上面说的继承,但是它既然作为一个单独的词,肯定有另外的意义所在,要理解这个词的含义,我还是一样的方法,举个例子,但是不是中文描述,虽然中文描述可以说清,但是不好理解,所以我就直接举一行代码:
Person person = new ZhangSan();
Person
是一个父类,里面有一个方法eat()
代表吃米饭,ZhangSan
是Person
的一个子类,它重写了父类的方法eat()
代表吃披萨,并且还有一个单独的方法swim()
代表游泳。它们的定义如下
class Person{
public void eat(){
System.out.println("吃米饭");
}
}
class ZhangSan extend Person{
@Override
public void eat(){
System.out.println("吃披萨");
}
public void swim(){
System.out.println("我会游泳");
}
}
这种情况下,我们如果调用例子中的person.eat()
,会出现什么样的情况呢,通过运行,我们发现了最终结果是:吃披萨,而这个结果就是多态的一种体现,因为我们在声明的时候,明明只是声明了一个父类对象,结果却调用到了子类的方法,原因就是因为我们是用子类来初始化的,如果子类重写了父类的方法,那用子类初始化的父类对象就会调用子类的实现方法,emmmmm,这句话可能有点绕,总之不理解的时候,想想上面的例子就明白了。
在上面的例子中,如果再来一个对象LiSi
或者WangWu
,也分别继承Person
,并重写eat()
方法,以及添加各自的特性方法,那么Person person = new ZhangSan();
、Person person = new Lisi();
、Person person = new WangWu();
,以及类似这样的代码,就可以理解为Person
这个类能以ZhangSan
Lisi
WangWu
这三种形式存在,也就是我们上面说的,一个类存在多种形态,也就是多态,怎么样,明白了吗。
OK,现在我们明白了多态是什么,那这样设计的意义何在呢,好像只是给人感觉很绕,但是没有什么用武之地,其实之所以这样想,还是因为对平常的代码没有仔细观察和思考,当然你可能也已经正在用这个特性来开发代码,只不过你不知道这个特性叫多态而已,在实际开发中,接口和抽象类其实就是多态最为直接的一种使用体现,举个很简单的例子,比如:张三现在需要回家,一般是滴滴打车回家。在这样一个简单的场景中,我们定义一个类ZhangSan
,添加一个方法goHome
去执行这个回家的操作,回家过程中需要选择一种交通方式才能回家,我们默认是滴滴打车回家,所以最终定义如下:
class ZhangSan{
TrafficWay trafficWay = new DiDi();
void goHome(){
trafficWay.go();
}
void setTrafficWay(TrafficWay way){
trafficWay = way;
}
}
interface TrafficWay{
void go();
}
class DiDi implements TrafficWay{
void go{
System.out.println("滴滴打车回家");
}
}
但是有一天加班太晚打不到车了,没办法ZhangSan
这次只能坐地铁回家,这时我们只需新增一个TrafficWay
接口的实现类,再给ZhangSan
设置进去即可,
class SubWay implements TrafficWay{
void go{
System.out.println("坐地铁回家");
}
}
ZhangSan zs = new ZhangSan();
zs.setTrafficWay(new SubWay());
zs.goHome();
同样的,假设地铁也停运了,那ZhangSan
只能走路回家了,我们同样的也只需要新增一个TrafficWay
接口的实现类Walk
,而无需修改以前的代码,就达到了效果。在该例子中,相信也不用我多说了吧,TrafficWay
这个接口就是一个多态的体现,能以DiDi
,SubWay
,Walk
等各种形态存在,用来满足不同情形的需求。
这就是多态的实际应用场景。怎么样,明白了吗,是不是发现原来我们平常看到的这些设计方式,原来就是多态的一种思想,这种思想一般用在框架设计上面,哪一天我们需要设计一个框架,就可以用这个思想啦。
结语
最后,我想感叹下,虽然这三个词在我们最初学java的时候,就会去了解学习,但是我们从java使用到现在,真真正正理解并且将这些特性思想融入到实际开发中的,又有多少呢,有时候我们只是沉浸于各种业务开发,而忽略了一些优秀的设计思路或方法,好了,今天就到这吧,本篇应该只算是一个java基础知识的复习,后续内容尽请期待哦,下次见!!!
=========================================
另外,本文章也会同步发布在本人的公众号上面哦,欢迎关注呀,公众号里会优先推送最新的文章,还有各种送书等福利,以及各种小惊喜,怎么样,快来关注呀!