背景

阿里云上有个阿里巴巴编码规范认证,我估算一下时间成本很低,多个认证也没什么坏处,就花了1分钱报了个名。这个认证报名后就可以下载链接下的编码规范,然后参加个考试应该就OK了。

共48页的规范实际上每读一遍都是要花一些时间的,因为每读一遍就会发现上面有些东西我不信。我需要去证明。过去证明过的因为JDK版本升级迭代有可能需要继续证明。下面是其中的一些证明过程。

案例1

规范原文

【强制】不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作需要对Iterator对象加锁。

正例:

List<String> list = new ArrayList<>();

list.add("1");

list.add("2");

Iterator<String> iteraot = list.interator();

while(iterator.hasNext()){

String item = iterator.next();

if(删除元素的条件) {

iterator.remove();

}

}

反例:

for(String item : list) {

if("1".equals(item)) {

list.remove(item);

}

}

说明:以上代码的执行结果肯定会出乎大家的意料,那么试一下把"1"换成"2",会是同样的结果吗?

证明

1.先按照反例例文运行测试(test1)

list里两个元素,remove掉一个,剩下1个。这应该是符合大多数人预期的。

2.按照说明把"1"换成"2"运行测试(test2)

这里没有按照预期remove掉"2",而是抛出了并发修改异常。点击到异常的地方

3.根据异常提示。找到抛出异常代码的地方查看是哪个方法抛出异常:

4.对源码做一个解析:

抛出并发修改异常的条件是modCount!=expectedModCount。

5.根据这个条件,我做一个推测:在一个操作里把这两者的值改的不一样了,因为这里调用了remove修改方法。我自然就推测是remove方法做的修改,来看remove方法的源码:

6.果然,源码中将modCount++,但是expectedModCount并没有修改。证明了推测。运行完remove后需要判断for(String item : list) ,这时候调用了迭代器的next方法。这样我理解了上面test2里为什么会抛出异常。那么再来思考下test1为什么不抛出异常呢?

7.我们来debug一下test1的情况1

运行完remove方法后,可看到这时候modCount!=expectedModCount,但是这时候只执行了hasNext(),判断了cursor != size,这时候不会执行next方法,所以不会产生异常。而下面再用到list时迭代器是新的迭代器,会把modCount=expectedModCount;

结论

如果list在for循环里调用remove方法是会抛出并发修改异常的,但是如果只修改了第1个就返回的情况是个例外,因为这时候不会调用next方法判断modCount和expectedModCount是否相等。

使用代码规范推荐的迭代器,底层remove方法会将modCount和expectedModCount一起修改,所以单线程不会有并发问题,作为类的成员变量,多线程情况下被修改就不确定了。

思考题

下面代码的执行结果是多少?

案例2

规范原文

【强制】在JDK7版本及以上,Comparotor实现类要满足如下三个条件,不然Arrays.sort、Collections.sort会抛IllegalArgumentException异常。

说明:三个条件如下

1)x、y的比较结果和y、x的比较结果相反。

2)x>y, y>z,则x>z。

3)x=y,则x,z比较结果和y,z比较结果相同。

反例:下例中没有处理相等的情况,交换两个对象判断结果并不互反,不符合第一个条件,在实际使用中可能会出现异常。

new Comparotor<Student>() {

@Override

public int compare(Student o1, Student o2) {

return o1.getId()>o2.getId()?1:-1;

}

}

证明

1.我们先来看看反例在实际使用中会抛出什么异常。

2.测试发现不论是Collections.sort还是Arrays.sort都抛出错误说必须实现Comparable接口而不是Comparator接口。而Comparable接口是不需要满足规范里所说的自反性、传递性和对称性的。

那为什么规范里会这么说呢?

3.我查了Collections类的源码,确实有几个方法用到了Comparator类。包括反转、二分查找、最大值、最小值和几个sort等。后面的原来sort是可以后面接Comparator参数的。

4. 然而我用源码的两个参数形式传入后,运行了几个例子并没有抛出非法参数异常。于是我又在源码中找线索。

Collections.sort底层用了Arrays.sort,Arrays.sort底层用了TimSort,TimSort有两处会抛出这个异常,看源码是在二分查找merge结果的时候。

5.使用这个sort果然是发生了非法参数异常。

6.具体为什么会发生异常,我打了断点,使用debug跟了一下TimSort源码,大体是有部分排序使用了if(x<y) else 判断,又一些方法里使用了if(x<=y)来判断。这两个结果对于等于的情况是冲突的。这时候会发生异常。

总结

实现Comparator的compare方法要满足自反性、对称性、传递性。

案例3

规范原文

分析

1.从上面总结来看线程安全的Map的key和value都不能为null。线程不安全的可以为null。大家都知道map的key要进行hash。对null进行hash不会空指针吗?

带着这个疑问,打开HashMap的源码看到hash方法有对null做判断,如果null则hash值为0。所以不会NPE

2.TreeMap的put方法没有对key做任何的判断,然后会调用compare方法,这里会抛出NPE

3.那么对于key如果不做特殊处理,肯定是要抛出NPE的,应该没有什么疑问了。为什么有的value为空也会NPE呢?

从上面源码可知道ConcurrentHashMap就是这么处理的,算是强制。

4.而Hashtable也是强制。

5.为什么线程安全的容器要设计成key和value不能为null呢?在网上找到了类设计者Lea的原话,主要表达的意思是因为map需要实现containsKey和containsValue方法。这个方法对于null的情况实际上是用get(XX)来实现的,如果为null就不好区分到底是因为不存在还是值就是null。

6.而线程不安全的就是按单线程处理,下面是TreeMap里containsValue的处理,如果为输入为null,并且有个对象值为null就是true了。

总结

四种常用map中线程安全的Map的key和value都不能为null。线程不安全的value都可以为null。TreeMap的key不能为null。

案例4

规范原文

【强制】多线程并行处理定时任务时,Timer 运行多个 TimeTask 时,只要其中之一没有捕获 抛出的异常,其它任务便会自动终止运行,如果在处理定时任务时使用ScheduledExecutorService 则没有这个问题。

证明

1.先让Timer 运行多个 TimeTask,让其中之一没有捕获 抛出的异常

这段代码的意思是在10秒内运行两个定时任务,其中一个定时任何每10ms做前后打印。另外一个抛出异常,结果抛出异常后两个都停止了。

2.从Timer源代码可知,本质上多个任务通过一个队列来维护。处理的时候整个过程整体try catch。那么一个出异常整个过程都停止了。

3.再验证使用ScheduledExecutorService的情况, 可看到抛出异常的线程运行了一次之后就停止了,另外一个线程一直继续运行。

4. 从源码可知如果一个工作线程出现了问题会直接从工作队列里移除,不影响其他的。

总结

ScheduledExecutorService相比Timer能避免多个任务之间的出现问题时的副作用。

案例5

规范原文

【强制】使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方 法,它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。说明:asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList 体现的是适 配器模式,只是转换接口,后台的数据仍是数组。

String[] str = new String[] { "yang", "hao" };

List list = Arrays.asList(str);第一种情况:list.add("yangguanbao"); 运行时异常。第二种情况:str[0] = "changed"; 也会随之修改,反之亦然。

证明

1.Arrays.asList()把数组转换成集合后添加元素,测试运行,果然抛出异常

跟踪源码可知道虽然asList生成的是ArrayList,但它并不是java.util.ArrayList,而是Arrays里自定义的。这个类不支持这些更新操作。

总结

小心集合类中返回一个子集或者转换类型的操作,可能返回的是内部定义的类,不是我们平时用的类,这些类中对一些操作做了限制。

思考题

下面测试类体现了规范的哪一条?

阿里巴巴编码规范(Java)证明的更多相关文章

  1. 《阿里巴巴编码规范(JAVA)》认证考后感

    2018.02.15除夕拿下了阿里云认证的<阿里巴巴编码规范(JAVA)>认证,写下这篇考后感,记录考试中碰到的一些考点. 先总体介绍下这个考试规则,50道选择题,大部分是多选题,有少部分 ...

  2. Apsara Clouder基础技能认证:阿里巴巴编码规范 考试备考题库

    考试网址: https://edu.aliyun.com/clouder/exam/intro/33 共50道题 限时90分钟 阿里云大学Apsara Clouder基础技能认证——阿里巴巴编码规范认 ...

  3. Java语言编码规范(Java Code Conventions)

    Java语言编码规范(Java Code Conventions) 名称 Java语言编码规范(Java Code Conventions) 译者 晨光(Morning) 简介 本文档讲述了Java语 ...

  4. Java语言编码规范 - Java语言编码规范(中文版)(http://doc.javanb.com/code-conventions-for-the-java-programming-language-zh/index.html)

      目录 1 介绍 1.1 为什么要有编码规范 1.2 版权声明 2 文件名 2.1 文件后缀 2.2 常用文件名 3 文件组织 3.1 Java源文件 3.1.1 开头注释 3.1.2 包和引入语句 ...

  5. 编码规范 | Java函数优雅之道(下)

    上文背景 本文总结了一套与Java函数相关的编码规则,旨在给广大Java程序员一些编码建议,有助于大家编写出更优雅.更高质.更高效的代码. 内部函数参数尽量使用基础类型 案例一:内部函数参数尽量使用基 ...

  6. 编码规范 | Java函数优雅之道(上)

    导读 随着软件项目代码的日积月累,系统维护成本变得越来越高,是所有软件团队面临的共同问题.持续地优化代码,提高代码的质量,是提升系统生命力的有效手段之一.软件系统思维有句话“Less coding, ...

  7. Java 编码规范有感

    应小组要求,开发测试都需要考阿里编码规范,因此,相当于是突击了一下关于编码规范方面的知识,目前做的项目后期需要进行项目迁移,数据迁移,功能迁移... 各种迁移... 阿里巴巴编码规范(Java)考试地 ...

  8. 阿里巴巴Java编码规范插件安装使用指南

    编码规范插件安装使用指南 阿里技术公众号公布的<阿里巴巴Java开发规约>,瞬间引起全民代码规范的热潮,后又发布了PDF的终极版,大家踊跃留言,期待配套的静态扫描工具开放出来. 为了让开发 ...

  9. 《阿里巴巴Android编码规范》阅读纪要(二)

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 本篇继续上一篇<阿里巴巴Android编码规范>阅读纪要(一) ,还是建议各位同学有时间完整阅读一下<阿里巴巴Android编码规 ...

随机推荐

  1. Blog Customization

    0 前言 从大二开始写博客,主要为了记录自己学习过程中的问题.尝试使用过CSDN.博客园等公共服务,也用Github pages搭建过自己的博客,但效果都不令人满意.CSDN广告太多,界面乌烟瘴气,而 ...

  2. INTERVIEW #2

    吐槽下ZZ的面试安排:面试时间12:30不说了,周围没有饭店,中午就没吃饭...不像其他公司给每个人安排不同的面试时间,这样可以节约大家的时间,SPDB是把一大批人都安排在了12:30,而且面试是5个 ...

  3. 集训模拟赛-1-T2

    好了不要在铺垫了直接整吧就 题目拿来!!!!!!! 倒水 (water) (256MB,1s) [问题描述] 你有一个水桶(记为 0),两个杯子(记为 1,2).水桶中的水量无限,容量也无限.1 号杯 ...

  4. Android EXCEL 解析 xls 和 xlsx,方法其实很简单

    前言 Excel 解析,一般来说是在服务端进行的,但是如果移动端要实现解析Excel的功能,那也是有实现的方法的. 不过由于Android 原生用Java/Kotlin实现,所以也可以参考服务端解析E ...

  5. vue获取当前时间 实时刷新

    需求:获取当前系统时间,在页面上展示 年月日 时分秒 ,并且实时刷新,和系统时间保持一致 第一步:在deta 里面声明两个变量第二步:把时间调用写在created() 生命周期里面,进入页面就需要调用 ...

  6. docker cannot open directory .: Permission denied无权限问题

    docker运行一个容器后,将主机中当前目录下的文件夹挂载到容器的文件夹后 进入到docker容器内对应的挂载目录中,运行命令ls后提示: ls: cannot open directory .: P ...

  7. 【SpringBoot 基础系列】实现一个自定义配置加载器(应用篇)

    [SpringBoot 基础系列]实现一个自定义配置加载器(应用篇) Spring 中提供了@Value注解,用来绑定配置,可以实现从配置文件中,读取对应的配置并赋值给成员变量:某些时候,我们的配置可 ...

  8. jQuery中的查找节点、创建节点、插入节点、删除节点、替换节点、复制节点操作方法

    jQuery操作节点我们可以分六点来讲,查找节点.创建节点.插入节点.删除节点.替换节点.复制节点. 一.查找节点 text() - 设置或返回所选元素的文本内容   ,html() - 设置或返回所 ...

  9. 操作系统实验——PV操作实现生产者消费者模型

    操作系统PV操作之--生产者消费者模型 个人博客主页 参考资料: Java实现PV操作 | 生产者与消费者 浙大公开课 在操作系统的多进程.多线程操作中经常会有因为同步.互斥等等问题引发出的一系列问题 ...

  10. 就没有我遇不到的报错!java.lang.NoClassDefFoundError: org/apache/hadoop/hbase/filter/Filter

    本来准备用HBase的Bulkload将HDFS的HFile文件导入到HBase的myuser2表中,用的是yarn jar的命令 yarn jar /export/servers/hbase-1.2 ...