Java代码精简优化指引

https://mp.weixin.qq.com/s/Icn5_RZzFHB9WsKip2ZZ6g

1. 利用语法

利用三元表达式

简单的if-else,特别是一句的条件代码块,要用三元表达式简化(但要特别注意返回类型不一致和自动拆装箱特性引入的NPE问题)。

for-each语句

用新的for(Object o : collection)替换老式的下标循环(如不需要下标)或迭代器while。IDEA可以通过collection.for/fori快速生成循环代码块。
此处要注意,对于ArrayList,fori和for-each方式差别不大,尤其是在需要下标时,推荐使用前者;而在LinkedList实现中,后者对前者优势巨大,必须使用后者进行遍历。

利用try-with-resource语句

所有实现 Closeable 接口的“资源”,均可采用 try-with-resource 进行简化。甚至可以支持在try的括号内有多个资源,在try块正常/异常结束后,会按声明相反的顺序关闭资源。


【推荐】可直接写在try的后面

利用return关键字

利用 return 关键字,可以提前函数返回,避免定义中间变量。


【反例】没有特殊理由,可以直接return true,不需要hasSuper变量

利用static关键字

利用 static 关键字,可以把字段变成静态字段,也可以把函数变为静态函数,调用时就无需初始化类对象。工具类使用该方法简化操作,仅有方法,没有“状态”。

利用Lambda表达式

lambda 表达式大量替代匿名内部类的使用,在简化了代码的同时,更突出了原有匿名内部类中真正有用的那部分代码。IDEA会给出普通匿名类到Lambda表达式的修改建议。


【对比】注意精简编译器可以自己推断的部分

利用方法引用

方法引用(::),可以简化 lambda 表达式,省略变量声明和函数调用。不熟悉的话只需要正常写Lambda表达式,IDEA会给出改进建议。不是所有类中的方法都可以在Lambda表达式中转换为方法引用,例如对方法的反向(!)使用

利用静态导入

静态导入(import static),当程序中大量使用同一静态常量和函数时,可以简化静态常量和函数的引用。


【推荐】大量使用的常量可以通过静态引入减少代码量

利用unchecked异常

Java 的异常分为两类:Checked 异常和 Unchecked 异常。Unchecked 异常继承了RuntimeException ,特点是代码不需要处理它们也能通过编译,所以它们称作 Unchecked 异常。利用 Unchecked 异常,可以避免不必要的 try-catch 和 throws 异常处理。
如果要使用unchecked异常,必须让自定义异常继承自RuntimeException。由于这种异常不被编译器check,则必须有处理这种异常的意识,不论在哪个层级。

【推荐】createUser方法既不用try-catch,也不用throws异常

2. 利用注解

利用Lombok注解

Lombok 提供了一组有用的注解,可以用来消除Java类中的大量样板代码。但要注意Lombok的@Data注解内容过多的问题。

利用Validation注解

Valiation注解是Java原生提供的一组Bean校验注解,它是一个JSR380(Bean Validation 2.0)的规范定义的实现,基于Java8。
而Hibernate有个Hibernate Validator是对其规范的实现和扩展(即Java和Hibernate都实现了JSR380)。
正常情况下,springboot程序用starter-web可以引入,具体关系如下


starter-web中的validation包

利用Valiation可以大大简化对参数的校验,有一套自己的方法,可用于controller接口和普通方法bean的校验。

利用@NonNull注解

利用注解特性

注解有以下特性可用于精简注解声明:
1、当注解属性值跟默认值一致时,可以删除该属性赋值;
2、当注解只有value属性时,可以去掉value进行简写;
3、当注解属性组合等于另一个特定注解时,直接采用该特定注解。

【对比】注意去掉并不需要的注解内容

3. 利用泛型

泛型接口

利用泛型限定接口的方式,明确接口内方法的对象类型,减少类型转换以及随之而来的ClassCastException

【推荐】接口上用泛型可限定实现类的对应类型

泛型类

同泛型接口,作用于具体类。一方面是对字段的类型限定,另一方面可以提高类的复用性

【推荐】通过泛型提高类的复用性

泛型方法

用法比较稀少,主要在入参和出参上保持一个灵活性。

4. 利用自身方法

利用构造方法

构造方法,可以简化对象的初始化(new一个对象)和设置属性操作(一堆set)。对于属性字段较少的类,可以自定义构造方法。

利用Set的add方法

利用 Set 的 add 方法的返回值,可以直接知道该值是否已经存在,可以避免调用 contains 方法判断存在。如果该集合在add前不存在该值,则返回true,即插入到集合的动作是成功的。这也就是为什么可以通过add方法返回值替代contains。

利用Map的computeIfAbsent方法

利用 Map 的 computeIfAbsent 方法,可以保证获取到的对象非空,从而避免了不必要的空判断和重新设置值。
典型应用是一个Map<K,List<V>>的结构,为了构造后面的list(map的value),就需要利用该函数,在K指示的List不存在时,new出一个对象再插入V即可。如果这个List已经存在,则直接加入,不会new出新对象。

【推荐】如果不存在,则创建的方式

利用链式编程

链式编程,也叫级联式编程,调用对象的函数时返回一个this对象指向对象本身,达到链式效果,可以级联调用。链式编程的优点是:编程性强、可读性强、代码简洁。

  • StringBuilder的append方法
  • Stream流的中间操作
  • Lombok的Builder注解
    ////////////////////////////////////////////

5. 利用工具方法

引入第三方库时的一些考量

  1. 不要重新发明轮子
  2. 能不引入就不引入第三方库(Java8可以替代JodaTime)
  3. 用大不用小,尽量选择开源社区流行的,Github上活跃的第三方库
  4. 注意平衡,不要为了一个小功能,引入一个庞大的库

避免空值判断

通过工具类的等值方法,可以避免现式判空和NPE隐患,Optional本质上也是一种工具方法

  • Optional
  • CollectionUtils
  • StringUtils

避免条件判断

工具类提供的集合方法,根据方法名描述的功能,避免了条件判断

  • Stream流的anyMatch,allMatch等
  • Math.max/min
  • StringUtils
  • ArrayUtils

简化赋值语句

各种of,as等类似方法

  • Arrays.asList,注意返回list的坑的问题(只读没问题)

简化数据拷贝

  • 利用反射
  • BeanUtils.copyProperties(userDO, userVO);

简化异常断言

用断言替代判断+抛出异常的代码方式

  • assert关键字
  • Spring-core中的Assert
  • Junit中的Assert/Assertions

简化测试用例

把测试用例数据以 JSON 格式存入文件中,通过 JSON 的 parseObject 和 parseArray 方法解析成对象。虽然执行效率上有所下降,但可以减少大量的赋值语句,从而精简了测试代码。

简化算法实现

不要重复发明轮子,先在系统中找是否有类似的,实在没有再自己实现

封装工具方法

  • 工具类Utils,Helper的使用
  • 独立于业务

6. 利用数据结构

利用数组简化

对于固定上下限范围的 if-else 语句,可以用数组+循环来简化。

利用Map转化

对于映射关系的 if-else 语句,可以用Map来简化。此外,此规则同样适用于简化映射关系的 switch 语句。

利用容器类简化

Java 不像 Python 和 Go ,方法不支持返回多个对象。如果需要返回多个对象,就必须自定义类,或者利用容器类。常见的容器类有 Apache 的 Pair 类和 Triple 类, Pair 类支持返回 2 个对象, Triple 类支持返回 3 个对象。

利用ThreadLocal简化

ThreadLocal 提供了线程专有对象,可以在整个线程生命周期中随时取用,极大地方便了一些逻辑的实现。用 ThreadLocal 保存线程上下文对象,可以避免不必要的参数传递。
注意:除非对ThreadLocal非常了解,在生产环境一定要慎用,以免引起不可控的问题。

7. 利用Optional

保证值存在

orElse和orElseGet的区别

保证值合法

filter的使用

避免空判断

map的使用

8. 利用Steam

匹配集合数据

anyMatch,allMatch

过滤集合数据

filter

汇总集合数据

sum,average,reduce

转化集合数据

collect.toCollection

分组集合数据

groupingBy

分组汇总集合

groupingBy + reduce

生成范围集合

range,limit

9. 利用程序结构

返回条件表达式

条件表达式判断返回布尔值,条件表达式本身就是结果。


【反例】可直接返回条件表达式

最小化条件作用域

最小化条件作用域,尽量提出公共处理代码。

【反例】需要最小化条件分支代码块

调整表达式位置

调整表达式位置,在逻辑不变的前提下,让代码变得更简洁。【不推荐】如果过于破坏可读性,例如在一行执行读取信息并判断结果。

利用非空对象

在比较对象时,交换对象位置,利用非空对象,可以避免空指针判断。


【推荐写法】避免value和result的NPE

10. 利用设计模式

模版方法模式

模板方法模式(Template Method Pattern)定义一个固定的算法框架,而将算法的一些步骤放到子类中实现,使得子类可以在不改变算法框架的情况下重定义该算法的某些步骤。

建造者模式

组合优于继承,用另外一个类描述抽象类中的模版方法,就是建造者。

代理模式

11. 利用删除代码

删除已废弃的代码

删除项目中的已废弃的包、类、字段、方法、变量、常量、导入、注解、注释、已注释代码、Maven包导入、MyBatis的SQL语句、属性配置字段等,可以精简项目代码便于维护。

删除接口方法的public

对于接口(interface),所有的字段和方法都是 public 的,可以不用显式声明为 public 。Idea对public都会置灰处理。

删除枚举构造方法的private

枚举的构造方法无需增加private关键字

删除final方法的final

对于 final 类,不能被子类继承,所以其方法不会被覆盖,没有必要添加 final 修饰。

删除基类implements的接口

如果基类已 implements 某接口,子类没有必要再 implements 该接口,只需要直接实现接口方法即可,除非是想明确该具体类实现了某接口。


【反例】可去掉不必要的声明

删除不必要的变量

不必要的变量,只会让代码看起来更繁琐。


【反例】不必要的变量声明

综合整理

  1. 初级:【11】删除代码,【9】程序结构
  2. 初中级:【1】语法,【2】注解,【7】Optional
  3. 中级:【3】泛型,【8】Stream,【5】工具方法,【4】自身方法,【6】数据结构
  4. 中高级:【10】设计模式(Lambda表达式)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,784评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,745评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,702评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,229评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,245评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,376评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,798评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,471评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,655评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,485评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,535评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,235评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,793评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,863评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,096评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,654评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,233评论 2 341

推荐阅读更多精彩内容