JAVA基础之代码简洁之道

JAVA基础之代码简洁之道

背景


软件质量,不但依赖于架构及项目管理,更与代码质量紧密相关。简洁高效的代码不但易于阅读,更能避免潜在BUG与风险,提升代码质量。近期,一位Oracle程序员在Hacker News上吐槽本身的工做,引发了热议。 avatarjava

这个工程师的核心痛点是,Oracle经历长期的产品线迭代,代码异常庞大、逻辑复杂,整个代码中充斥着神秘的宏命令。每新增一个特性或者修复BUG,该工程师都须要大量的调研,当心谨慎的进行着平常的工做。而Oracle每次的版本发布都经历数百万次的测试,脑补一下,如噩梦通常。那么咱们应该如何编写简洁高效的代码呢?其实业内有过不少相关书籍,好比经典的书籍有《代码整洁之道》、《编写可读代码的艺术》、《重构:改善既有代码的设计》,可用于修炼内功。以及咱们有严格的代码规范以及方便的静态代码扫描工具,可用于增强研发代码质量能力。程序员

简洁之术


其实代码规范和静态代码扫描工具可以帮助咱们完成不少代码简洁的工做。诸如:注释、命名、方法、异常、单元测试等多个方面。但却没法总结了一些代码简洁最佳实践,其实Java是面向对象语音,而面向对象的特征是封装、继承、多态,巧妙的运用这三大特性、理解Java的一些关键字特性、语音特性、阅读JDK源码,就能够写出相对简洁的代码了。算法

简化逻辑

// 修改前``编程

if(list.size()>0) {
    return true;
} else {
    return false;    
}

// 修改后设计模式

return list.size()>0;

(1) if/else 语法:if语句包含一个布尔表达式,当if语句的布尔表达式值为false时,else语句块会被执行;数组

(2) return 关键字:返回一个任意类型的值;安全

(3) list.size()>0 表达式:list.size()方法自己是一个返回int类型数值的函数,而与>0组成了一个布尔表达式;多线程

省略无心义赋值

avatar

(1)局部变量list的数据类型与该方法的返回值类型一致,而多余的变量也将会增长JVM垃圾回收的消耗;架构

(2)局部变量list只是负责接收了mapper.queryList(params)的返回值,而并无其余逻辑处理;app

(3)此代码存在于service层和mapper层之间,能够在框架层面进一步抽象,利用注解、java8 default方法等进一步改进;

最小化判断

avatar

代码中if else的存在只是由于sendMessage函数的第二个参数会有两种状况(成功/失败),尽可能让判断最小化;

set方法治理

avatar

(1)大坨的set方法很影响代码可读性,可封装成特定方法或者使用lombok工具简化代码;

(2)局部变量就近声明,增长可读性,局部变量声明和使用地方距离遥远,会致使的读者频繁滑动;

(3)可不声明变量尽可能不要声明多余的变量,冗余代码;(如date、time两段代码);

巧用JAVA8特性-函数式编程简化代码

JAVA8特性“函数式编程”,使用Lambdas咱们能作到什么?

(1)遍历集合(List、Map等)、Sum、Max、Min、Avg、Sort、Distinct等等 (2)函数接口 (3)谓词(Predicate)使用 (4)实现Map和Reduce (5)实现事件处理/简化多线程

内、外部循环

avatar

上述代码是传统方式的遍历一个List的写法,简单来讲主要有3个不足:

(1)只能顺序处理list中的数据(process one by one)

(2)不能充分利用多核cpu

(3)不利于编译器优化(jit)

而使用函数式编程能规避上面的三个问题:

(1)不必定须要顺序处理List中的元素,顺序能够不肯定

(2)能够并行处理,充分利用多核CPU的优点

(3)有利于JIT编译器对代码进行优化

(4)代码看起来更简洁,彻底交给编译器内部循环

default方法

在Java8中,接口中的方法能够被实现,用关键字 default 做为修饰符来标识,接口中被实现的方法叫作 default 方法。使用default方法,当接口发生改变的时候,实现类不须要作改动,全部的子类都会继承 default 方法。

avatar

当一个接口扩展另一个包含默认方法的接口的时候,有如下3种处理方式。

(1)彻底无视默认方法(直接继承上级接口的默认方法)

(2)从新申明默认方法为抽象方法(无实现,具体子类必需再次实现该方法)

(3)从新实现默认方法(重写了默认方法的实现,依然是一个默认方法)

日期处理

Java8中新增了LocalDate和LocalTime接口,为何要搞一套全新的处理日期和时间的API?由于旧的java.util.Date实在是太难用了。

(1)java.util.Date月份从0开始,一月是0,十二月是11,变态吧!java.time.LocalDate月份和星期都改为了enum,就不可能再用错了。

(2)java.util.Date和SimpleDateFormatter都不是线程安全的,而LocalDate和LocalTime和最基本的String同样,是不变类型,不但线程安全,并且不能修改。

(3)java.util.Date是一个“万能接口”,它包含日期、时间,还有毫秒数,若是你只想用java.util.Date存储日期,或者只存储时间,那么,只有你知道哪些部分的数据是有用的,哪些部分的数据是不能用的。在新的Java8中,日期和时间被明确划分为LocalDate和LocalTime,LocalDate没法包含时间,LocalTime没法包含日期。

固然,LocalDateTime才能同时包含日期和时间。

新接口更好用的缘由是考虑到了日期时间的操做,常常发生往前推或日后推几天的状况。用java.util.Date配合Calendar要写好多代码,并且通常的开发人员还不必定能写对。

一、Clock时钟。Clock类提供了访问当前日期和时间的方法,Clock是时区敏感的,能够用来取代System.currentTimeMillis(),来获取当前的微秒数。某一个特定的时间点也可使用Instant类(为Final类)来表示,Instant类也能够用来建立老的java.util.Date对象。

avatar

二、LocalDate和LocalTime、LocalDateTime(均为Final类,不带时区)的一系列计算。LocalDateTime和Instant二者很像都是不带时区的日期和时间,Instant中是不带时区的即时时间点。好比:两我的都是2018年4月14日出生的,一个出生在北京,一个出生在纽约;看上去他们是一块儿出生的(LocalDateTime的语义),其实他们是有时间差的(Instant的语义)

avatar

Streams与集合

Stream是对集合的包装,一般和lambda一块儿使用。使用lambdas能够支持许多操做。如 map,filter,limit,sorted,count,min,max,sum,collect等等。 一样,Stream使用懒运算,他们并不会真正地读取全部数据。遇到像getFirst()这样的方法就会结束链式语法,经过下面一系列例子介绍:好比我有个Person类,就是一个简单的pojo, 针对这个对象,咱们可能有这样一系列的运算需求。

avatar avatar

传递行为,而不只仅是传值

//sumAll算法很简单,完成的是将List中全部元素相加。

public static int sumAll(List<Integer> numbers) {

    int total = 0;
    
    for (int number : numbers) {
    
        total += number;
        
    }
    
    return total;
    
}

sumAll算法很简单,完成的是将List中全部元素相加。某一天若是咱们须要增长一个对List中全部偶数求和的方法sumAllEven,那么就产生了sumAll2,以下:

public static int sumAll2(List<Integer> numbers) {

    int total = 0;
    
    for (int number : numbers) {
    
        if (number % 2 == 0) {
        
            total += number;  
            
        }  
        
    }
    
    return total;
    
}

又有一天,咱们须要增长第三个方法:对List中全部大于3的元素求和,那是否是继续加下面的方法呢?sumAll3

public static int sumAll3(List<Integer> numbers) {

    int total = 0;
    
    for (int number : numbers) {
    
        if (number > 3) {
        
            total += number;  
            
        }  
        
    }
    
    return total;
    
}

观察这三个方法咱们发现,有不少重复内容,惟一不一样的是方法中的if条件不同(第一个能够当作if(true)),若是让咱们优化,可能想到的第一种重构就是策略模式吧,代码以下:

avatar

这无疑使用设计模式的方式优化了冗余代码,可是可能要额外增长几个类,之后扩展也要新增,下面看看使用lambda如何实现,声明方法:第一个参数仍是咱们以前传递的List数组,第二个看起来可能有点陌生,经过查看jdk能够知道,这个类是一个谓词(布尔值的函数)

public static int sumAllByPredicate(List<Integer> numbers, Predicate<Integer> p) {  

        int total = 0;  
        
        for (int number : numbers) {  
        
            if (p.test(number)) {  
            
                total += number;  
                
            }  
            
        }  
        
        return total;  
        
    }  
    
//调用:

sumAllByPredicate(numbers, n -> true);

sumAllByPredicate(numbers, n -> n % 2 == 0);

sumAllByPredicate(numbers, n -> n > 3);

代码是否是比上面简洁了不少?语义也很明确,重要的是无论之后怎么变,均可以一行代码就修改了。。。万金油啊。

其余

JAVA8 还推出了不少特性,来简化代码。好比String.join函数、Objects类、Base64编码类。

字符串拼接

avatar

Objects类

avatar

Base64编码

avatar

总结

好的代码须要不停的打磨,做为一个优秀的工程师,咱们应该严格遵照,每次提交的代码要比迁出的时候更好。常常有人说,做为工程师必定要有团队精神,但这种精神并非说说而已的,须要实际的行动来体现的。设计模式、JDK的新特性都是咱们能够借助的经验,编码完成后思考一下,还可不能够在简化、优化,不要成为一个“做恶”的工程师。

做者简介

马铁利,随行付架构部负责人 & TGO鲲鹏会北京分会会员,10年全栈工程师,擅长微服务分布式架构设计。主要负责随行付架构部平常管理;参与构建微服务平台周边基础设施及中间件;负责随行付对外开源等事宜。

相关文章
相关标签/搜索