阿里巴巴Java开发手册正确学习姿式是怎样的?刷新代码规范认知

不少人都知道,阿里巴巴在2017发布了《阿里巴巴Java开发手册》,先后推出了不少个版本,并在后续推出了与之配套的IDEA插件和书籍。程序员

相信不少Java开发都或多或少看过这份手册,这份手册有7个章节,覆盖了编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构以及设计规约等方面。数据库

这份规约能够说是覆盖了Java开发的方方面面,若是还有人没看的话,强烈建议你们好好看看,而且仔细研读。编程

手册中,有那么一些规则,是比较容易理解的。好比一些变量命名规范,有另一些规则,是不太容易理解的,背后是有不少思考的,有一些则是阿里这么多年来遇到的坑的总结。json

这份手册在诞生之初,是在阿里内部的,那时候就引发了普遍的讨论。最终外界看到的那份手册,是阿里无数工程师"挑剔"后的结果,能够说是凝聚了无数工程师成功的经验、踩过的坑等。数组

其实,规约最大的价值,应该是促令人去思考规约制定背后的思考。真的去探查规约背后的原理,这个过程当中能够学习到不少东西。安全

《阿里巴巴Java开发手册》尚未的小伙伴能够来私信我【阿里学习手册】领取一份多线程

1、为何阿里巴巴禁止工程师直接使用日志系统(Log4j、Logback)中的 API

阿里巴巴Java开发手册正确学习姿式是怎样的?刷新代码规范认知

 

在Java生态体系中,围绕着日志,有不少成熟的解决方案。关于日志输出,主要有两类工具。并发

一类是日志框架,主要用来进行日志的输出的,好比输出到哪一个文件,日志格式如何等。另一类是日志门面,主要一套通用的API,用来屏蔽各个日志框架之间的差别的。app

因此,对于Java工程师来讲,关于日志工具的使用,最佳实践就是在应用中使用如Log4j + SLF4J 这样的组合来进行日志输出。框架

这样作的最大好处,就是业务层的开发不须要关心底层日志框架的实现及细节,在编码的时候也不须要考虑往后更换框架所带来的成本。这也是门面模式所带来的好处。

2、为何阿里巴巴建议集合初始化时,指定集合容量大小?

阿里巴巴Java开发手册正确学习姿式是怎样的?刷新代码规范认知

 

HashMap有扩容机制,就是当达到扩容条件时会进行扩容。若是咱们没有设置初始容量大小,随着元素的不断增长,HashMap会发生屡次扩容,而HashMap中的扩容机制决定了每次扩容都须要重建hash表,是很是影响性能的。

默认状况下,当咱们设置HashMap的初始化容量时,实际上HashMap会采用第一个大于该数值的2的幂做为初始化容量。

可是,为了最大程度的避免扩容带来的性能消耗,咱们建议能够把默认容量的数字设置成expectedSize / 0.75F + 1.0F 。在平常开发中,可使用

Map<String,String>map=Maps.newHashMapWithExpectedSize(10);

来建立一个HashMap,计算的过程guava会帮咱们完成。

可是,以上的操做是一种用内存换性能的作法,真正使用的时候,要考虑到内存的影响。

3、为何阿里巴巴禁止在 foreach 循环里进行元素的 remove/add 操做

阿里巴巴Java开发手册正确学习姿式是怎样的?刷新代码规范认知

 

咱们使用的加强for循环,实际上是Java提供的语法糖,其实现原理是借助Iterator进行元素的遍历。

可是若是在遍历过程当中,不经过Iterator,而是经过集合类自身的方法对集合进行添加/删除操做。那么在Iterator进行下一次的遍历时,经检测发现有一次集合的修改操做并未经过自身进行,那么多是发生了并发被其余线程执行的,这时候就会抛出异常,来提示用户可能发生了并发修改,这就是所谓的fail-fast机制。

固然仍是有不少种方法能够解决这类问题的。好比使用普通for循环、使用Iterator进行元素删除、使用Stream的filter、使用fail-safe的类等。

4、为何阿里巴巴禁止把SimpleDateFormat定义为static类型的?

阿里巴巴Java开发手册正确学习姿式是怎样的?刷新代码规范认知

 

SimpleDateFormat主要能够在String和Date之间作转换,还能够将时间转换成不一样时区输出。可是在并发场景中SimpleDateFormat是不能保证线程安全的,须要开发者本身来保证其安全性。

SimpleDateFormat中的format方法在执行过程当中,会使用一个成员变量calendar来保存时间。这其实就是问题的关键。

若是一个SimpleDateFormat使用的是static定义的。那么这个SimpleDateFormat就是一个共享变量,随之,SimpleDateFormat中的calendar也就能够被多个线程访问到。就会有并发安全问题。

5、为何阿里巴巴不建议在for循环中使用"+"进行字符串拼接

阿里巴巴Java开发手册正确学习姿式是怎样的?刷新代码规范认知

 

使用+拼接字符串,其实只是Java提供的一个语法糖,经过反编译,咱们能够发现,其实字符串常量使用"+"在拼接过程当中,是将String转成了StringBuilder后,使用其append方法进行处理的。

若是在一个for循环中,使用"+"对字符串进行拼接,那么每一次都会从新new一个StringBuilder,而后再把String转成StringBuilder,再进行append。

而频繁的新建对象固然要耗费不少时间了,不只仅会耗费时间,频繁的建立对象,还会形成内存资源的浪费。

6、为何阿里巴巴要求程序员谨慎修改serialVersionUID 字段的值

阿里巴巴Java开发手册正确学习姿式是怎样的?刷新代码规范认知

 

序列化提供了一种方案,可让你在即便JVM停机的状况下也能把对象保存下来的方案。就像咱们平时用的U盘同样。把Java对象序列化成可存储或传输的形式(如二进制流),好比保存在文件中。这样,当再次须要这个对象的时候,从文件中读取出二进制流,再从二进制流中反序列化出对象。

虚拟机是否容许反序列化,不只取决于类路径和功能代码是否一致,一个很是重要的一点是两个类的序列化 ID 是否一致,这个所谓的序列化ID,就是咱们在代码中定义的serialVersionUID。

在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,若是相同就认为是一致的,能够进行反序列化,不然就会出现序列化版本不一致的异常,便是InvalidCastException。

7、为何阿里巴巴要求谨慎使用ArrayList中的subList方法

阿里巴巴Java开发手册正确学习姿式是怎样的?刷新代码规范认知

 

subList是List接口中定义的一个方法,该方法主要用于返回一个集合中的一段、能够理解为截取一个集合中的部分元素,他的返回值也是一个List。

可是subList 返回的并非一个全新的List,而是一个视图。就是说,SubList并无从新建立一个List,而是直接引用了原有的List(返回了父类的视图),只是指定了一下他要使用的元素的范围而已(从fromIndex(包含),到toIndex(不包含))。

因此,首先咱们没法将subList方法获得的集合直接转换成ArrayList。由于SubList只是ArrayList的内部类,他们之间并无继承关系,故没法直接进行强制类型转换。

另外,视图和原List的修改还须要注意几点,尤为是他们之间的相互影响:

  • 一、对父(sourceList)子(subList)List作的非结构性修改(non-structural changes),都会影响到彼此。
  • 二、对子List作结构性修改,操做一样会反映到父List上。
  • 三、对父List作结构性修改,会抛出异常ConcurrentModificationException。

因此,阿里巴巴Java开发手册中有另一条规定:

阿里巴巴Java开发手册正确学习姿式是怎样的?刷新代码规范认知

 

8、为何阿里巴巴建议开发者谨慎使用继承?

阿里巴巴Java开发手册正确学习姿式是怎样的?刷新代码规范认知

 


做为一门面向对象开发的语言,代码复用是Java引人注意的功能之一。Java代码的复用有继承,组合以及代理三种具体的表现形式。

继承,在写代码的时候就要指明具体继承哪一个类,因此,类的继承关系是在编译期就肯定的。而且从基类继承来的实现是没法在运行期动态改变的,所以下降了应用的灵活性。

组合,在写代码的时候能够采用面向接口编程。因此,类的组合关系通常在运行期肯定。另外,代码复用方式上也有必定区别:

  • 继承结构中,父类的内部细节对于子类是可见的。因此咱们一般也能够说经过继承的代码复用是一种白盒式代码复用。若是基类的实现发生改变,那么派生类的实现也将随之改变。这样就致使了子类行为的不可预知性。
  • 组合是经过对现有的对象进行拼装(组合)产生新的、更复杂的功能。由于在对象之间,各自的内部细节是不可见的,因此咱们也说经过组合的代码复用是黑盒式代码复用。由于组合中通常都定义一个类型,因此在编译期根本不知道具体会调用哪一个实现类的方法。

还有,Java中不支持多继承,而组合是没有限制的。就像一我的只能有一个父亲,可是他能够有很不少辆车。

9、为何阿里巴巴禁止开发人员使用isSuccess做为变量名

阿里巴巴Java开发手册正确学习姿式是怎样的?刷新代码规范认知

 

fastjson和jackson在把对象序列化成json字符串的时候,是经过反射遍历出该类中的全部getter方法,获得getHollis和isSuccess,而后根据JavaBeans规则,他会认为这是两个属性hollis和success的值。

可是Gson并非这么作的,他是经过反射遍历该类中的全部属性,并把其值序列化成json

能够看到,因为不一样的序列化工具,在进行序列化的时候使用到的策略是不同的,因此,对于同一个类的同一个对象的序列化结果多是不一样的。

若是对于同一个对象,我使用fastjson进行序列化,再使用Gson反序列化,而且恰巧这个对象中某个属性是以isXXX命名的,那么就会产生问题。

做为开发者,咱们应该想办法尽可能避免这种问题的发生,对于POJO的设计者来讲,只须要作简单的一件事就能够解决这个问题了,那就是把isSuccess改成success。

10、为何阿里巴巴不让使用 COUNT(列名)或 COUNT(常量)来替代 COUNT(*)呢

阿里巴巴Java开发手册正确学习姿式是怎样的?刷新代码规范认知

 

由于COUNT(*)是SQL92定义的标准统计行数的语法,因此MySQL对他进行了不少优化,MyISAM中会直接把表的总行数单独记录下来供COUNT(*)查询,而InnoDB则会在扫表的时候选择最小的索引来下降成本。固然,这些优化的前提都是没有进行where和group的条件查询。

在InnoDB中COUNT(*)和COUNT(1)实现上没有区别,并且效率同样,可是COUNT(字段)须要进行字段的非NULL判断,因此效率会低一些。

由于COUNT(*)是SQL92定义的标准统计行数的语法,而且效率高,因此请直接使用COUNT(*)查询表的行数!

11、为何阿里巴巴不容许使用Executors建立线程池?

阿里巴巴Java开发手册正确学习姿式是怎样的?刷新代码规范认知

 

Java中的BlockingQueue主要有两种实现,分别是ArrayBlockingQueue 和 LinkedBlockingQueue。

ArrayBlockingQueue是一个用数组实现的有界阻塞队列,必须设置容量。

LinkedBlockingQueue是一个用链表实现的有界阻塞队列,容量能够选择进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE。

这里的问题就出在:不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE。也就是说,若是咱们不设置LinkedBlockingQueue的容量的话,其默认容量将会是Integer.MAX_VALUE。

而newFixedThreadPool中建立LinkedBlockingQueue时,并未指定容量。此时,LinkedBlockingQueue就是一个无边界队列,对于一个无边界队列来讲,是能够不断的向队列中加入任务的,这种状况下就有可能由于任务过多而致使内存溢出问题。

上面提到的问题主要体如今newFixedThreadPool和newSingleThreadExecutor两个工厂方法上,并非说newCachedThreadPool和newScheduledThreadPool这两个方法就安全了,这两种方式建立的最大线程数多是Integer.MAX_VALUE,而建立这么多线程,必然就有可能致使OOM。

总结

以上,就是我以前一段时间经过学习《手册》中的部分规则以后,本身总结的一些内容,这个过程当中本身也学习到不少东西。

因此想说,这才是学习《手册》的正确姿式,这样才能最大程度的成长!

这个系列还在继续更新,后面还会会逐步完善。欢迎关注与交流。

《阿里巴巴Java开发手册》尚未的小伙伴能够来私信我【阿里学习手册】领取一份

相关文章
相关标签/搜索