良好的编码风格和完善统一的规约是最高效的方式。
前言
虽然在 GitHub 有着 17000+ 的 star 和大量的学习者,这本书即使涵盖的知识点对于很多入行较久 developer 来说并没有太大深入研究的价值,但是当时刚刚出炉的开发手册 PDF 精简版却一点一点的影响了我。也许是知乎上关注的技术话题下孤尽先生发布的回答吸引到了我,我开始注意到了平时编码中的那些事。
本篇汲取了本书中较为精华的知识要点和实践经验加上读者整理,作为本系列里的第一篇章第一节:面向对象之类篇。
本系列目录:
- 《码出高效》系列笔记(一):面向对象中的类
- 《码出高效》系列笔记(一):面向对象中的方法
- 《码出高效》系列笔记(一):面向对象中的其他知识点
- 《码出高效》系列笔记(二):代码风格
- 《码出高效》系列笔记(三):异常与日志
- 《码出高效》系列笔记(四):数据结构与集合的框架
- 《码出高效》系列笔记(四):数据结构与集合的数组和泛型
- 《码出高效》系列笔记(四):元素的比较
集成 IDE
《码出高效》不但有实体书,同时在 IDE 中有对应的插件,可以帮助你在实际开发中扫描代码编写存在的问题和可能会引发的隐患。
Intellij IDEA 在 Plugins 中直接搜索 alibaba,如图所示的第一个点击 install 即可,第二个名为 cloud Toolkit 是阿里中间件团队出品的 Apache dubbo 快速开发部署的插件,可以快速创建 Apache Dubbo 工程,阿里中间件团队博客。
在需要扫描的地方邮件,最后有个编码规约扫描,当然也可以将整个工程扫描。
扫描的结果,可以根据 tips 进行代码的整改,当然这个插件也没有那么智能,有些特定场景不需要这些提示,可以将自行定义。
面向对象
一切万物皆对象
面向过程让计算机有步骤地按照顺次做一件事情,是一种过程化的叙事思维。但是在大型软件开发中,流程互相穿插,模块互相耦合,往往牵一发动全身。面向对象就是计算机世界里解决复杂软件工程的方法论,拆解问题复杂度。
类
无类鬼
面向过程的结构相对松散,强调如何流程化地解决问题;面向对象的思维往往更加内聚,强调低耦合,高内聚,先抽象模型,定义共性行为,再解决实际问题。
传统意义上,面向对象有三大特性:封装
、继承
、多态
。在《码出高效》这本书中将 抽象
也作为面向对象的特性之一,进而成为四大特性。
OOP(Object Oriented Programming)
- 第一特性
封装
:使模块之间的耦合度变低,增强复用性,更具有维护性。例如多个业务类中socket
对象需要对请求消息体的对象转化成字节数组并且计算消息体长度,在多个业务类中分别去实现是不合适的,不但低效而且一旦需求变更将会更改大量的代码,抽象出共性的行为特征封装成专门维护请求消息对象的类才是正确的选择。 - 第二特性
继承
:在代码中广泛存在,子类继承父类,获得父类的部分属性和行为,也是增强复用性的体现。例如我们最常见的集合中的List
,Set
接口就是继承于Collection
接口,而Collection
则继承于Iterable
接口。假如自定义的一个exception
类,我们可以将它继承RuntimeException
,通过super
方法去个性化
构建一个我们想要 catch 到的异常格式。 - 第三特性
多态
:多态使得模块在复用性的基础上更加具有扩展性,让运行期更具有想象空间。可以进行覆写Override
和重载Overload
就是很好地体现了多态的意义。前面篇章中的工厂模式也是很好体验了多态的作用。 - 第四特性
抽象
:抽象其实是完全囊括了以上三种特性,应该是所有程序员的核心素养之一。体现出程序员对业务的建模能力,以及对架构的宏观掌控力,从抽象到具体,逐步形象化的过程。比如 Object 类,是任何类的默认父类,是对万事万物的抽象,高度概括事物的自然行为和社会行为。
我们在写代码中会常见到这些:getClass()
是用来说明本质上是谁;toString()
是当前名片;finalize()
是再度向销毁时触发的方法;clone()
是繁殖对象的一种方式;hashCode
和equals()
是判断你其他元素是否相等的身份证。
抽象类与接口
语法维度 | 抽象类 | 接口 |
---|---|---|
定义关键字 | abstract | interface |
子类继承或实现的关键字 | extends | extends |
方法访问控制符 | 无限制 | 有限制,默认是 public abstract 类型 |
属性访问控制符 | 无限制 | 有限制,默认是 public abstract 类型 |
静态方法 | 可以有 | 不能有 |
static{静态代码块} | 可以有 | 不能有 |
本类型之间扩展 | 单继承 | 多继承 |
本类型之间扩展的关键字 | extends | extends |
总结:抽象类在被 继承
时体现的是 is-a
关系,而接口被实现时是 can-do
关系。抽象类可以包含抽象方法、属性变量、实体方法。抽象方法与接口一样,继承他的类必须要覆写实现。抽象类往往是用作同类食物的抽象,比如各类排行榜(常见的土豪排行榜、天台排行榜、天梯排行榜)他们中有着相似的特征。飞机会飞,鸟类也会飞,而 fly()
不应该被定义在抽象类中,因为飞机和鸟类除了都会飞以外很难再找到其他的共同特征。
抽象类是模板式设计,类似一个模具,而接口是契约式设计,更像一个合同。
内部类
在一个.java 源文件中,只能定义一个类名与文件名完全一致的公开类,使用 public class
关键字来修饰,我们可以在这个类的内部和外部分别去定义另外的类,前者就叫内部类,后者叫外部类,内部类就成了这个类本身的一个属性,与其他属性的定义方式一致,可以使用 public
,private
,protected
访问权限关键字。可以定义成 static
静态内部类,当然类型也可以定义成 class
,interface
和 enum
。
访问权限控制
我们在讲到封装的时候往往不能忽略掉使用关键字来限制类外部对类内属性和方法的随意访问,那么在 Java 中访问权限主要分为以下四个等级
访问权限控制符 | 任何地方 | 包外子类 | 包内 | 类内 |
---|---|---|---|---|
public | OK | OK | OK | OK |
protected | NO | OK | OK | OK |
无 | NO | NO | OK | OK |
private | NO | NO | NO | OK |
- public:可以修饰
外部类
、属性
、方法
,其他不详写了。 - protected:只能修饰
属性
和方法
,只能够被包内的类访问,当然还有一种情况就是只要是他的子类都可以访问。 - 无:这个比较陌生,Intellij IDEA 有时会提示是否把一些只在包内访问的方法修改成无控制符。他并非是 default,书中也明确说到定义外部类也极少用到无控制符的方式,一般要么定义 public class,包外实例化;要么定义内部类,功能内聚。
- private:修饰
属性
,方法
,内部类
,被其修饰过的属性或方法只能在该类访问,子类 no way,包内外部类 no way,包外 without thinking。
在代码重构时,private 方法过旧,可以直接删除,且无后顾之忧。但是删除一个 public 的方法就要谨慎小心地检查是否被调用。变量就像自己的小孩(我还没小孩),要尽量控制在自己的视线范围内,作用域太大,往往容易出现问题。因此,在定义类时,推荐访问控制级别从严处理:
- 如果不允许通过外部通过 new 创建对象,构造方法必须是 private。
- 工具类不允许有 public 或者 default 构造方法。
- 类非 static 成员变量并且与子类共享,必须是 protected。
- 类非 static 成员变量并且仅在本类使用,必须是 private。
- 类 static 成员变量如果仅在本类使用,必须是 private。
- 类 static 成员变量,必须考虑是否为 final。
- 类成员方法只供类内部调用,必须是 private。
- 类成员方法只对继承类公开,那么限制为 protected。
this 与 super
对象实例化时,至少有一条从本类到 Object 的通路,打通这一条路的工兵就是 this
和 super
,但是 this
和 super
往往是默默无闻的,在很多情况不需要显式的调用,比如:
- 本类方法调用本类属性
- 本类方法调用另一个本类方法
- 子类构造器隐含调用
super()
子类基层的父类,而父类坚持不提供默认的无参构造方法,必须在本类的无参构造方法中使用 super
方法调用父类的有参构造方法,比如以下情形:
class Father {
public Father(int arg) {}
}
class Son extends Father {
public Son() {
super(123);
}
}
关键字 | 基本概念 | 查找范围 | 特异功能 |
---|---|---|---|
this | 访问本类属性和方法 | 先找本类,没有则找父类 | 单独使用时,表示当前对象 |
super | 由子类访问父类中的实例属性和方法 | 直接查找父类 | 在子类覆写父类方法时,访问父类同名方法 |
共同点 1 | 都是关键字,起指代作用 | 共同点 2 | 在构造方法中必须出现在第一行 |
类关系
5 种类型:
- 继承:extends(is-a 关系)
- 实现:implements(can-do 关系)
- 组合:类是成员变量(contains-a 关系)
- 聚合:类似成员变量(has-a 关系)
- 依赖:import 类(use-a 关系)
类关系 | 英文名 | description | 权力强侧 | 示例说明 |
---|---|---|---|---|
继承 | Generalization | 父类与子类之间的关系:is-a | 父类方 | 舔狗继承于动物,完全符合里式代换 |
实现 | Realization | 接口与实现类之间的关系:can-do | 接口方 | 舔狗实现了舔的接口行为 |
组合 | Composition | 比聚合更强的关系:contains-a | 整体方 | 头只能是身体强组合的一部分,两者完全不可分,具有相同的生命周期 |
聚合 | Aggregation | 暂时组装的关系:has-a | 组装方 | 小狗与项圈之间只是暂时的聚合的关系,项圈完全可以复用在另一条舔狗身上 |
依赖 | Dependency | 一个类用到另一个类:use-a | 被依赖方 | 女神玩弄舔狗,舔狗作为参数传入,是一种依赖关系(这里是女神依赖舔狗哦,舔狗大翻身!) |
序列化
内存中的数据对象只有转换为二进制流才可以进行数据持久化和网络传输。将数据对象转换为二进制流的过程称为对象的序列化。反之,将二进制流恢复为数据对象的过程称为反序列化。序列化常见的场景是 RPC 框架的数据传输。
常见的序列化方式有三种:
- Java 原生序列化
- Hessian 序列化
- JSON 序列化
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于