[笔记]码出高效:Java 开发手册

# Java  /  笔记

1.计算机基础

1.1 TCP/IP 网络协议(从上自下)

程序在发送消息时,

  • 应用层(HTTP/FTP/SMTP/TELNET..):按照既定协议打包
  • 传输层(TCP/UDP):加上双方的端口号
  • 网络层(IP/ARP):加上双方的 IP 地址
  • 链路层(IEEE 802.x/PPP..):加上双方的 MAC 地址,并将数据拆分成数据帧,经过多个路由器和网关后,到达目标机器。

简而言之,就是按照**"端口 -> IP 地址 -> 地址"**这样的路径进行数据的封装和发送,解包的时候反过来操作即可。

1.2 信息安全 CIA 原则

互联网企业都要建立一套完整的信息安全体系,遵循 CIA 原则,即保密性(Confidentiality)、完整性(Integrity)、可用性(Availability)。

2.面向对象

2.1 类

在定义 Java 类时,推荐首先定义变量,然后定义方法。公有方法是使用者和维护者最关心的,最好首屏展示;保护方法重要性仅次于共有方法;私有方法相当于黑盒实现,一般不要被特别关注;最后是 getter/setter 方法,虽然是公有的,但承载的信息价值较低,一般不包含业务逻辑,所以都放到类最后。

抽象类在被继承时体现的是:is-a 关系(需要符里氏代换原则),模板式设计;
接口在被实现时体现的是:can-do 关系(需要符合接口隔离原则),契约式设计。

2.1.1 访问权限控制

定义类时,访问控制级别要从严处理(推荐最小化),只把有限的方法和成员公开给别人(迪米特法则——最少知识原则的内在要求)。

  • 1.如果不允许外部直接通过 new 创建对象,构造方法必须是private
  • 2.工具类不允许有publicdefault构造方法。
  • 3.类非static成员变量并且与子类共享,必须是protected
  • 4.类非static成员变量并且仅在本类使用,必须是private
  • 5.类static成员变量如果仅在本类使用,必须是private
  • 6.若是static成员变量,必须考虑是否为final
  • 7.类成员方法只供类内部调用,必须是private
  • 8.类成员方法只对继承类公开,那么限制为protected

2.1.2 类关系

类关系包含如下 5 种类型:

  • [ 继承 ] extends (is-a)
  • [ 实现 ] implements (can-do)
  • [ 组合 ] 类是成员变量 (contains-a)
  • [ 聚合 ] 类是成员变量 (has-a)
  • [ 依赖 ] import 类 (use-a)

注意组合和聚合的区别,组合:是一种完全绑定的关系,所有成员共同完成一件使命,他们的生命周期是一样的,体现的是非常强的整体与部分的关系,==同生共死==,部分不能在整体之间共享;
聚合:是一种可拆分的整体与部分的关系,是非常松散的==暂时组合==,部分可以被拆出来给另一个整体。

在 UML 类图中,空心三角形△表示继承,实心菱形◆表示组合,空心菱形◇表示聚合,这三者都是实线连接。
用空心三角形△来表示实现(接口),用一个箭头↑表示依赖,虚线连接。
有一个规律:有形状的图形符号一律放在权利强的一侧。

==序列化==并不保存静态变量。

2.2 重写

方法覆写的口诀:

  • 一大: 访问权限控制符只能更大(或相同)
  • 二小: 返回值和抛出异常只能更小(或相同)
  • 二相同:方法名和参数列表须相同(方法签名)

重写是在运行期间由JVM进行绑定,调用合适覆写方法来执行。
重载是编译器确定方法调用,属于静态绑定。

2.3 泛型

  • 1.尖括号<>里的每个元素都指代一种未知类型。

即使 String 出现在尖括号里,它也不是 java.lang.String了 ,而仅仅是一个代号。
类名后方定义的泛型<T>和方法返回值前方定义的<T>是两个指代,可以完全不同,互不影响。

  • 2.尖括号的为位置非常讲究,必须在类名之后或者方法返回值之前。
  • 3.泛型在定义处只具备执行 Object 方法的能力。

在定义了泛型的方法内部,泛型只能调用 Object 类中的方法,如 toString()。

  • 4.对于编译之后的字节码指令,其实没有这些花头花脑的方法签名,充分说明了泛型只是一种编写代码时的语法检查。

在使用泛型元素肘,会执行强制类型转换。(存在类型擦除)

使用泛型的好处:

  • 类型安全:放置的是什么,取出来自然是什么,不用担心会抛出 ClassCaseException 异常。
  • 提升可读性:从编码阶段就显式的知道泛型集合、泛型方法等处理的对象类型是什么。
  • 代码重用:泛型合并了同类型的处理代码,使代码重用度变高。

2.4 数据类型

基本数据类型

对象的存储空间分配单位是8个字节(必须是 8B 的倍数)。

包装类型和基本数据类型的选择:

    1. 全部的POJO类属性必须使用包装数据类型。
    1. RPC 方法的返回值和参数必须使用包装数据类型。
    1. 所有的局部变量推荐使用基本数据类型。

3.代码风格

3.1 命名规约

命名要符合语言特性、体现元素特征。命名做到望文知义、自解释是每个开发工程师的基本素质之一。

推荐命名时能体现元素特征:

  • 包名统使用小写,点分隔符之间有且仅有1个自然语义的英语单词。
  • 包名统一使用单数形式,但是类名如果有复数含义,则可以使用复数形式。
  • 抽象类命名使用AbstractBase开头;异常类命名使用 Exception结尾,测试类命名以它要测试的类名开始,以Test结尾。
  • 类型与中括号紧挨相连来定义数组。
  • 枚举类名带上Enum后缀,枚举成员名称需要全大写,单词间用下画线隔开。

变量起名可参考:CODELF

3.1.1 常量

  • 状态(没有扩展信息的)可以用 不能实例化的抽象类的全局常量来表示。

  • 类型(有扩展信息的)可以用 枚举类型来表示全局常量,并描述扩展信息。

  • 魔法值“害人害己”,无论如何都不允许任何魔法值直接出现在代码中(常在河边走哪有不湿鞋)。

  • 某些公认的字面常量是不需要预先定义的,但如果具备了特殊含义,就必须定义出有意义的常量名称。

    必须:for() 里的 0 可以直接使用,true 和 false 可以直接使用。

  • 类内常量必须全部大写,单词间用下划线隔开,力求语义表达完整清楚(不要嫌长);方法内常量使用小驼峰即可。

3.1.1 变量

一般情况下,变量的命名需要满足小驼峰格式,命名体现业务含义即可。

存在一种特殊情况,在定义类成员变量时,特别是在 POJO 类中,针对布尔类型的变量,命名不要加is前缀,否则部分框架解析会引起序列化错误,但是数据库建表中,推荐表达是与否的字段采用is_xxx的命名方式(需要配置POJO映射)。

3.2 代码展示风格

3.2.1 缩进、空格和换行

  • 推荐使用4个空格代表缩进,禁止使用 Tab(不同编辑器对Tab解析不一致)
  • 空格用于分隔不同的编程元素,可以让运算符、数值、注释、参数等各种编程元素错落有致,方便快速定位。
  • 空行用来分隔功能相似、逻辑内聚、意思相近的代码片段,是得程序布局更加清晰。浏览时也能起到自然停顿的作用,提升阅读体验。

    例如在:方法定义之后、属性定义和方法定义之间、不同逻辑、不同语义、不同业务的代码之间都需要通过空行来分隔。

3.2.2 换行和高度

  • 代码中需要限定每行的字符个数,以便适配显示器的宽度,以及方便CodeReview时进行diff对比,要求单行字符数不超过 120 个,超出则需换行。
  • 应该将次要逻辑抽取为独立方法,将共性逻辑抽取为共性方法(比如参数校验、权限判断等),便于复用和维护,使主干代码逻辑更加清晰。
  • 约定单个方法总行数不超过 80 行(除注释之外,方法签名、左右大括号、方法内代码、空行、回车及任何不可见字符的总行数不超过 80 行)。

    心理学认为人对事物的印象通常不能超过 3 这个魔法数,三屏是人类短期记忆的极限,而 80 行在一般显示器上是两屏半的代码量。

3.2.3 控制语句

  • if、else、for、while等语句中必须使用大括号,即使只有一行代码。

  • 在条件表达式中不允许有赋值操作,也不允许在判断表达式中出现复杂的逻辑组合。

    可以将复杂的逻辑运算赋值给一个具有业务含义的布尔变量。

  • 多层嵌套不能超过 3 层,如果非得使用多层嵌套,请使用设计模式。

    对于超过 3 层的if-else逻辑判断可以使用卫语句、策略模式、状态模式等来实现。

  • 避免采用取反逻辑运算符。

3.3 代码注释

3.3.1 优雅注释三要素

  • 1.Nothing is strange

    我们提倡要写注释,然后才是把注释写的精简。

  • 2.Less is more

    代码中的注释一定是精华中的精华。

  • 3.Advance with the times

    任何对代码的修改,都应该同时修改注释。

3.3.2 注释格式

  • 1.Javadoc 规范
    类、类属性和类方法的注释必须遵循 Javadoc 规范,使用文档注释(/** */)的格式。
    按 Javadoc 规范编写的注释,可以生成规范的 JavaAPI 文档,为外部用户提供非常有效的文档支持。
    而且在使用 IDE 工具编码时,IDE 会自动提示所用到的类、方法等注释,提高了编码的效率。

对于枚举类型的注释是必需的:
1)==枚举类型实在太特殊了,它的代码极其稳定==。如果它的定义和使用出现错误,通常影响较大。
2)==注释的内容不仅限于解释属性值的含义,还可以包括注意事项、业务逻辑==。如果在原有枚举类上新增或修改一个属性值,还需要加上创建和修改时间,让使用者零成本地知道这个枚举类的所有意图。
3)==枚举类的删除或者修改都存在很大的风险==。不可直接删除过时属性,需要标注为过时,同时注释说明过时的逻辑考虑和业务背景。

  • 2.简单注释
    包括单行注释和多行注释。
    特别强调此类注释不允许写在代码后方,必须写在代码上方,这是为了避免注释的参差不齐,导致代码版式混乱。
    双画线注释往往使用在方法内部,此时的注释是提供给程序开发者、维护者和关注方法细节的调用者查看的。
    因此,注释的作用更应该是画龙点睛的,通常添加在非常必要的地方,例如复杂算法或需要警示的特殊业务场景等。

4.走进 JVM

4.1 字节码

静态编译器转到源码成字节码的过程:

graph LR Java源文件 --> 词法解析 --> 语法解析 --> 语义分析 --> 生成字节码 --> 字节码

字节码必须通过类加载过程加载到 JVM 环境后,才可以执行。
执行有三种模式,第1.解释执行;第2.JIT 编译执行;第3.JIT 编译与解释混合执行(主流 JVM默认执行模式)。
混合执行模式的优势在于解释器在启动时先解释执行,省去编译时间。随着时间推进,JVM 通过热点代码统计分析,识别高频的方法调用、循环体、公共模块等,基于强大的 JlT 动态编译技术,将热点代码转换成机器码,直接交给 CPU 执行。

JIT 的作用是将 Java 字节码动态地编译成可以直接发送给处理器指令执行的机器码。

JIT 即时编译流程图:
JIT.png

类加载过程图:

类加载是一个将.class字节码文件实例化成Class对象,并进行相关初始化的过程。
ClassLoad


依赖倒置原则主要规定了两件事情:

  1. 高层模块不应该依赖底层模块,两者都应该依赖抽象
  2. 抽象不应该依赖细节,细节应该依赖抽象。

JSR(Java Specification Request)规范的内容都是抽象的,其对外发布的形式都是接口,它不提供实现,最多会指导实现

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×