《阿里JAVA编码规范》读后笔记

正所谓脑袋空空,口袋空空,坚持每天学一点东西,坚持每天记录所见所学 :P

这两天耐着性子把《阿里JAVA编码规范》看完了,觉得收获良多,先记载下自己的学习笔记,等抽空了再尝试把里面的内容结构化一下方便以后速查,惯例先放出下载地址,大公司做事情都比较有始有终,即使是编码规范文档,也是本着经营产品的心态去维护的,这份规范阿里巴巴有不断地迭代更新,大家可以到云栖论坛下载最新的版本查看。

阿里JAVA编码规范.PDF
https://yq.aliyun.com/attachment/download/?id=1170
云栖论坛:
https://www.aliyun.com


治其人而不知其所以然是不行滴,在了解规范的同时,也需要了解具体的含义:

  • Note one:

【推荐】如果使用到了设计模式,建议在类名中体现出具体模式。 说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计思想。 正例:public class OrderFactory; public class LoginProxy; public class ResourceObserver;

设计模式延伸阅读:

http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html

  • Note two:

【推荐】接口类中的方法和属性不要加任何修饰符号(public 也不要加),保持代码的简洁性,并加上有效的Javadoc注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是与接口方法相关,并且是整个应用的基础常量。 正例:接口方法签名:void f(); 接口基础常量表示:String COMPANY = “alibaba”; 反例:接口方法定义:public abstract void f(); 说明:JDK8中接口允许有默认实现,那么这个default方法,是对所有实现类都有价值的默认实现。

让我真真切切地感受到看懂字节码的重要性…这里提到了Lambda表达式和JDK类库之间的桥梁 – Default方法, 同样引用别人的专业解答:

Lamba(JDK8 引入),不得不说,lamba一定程度上是简化了代码的表达,可是…可是…私以为可读性有点差,是因为我还不熟悉?lamba省去了一些必要的程序说明,对于跳跃性思维不敏感的我来说简直是灾难:

http://www.importnew.com/16436.html

Default 方法(JDK8 引入):

http://blog.csdn.net/wwwsssaaaddd/article/details/24213525

  • Note three

2) 【推荐】 如果是形容能力的接口名称,取对应的形容词做接口名(通常是–able的形式)。 正例:AbstractTranslator实现 Translatable。

这里是个存疑点..为什么要加Abstract(黑人问号!)? 那它到底是一个抽象类,还是一个具体的实现类,这里表达的意思貌似是实现类? 那Abstract就加得匪夷所思了。

  • Note four

【强制】不允许出现任何魔法值(即未经定义的常量)直接出现在代码中。 反例: String key=”Id#taobao_”+tradeId; cache.put(key, value);

受教了…原来魔法值是指未经定义的变量…

  • Note five

【推荐】常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、包内共享常量、类内共享常量。 1) 跨应用共享常量:放置在二方库中,通常是client.jar中的constant目录下。 2) 应用内共享常量:放置在一方库的modules中的constant目录下。 反例:易懂变量也要统一定义成应用内共享常量,两位攻城师在两个类中分别定义 了表示“是”的变量: 类A中:public static final String YES = “yes”; 类B中:public static final String YES = “y”; A.YES.equals(B.YES),预期是true,但实际返回为false,导致产生线上问题。
阿里巴巴 Java 开发手册
——禁止用于商业用途,违者必究—— 4 / 34
3) 子工程内部共享常量:即在当前子工程的constant目录下。 4) 包内共享常量:即在当前包下单独的constant目录下。 5) 类内共享常量:直接在类内部private static final定义。

一方库,二方库,三方库的定义,让我邪恶地想起了一库….:

http://blog.csdn.net/ypz_ghost/article/details/41113681

  • Note six

【强制】相同参数类型,相同业务含义,才可以使用Java的可变参数,避免使用Object。 说明:可变参数必须放置在参数列表的最后。(提倡同学们尽量不用可变参数编程) 正例:public User getUsers(String type, Integer… ids)

之前不知道,严格遵守就是了,best practice嘛

  • Note seven

【强制】所有的相同类型的包装类对象之间值的比较,全部使用equals方法比较。 说明:对于Integer var=?在-128至127之间的赋值,Integer对象是在IntegerCache.cache产生,会复用已有对象,这个区间内的Integer值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用equals方法进行判断。

看了很多次,还是会忘记,记录下来以备查看

  • Note eight

【强制】关于基本数据类型与包装数据类型的使用标准如下: 1) 所有的POJO类属性必须使用包装数据类型。 2) RPC方法的返回值和参数必须使用包装数据类型。 3) 所有的局部变量【推荐】使用基本数据类型。 说明:POJO类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何NPE问题,或者入库检查,都由使用者来保证。

以前POJO类的属性老是用基本类型,看来用封装类才是正解,有时候做接口什么的,真的不需要过度地为使用者处理NPE这类问题。

  • Note nine

【强制】构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在init方法中。

居然是强制的,受教了。

  • Note ten

【强制】POJO类必须写toString方法。使用IDE的中工具:source> generate toString时,如果继承了另一个POJO类,注意在前面加一下super.toString。 说明:在方法执行抛出异常时,可以直接调用POJO的toString()方法打印其属性值,便于排查问题。

以前处理异常的时候只是简单地抛出异常信息,以后会注意把相关的POJO打印一下。

  • Note eleven

【推荐】慎用Object的clone方法来拷贝对象。 说明:对象的clone方法默认是浅拷贝,若想实现深拷贝需要重写clone方法实现属性对象的拷贝。

这里就涉及到浅拷贝和深拷贝的概念,继续引用别人的帖子科普:

http://www.jb51.net/article/48201.htm

  • Note twelve

【强制】 ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException异常:java.util.RandomAccessSubList cannot be cast to java.util.ArrayList ; 说明:subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList ,而是 ArrayList 的一个视图,对于SubList子列表的所有操作最终会反映到原列表上。

【强制】 在subList场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除均产生ConcurrentModificationException 异常。

这里要划重点,省得以后犯错。 对于SubList子列表的所有操作最终会反映到原列表上!

  • Note thirteen

【强制】使用集合转数组的方法,必须使用集合的toArray(T[] array),传入的是类型完全一样的数组,大小就是list.size()。 反例:直接使用toArray无参方法存在问题,此方法返回值只能是Object[]类,若强转其它类型数组将出现ClassCastException错误。 正例:
List list = new ArrayList(2);
list.add(“guan”);
list.add(“bao”);
String[] array = new String[list.size()];
array = list.toArray(array);
说明:使用toArray带参方法,入参分配的数组空间不够大时,toArray方法内部将重新分配内存空间,并返回新数组地址;如果数组元素大于实际所需,下标为[ list.size() ]的数组元素将被置为null,其它数组元素保持原值,因此最好将方法入参数组大小定义与集合元素个数一致。

验证一下,确有此事:
这里写图片描述

  • Note fourteen

【强制】使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear方法会抛出UnsupportedOperationException异常。 说明:asList的返回对象是一个Arrays内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。 String[] str = new String[] { “a”, “b” }; List list = Arrays.asList(str); 第一种情况:list.add(“c”); 运行时异常。 第二种情况:str[0]= “gujin”; 那么list.get(0)也会随之修改。

划重点,这里留个TODO吧,以后要看下源码,不能人云亦云。

  • Note fifteen

【强制】不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。 反例:
List a = new ArrayList();
a.add(“1”);
a.add(“2”);
for (String temp : a) {
if(“1”.equals(temp)){
a.remove(temp);
}
}
说明:以上代码的执行结果肯定会出乎大家的意料,那么试一下把“1”换成“2”,会是同样的结果吗? 正例:
Iterator it = a.iterator();
while(it.hasNext()){
String temp = it.next();
if(删除元素的条件){
it.remove();
}
}

试了下反例:


    private static void checkRemoveItemInForeach() {
        List<String> a = new ArrayList<String>();
        a.add("1");
        a.add("2");
        System.out.println(a.toString());
        for (String temp : a) {
            if ("2".equals(temp)) {
                a.remove(temp);
            }
        }

        System.out.println(a.toString());
    }

条件是”1”.equals(temp)的事后没问题,但是改成”2”.equals(temp)之后就报如下错了,真是不试不知道,一试吓一跳

[1, 2]
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at ValidationDemo.checkRemoveItemInForeach(ValidationDemo.java:34)
    at ValidationDemo.main(ValidationDemo.java:7)

查了一下原因,网上有高手研究出来的结果

http://blog.csdn.net/Melod_bc/article/details/53930817

  • Note sixteen

【强制】 在JDK7版本以上,Comparator要满足自反性,传递性,对称性,不然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 Comparator() {
@Override
public int compare(Student o1, Student o2) {
return o1.getId() > o2.getId() ? 1 : -1;
}
}

Comparator用得少,记一下

  • Note seventeen

【强制】创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。 正例:
public class TimerTaskThread extends Thread {
public TimerTaskThread(){
super.setName(“TimerTaskThread”); …
}

一般都忘记给线程起名字,每次起名字都很头疼

  • Note eighteen

【强制】SimpleDateFormat 是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用DateUtils工具类。 正例:注意线程安全,使用DateUtils。亦推荐如下处理:
private static final ThreadLocal df = new ThreadLocal() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat(“yyyy-MM-dd”);
}
};
说明:如果是JDK8的应用,可以使用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替Simpledateformatter,官方给出的解释:simple beautiful strong immutable thread-safe。

SimpleDateFormat的多并发适应性欠缺,幸好jdk8有救星

  • Note nineteen

【强制】并发修改同一记录时,避免更新丢失,要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用version作为更新依据。 说明:如果每次访问冲突概率小于20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于3次。

同样的给我科普了一下乐观锁和悲观锁的定义,引用高手的解析

http://www.javaweb1024.com/java/JavaWebzhongji/2015/09/06/847.html

  • Note twenty

【强制】多线程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用ScheduledExecutorService则没有这个问题。
阿里巴巴 Java 开发手册
【推荐】使用CountDownLatch进行异步转同步操作,每个线程退出前必须调用countDown方法,线程执行代码注意catch异常,确保countDown方法可以执行,避免主线程无法执行至countDown方法,直到超时才返回结果。 说明:注意,子线程抛出异常堆栈,不能在主线程try-catch到。

划重点,不熟悉,所以记一下,以后详细研究

  • Note 21

【推荐】避免Random实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一seed 导致的性能下降。
说明:Random实例包括java.util.Random 的实例或者 Math.random()实例。 正例:在JDK7之后,可以直接使用API ThreadLocalRandom,在 JDK7之前,可以做到每个线程一个实例。

在JDK7之后,可以直接使用API ThreadLocalRandom,嗯嗯,记就是了。

  • Note 22

【推荐】代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑等的修改。 说明:代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后,就失去了导航的意义。

这让我想起了测试用例的恐惧,有时候工具是好,只是需要在适当的时候适当地用。

  • Note 23

【参考】好的命名、代码结构是自解释的,注释力求精简准确、表达到位。避免出现注释的一个极端:过多过滥的注释,代码的逻辑一旦修改,修改注释是相当大的负担。 反例:
// put elephant into fridge
put(elephant, fridge);
方法名put,加上两个有意义的变量名elephant和fridge,已经说明了这是在干什么,语义清晰的代码不需要额外的注释。

我个人是非常认同第一句话的,看了《代码简洁之道》之后,我也努力地尝试着把方法名和变量名变得通俗易懂。


*好吧,先记到这里,太多了,明天再补,接着要研究在线修复bug的相关技术,fight!*

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值