美文网首页
Java代码精简优化指引

Java代码精简优化指引

作者: 扁圆柱体 | 来源:发表于2022-04-25 23:30 被阅读0次

    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表达式)

    相关文章

      网友评论

          本文标题:Java代码精简优化指引

          本文链接:https://www.haomeiwen.com/subject/xbujohtx.html