文章目录
面向对象 封装、继承、多态
一、封装
1.1 简介
-
为什么需要封装
举个例子: 我们使用的电脑,内部有CPU、硬盘、内存条、显卡等部件,部件通过某种连接方式一起工作(
电脑从而可以使用
)。
你的需求是使用电脑, 需要关心他内部是怎么调用交互的吗?不需要。
如果你要调整cpu
频率, 是不能直接操作硬件的, 而是给你通过了外部接口BIOS界面
, 通过BIOS
的设置调整,如果频率调高了, 电脑会自动黑屏断电。如果让你直接调整硬件, 频率不经过BIOS
控制, 高了不就把硬件给烧了吗。 -
封装的思想
可以再理解一下上面的电脑例子,电脑中的高内聚和低耦合。
高内聚: 类的内部数据操作细节自己完成,不允许外部干涉;
低耦合: 仅对外暴露少量的方法用于使用 -
仅对外暴露少量的方法用于使用
通俗的讲,封装就是把该隐藏的隐藏起来,该暴露的暴露出来。那么暴露的程度如何控制呢? 就是依赖访问修饰符。 安全性提高
1.2 访问修饰符
权限修饰符共有4种,分别为
public
,protected
、缺省
、private
;权限修饰符可以使得数据在一定范围内可见或者隐藏。
修饰符 | 本类 | 本包 | 其他包子类 | 任意位置 |
---|---|---|---|---|
private | √ | × | × | × |
缺省 | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
权限修饰符可以修饰:
-
外部类:public和缺省
-
成员变量、成员方法、构造器、成员内部类:public,protected,缺省,private
1.3 类的封装
-
类的封装的意义
隐藏类的实现细节
-
让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里面加入控制逻辑,限制对成员变量的不合理访问。还可以进行数据检查,从而有利于保证对象信息的完整性。
-
便于修改,提高代码的可维护性。主要说的是隐藏的部分,在内部修改了,如果其对外可以的访问方式不变的话,外部根本感觉不到它的修改。例如:
Java8
->Java9
,String
从char[]
转为byte[]
内部实现,而对外的方法不变,我们使用者根本感觉不到它内部的修改。
-
-
如何进行类的封装?
- 成员变量(
field
)私有化 - 提供标准的
get
/set
方法
public class Person { //使用 `private` 修饰成员变量 private String name; private int age; //提供 `getXxx`方法 / `setXxx` 方法,可以访问成员变量 public void setName(String n) { name = n; } public String getName() { return name; } public void setAge(int a) { age = a; } public int getAge() { return age; } }
注意: 通过类的封装之后,我们把
age
私有化了, 用户只能通过调用外部方法setAge()
设置值 和getAge()
获取值。如果判断不合法的数据,直接在方法中判断, 不给age
赋值负数就可以了 - 成员变量(
二、继承
2.1 简介
-
生活中的继承
父辈的东西可以被孩子继承,“子承父业”,“这孩子长得像妈妈”
-
Java中的继承
Java 中有父类、子类的概念,类似生活中的父子关系,父类中的一些属性和方法可以被子类继承下来使用,不再需要重复定义。
继承描述的是事物之间的所属关系,这种关系是:
is-a
的关系。例如,猫属于动物,狗也属于动物。可见,父类更通用,子类更具体。我们通过继承,可以使多种事物之间形成一种关系体系。
2.2 继承的好处
优点: 提高
代码的复用性, 扩展性, 提高开发效率
。
缺点:增加了类与类之间的耦合度
。
- 复用性
如果要写三个动物类, 每个里面都有动物的名称, 年龄, 每个动物都具有的特征。 使用继承可以复用公共的代码。 - 扩展性
每次要新增一个类都需要写相同的代码,不利于扩展。使用继承更加利于扩展了。 - 缺点:增加了耦合度
如果我为了复用, 植物也继承了动物类, 这样我植物就有名称和年龄了。看视代码进行了复用。但是如果动物类有改变, 动物可以移动,那么是不是所有的植物类都有问题了。(出现问题的主要原因就是动物和植物增加了耦合导致的)所以要保持 Is -a 的关系
2.3 父类的私有成员可以被继承吗?
子类实际会继承父类的所有成员变量和方法,只是因为是
private
不能访问。
如果子类要访问父类的私有成员, 可以通过父类提供公共的方法来间接访问。
三、多态
3.1 简介
编译时类型与运行时类型不一致,编译时看左边的“父类”,运行时看右边的“子类”。父类引用指向子类对象
- 提高程序的
扩展性
降低类与类之间的耦合度
- 扩展性
一个宠物商店要卖小猫, 写一个销售小猫的方法。 如果之后还要卖小狗等等, 是不是需要写好多个方法。 违背了开闭原则
(面向修改关闭, 面向扩展开放)。使用继承之后直接销售动物,其子类可以直接传递给父类,有再多的动物, 我动物商店卖动物就完了。 - 降低类与类之间的耦合度
还是上面的例子, 本身每次有新宠物, 都需要在宠物商店添加。实际上线项目之后,你的类还需要一直改。耦合度太高了,使用父类。有新宠物就有呗。只要你继承动物类,我就能调用你。
3.2 案例
- 购买动物
这样每添加一个新宠物, 直接在 market 里面判断返回对应的宠物就可以, 而不是 每添加一个新宠物就必须要新建一个
销售对应宠物的方法
public class petStore{ public Animal market(String type){ if("cat".equals(type)){ return new Cat(); }else if("dog".equals(type)){ return new Dog(); } return null; } }
- 喂养动物
如果要喂养不同的动物, 不使用多态。每有一个新动物。人就要有一个对应的喂养方法, 使用多态
新的动物
直接继承动物类
,重写对应的 eat()方法就可以了, 人类完全不需要改变。public class Person{ public void feed(Animal animal){ animal.eat(); } }
四、向上转型与向下转型
4.1 为什么要类型转换呢?
因为多态,就一定会有把子类对象赋值给父类变量的时候。
但是使用父类变量接收了子类对象之后,我们就 不能调用 子类拥有,而父类没有的方法了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做类型转换。
4.2 向上转型与向下转型
向上转型: 自动完成
向下转型:(子类类型)父类变量
4.3 instanceof 关键字
instanceof
关键字 用于判断一个对象的运行时类型
是为了避免 ClassCastException
的发生
对象 instanceof 数据类型
对象的运行时类型 <= instanceof 后面数据类型,才为true