阿里巴巴编码规范(Java)证明(上)

简介: 阿里云上有个阿里巴巴编码规范认证,我估算一下时间成本很低,多个认证也没什么坏处,就花了1分钱报了个名。这个认证报名后就可以下载链接下的编码规范,然后参加个考试应该就OK了。 共48页的规范实际上每读一遍都是要花一些时间的,因为每读一遍就会发现上面有些东西我不信。我需要去证明。过去证明过的因为JDK版本升级迭代有可能需要继续证明。下面是其中的一些证明过程。

背景


阿里云上有个阿里巴巴编码规范认证,我估算一下时间成本很低,多个认证也没什么坏处,就花了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)


1112728-20200527005423206-372237513.png


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


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


1112728-20200527005442337-1622596988.png


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


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


1112728-20200527005534886-596043570.png


4.对源码做一个解析:


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


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


1112728-20200527005617848-661894801.png


1112728-20200527005617848-661894801.png6.果然,源码中将modCount++,但是expectedModCount并没有修改。证明了推测。


运行完remove后需要判断for(String item : list) ,这时候调用了迭代器的next方法。这样我理解了上面test2里为什么会抛出异常。那么再来思考下test1为什么不抛出异常呢?


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


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


1112728-20200527005645327-1179469962.png


结论


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


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


思考题


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


1112728-20200527005706176-1857543352.png


案例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.我们先来看看反例在实际使用中会抛出什么异常。


1112728-20200527005740110-936768277.png


1112728-20200527005815745-617402803.png


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


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


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


1112728-20200527005919062-1625431069.png


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


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


1112728-20200527005949674-2102412337.png


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


1112728-20200527010039956-1749583504.png


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


总结


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



目录
打赏
0
1
1
0
2
分享
相关文章
|
3月前
|
Java开发实现图片URL地址检验,如何编码?
【10月更文挑战第14天】Java开发实现图片URL地址检验,如何编码?
127 4
|
3月前
|
Java实现随机生成某个省某个市的身份证号?如何编码?
【10月更文挑战第18天】Java实现随机生成某个省某个市的身份证号?如何编码?
249 5
|
3月前
|
Java开发实现图片地址检验,如果无法找到资源则使用默认图片,如何编码?
【10月更文挑战第14天】Java开发实现图片地址检验,如果无法找到资源则使用默认图片,如何编码?
87 2
Java中的字符集编码入门-增补字符(转载)
本文探讨Java对Unicode的支持及其发展历程。文章详细解析了Unicode字符集的结构,包括基本多语言面(BMP)和增补字符的表示方法,以及UTF-16编码中surrogate pair的使用。同时介绍了代码点和代码单元的概念,并解释了UTF-8的编码规则及其兼容性。
100 60
|
5月前
|
告别繁琐编码,拥抱Java 8新特性:Stream API与Optional类助你高效编程,成就卓越开发者!
【8月更文挑战第29天】Java 8为开发者引入了多项新特性,其中Stream API和Optional类尤其值得关注。Stream API对集合操作进行了高级抽象,支持声明式的数据处理,避免了显式循环代码的编写;而Optional类则作为非空值的容器,有效减少了空指针异常的风险。通过几个实战示例,我们展示了如何利用Stream API进行过滤与转换操作,以及如何借助Optional类安全地处理可能为null的数据,从而使代码更加简洁和健壮。
161 0
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
119 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
java核心之字符串与编码
java核心之字符串与编码
31 2
Java字符串拼接技术演进及阿里巴巴的贡献
本文主要讲述了Java字符串拼接技术的演进历程,以及阿里巴巴贡献的最新实现 PR 20273。
176 11
代码规范Java阿里巴巴
本文介绍了阿里巴巴的《Java开发手册》,这是一份业界公认的代码规范手册,涵盖编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约七个维度。手册通过强制、推荐、参考三大类规约,详细指导开发者编写高质量的Java代码。文中还提供了常见问题的正反例对比,如方法参数类型一致性、SimpleDateFormat线程安全等问题的解决方案。此外,还介绍了如何安装和使用阿里规范插件来提高编码效率。
867 9
Java字符串拼接技术演进及阿里巴巴的贡献
本文主要讲述了Java字符串拼接技术的演进历程,以及阿里巴巴贡献的最新实现 PR 20273。
118 12