阿里巴巴Java开发手册 wode摘抄

阿里巴巴Java开发手册摘抄

  • ==编码规约==
    • 1.接口和实现类的命名有两套规则:
    • 2.各层命名规约
        • 2.1领域模型命名规约
    • 3.不要使用一个常量类维护所有常量,按常量功能进行归类,分开维护。
    • 4.常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、包
    • 5.相同参数类型,相同业务含义,才可以使用 Java 的可变参数,避免使用 Object
    • 6. Object 的 equals 方法容易抛空指针异常,应用常量或确定有值的对象来调用equals 。
    • 7.所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。
    • 8.RPC
    • 9.关于基本数据类型与包装数据类型的使用标准如下:
    • 10.类内方法定义的顺序依次是:公有方法或保护方法 > 私有方法 > getter / setter方法。
    • 11.循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。
    • 12.类成员与方法访问控制从严
    • 13.关于 hashCode 和 equals 的处理,遵循如下规则:
    • 14. ArrayList 的 subList 结果不可强转成 ArrayList ,否则会抛出 ClassCastException
    • 15.使用集合转数组的方法,必须使用集合的 toArray(T[] array) ,传入的是类型完全
    • 16.使用工具类 Arrays . asList()
    • 17.==泛型==
    • 18.不要在 foreach 循环(包含for,while)里进行元素的 remove / add 操作。
    • 19.在 JDK 7 版本及以上, Comparator 要满足如下三个条件
    • 20.集合初始化时,指定集合初始值大小。
    • 21.高度注意 Map 类集合 K / V 能不能存储 null 值的情况
    • 22.合理利用好集合的有序性 (sort) 和稳定性 (order) ,避免集合的无序性 (unsort) 和不稳定性 (unorder) 带来的负面影响。
    • 23.SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量
    • 24.对象锁 类锁
    • 25.并发修改同一记录时,避免更新丢失,需要加锁。
    • 26. 乐观锁 悲观锁
        • 悲观锁应用
    • 27.多线程之Timer 和TimerTask
    • 28.volatile
  • ==异常日志==
    • 1. Java 类库中定义的可以通过预检查方式规避的 RuntimeException 异常不应该通过catch 的方式来处理
    • 2.try-with-resource:JDK7中新增了try-with-resource语法
    • 3.自动拆箱装箱
    • 4.NullPointerException
    • 5. Math . random() 这个方法返回是 double 类型
    • 6.获取当前毫秒数 System . currentTimeMillis();
  • ==Mysql数据库==
    • 1.表必备三字段: id , gmt _ create , gmt _ modified 。
    • 2.单表行数超过 500 万行或者单表容量超过 2 GB ,才推荐进行分库分表
    • 3.当某一列的值全是 NULL 时, count(col) 的返回结果为 0,但 sum(col) 的返回结果为NULL
    • 4.更新数据表记录时,必须同时更新记录对应的 gmt _ modified 字段值为当前时间

编码规约

1.接口和实现类的命名有两套规则:

1 ) 【强制】对于 ServiceDAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部
的实现类用 Impl 的后缀与接口区别。
正例: CacheServiceImpl 实现 CacheService 接口。
2 ) 【推荐】 如果是形容能力的接口名称,取对应的形容词为接口名 ( 通常是– able 的形式 ) 。
正例: AbstractTranslator 实现 Translatable

2.各层命名规约

2.1领域模型命名规约

1 ) 数据对象: xxxDO , xxx 即为数据表名。
2 ) 数据传输对象: xxxDTO , xxx 为业务领域相关的名称。
3 ) 展示对象: xxxVO , xxx 一般为网页名称。
4 ) POJODO / DTO / BO / VO 的统称,禁止命名成 xxxPOJO

3.不要使用一个常量类维护所有常量,按常量功能进行归类,分开维护。

说明:大而全的常量类,非得使用查找功能才能定位到修改的常量,不利于理解和维护。
正例:缓存相关常量放在类 CacheConsts 下 ; 系统配置相关常量放在类 ConfigConsts 下。

4.常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、包

内共享常量、类内共享常量。
1 ) 跨应用共享常量:放置在二方库中,通常是 client . jar 中的 constant 目录下。
2 ) 应用内共享常量:放置在一方库中,通常是 子模块 中的 constant 目录下。
反例:易懂变量也要统一定义成应用内共享常量,两位攻城师在两个类中分别定义了表示
“是”的变量:
类 A 中: public static final String YES = " yes " ;
类 B 中: public static final String YES = " y " ;
A . YES . equals(B . YES) ,预期是 true ,但实际返回为 false ,导致线上问题。
3 ) 子工程内部共享常量:即在当前子工程的 constant 目录下。
4 ) 包内共享常量:即在当前包下单独的 constant 目录下。
5 ) 类内共享常量:直接在类内部 private static final 定义

5.相同参数类型,相同业务含义,才可以使用 Java 的可变参数,避免使用 Object

说明:可变参数必须放置在参数列表的最后。 ( 提倡同学们尽量不用可变参数编程 )
正例: public User getUsers(String type, Integer… ids) {…}

6. Object 的 equals 方法容易抛空指针异常,应用常量或确定有值的对象来调用equals 。

正例:" test " .equals(object);
反例: object.equals( " test " );
说明:推荐使用 java . util . Objects # equals(JDK 7 引入的工具类 )

7.所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。

说明:对于 Integer var = ? 在-128 至 127 范围内的赋值, Integer 对象是在
IntegerCache . cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行
判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,
推荐使用 equals 方法进行判断

8.RPC

入门总结(一)RPC定义和原理

9.关于基本数据类型与包装数据类型的使用标准如下:

1 ) 【强制】所有的 POJO 类属性必须使用包装数据类型
2 ) 【强制】 RPC 方法的返回值和参数必须使用包装数据类型
3 ) 【推荐】所有的局部变量使用基本数据类型。
说明: POJO 类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何
NPE 问题,或者入库检查,都由使用者来保证。
正例:数据库的查询结果可能是 null ,因为自动拆箱,用基本数据类型接收有 NPE 风险。
反例:比如显示成交总额涨跌情况,即正负 x %, x 为基本数据类型,调用的 RPC 服务,调用
不成功时,返回的是默认值,页面显示为 0%,这是不合理的,应该显示成中划线。所以包装
数据类型的 null 值,能够表示额外的信息,如:远程调用失败,异常退出。

10.类内方法定义的顺序依次是:公有方法或保护方法 > 私有方法 > getter / setter方法。

11.循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。

说明:反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象,然后进行
append 操作,最后通过 toString 方法返回 String 对象,造成内存资源浪费。
反例:

String str = "start";
for (int i = 0; i < 100; i++) {
str = str + "hello";
}

12.类成员与方法访问控制从严

1 ) 如果不允许外部直接通过 new 来创建对象,那么构造方法必须是 private 。
2 ) 工具类不允许有 public 或 default 构造方法。
3 ) 类非 static 成员变量并且与子类共享,必须是 protected 。
4 ) 类非 static 成员变量并且仅在本类使用,必须是 private 。
5 ) 类 static 成员变量如果仅在本类使用,必须是 private 。
6 ) 若是 static 成员变量,必须考虑是否为 final 。
7 ) 类成员方法只供类内部调用,必须是 private 。
8 ) 类成员方法只对继承类公开,那么限制为 protected 。

13.关于 hashCode 和 equals 的处理,遵循如下规则:

1) 只要重写 equals ,就必须重写 hashCode 。
2) 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的
对象必须重写这两个方法。
3) 如果自定义对象作为 Map 的键,那么必须重写 hashCode 和 equals 。
说明: String 重写了 hashCode 和 equals 方法,所以我们可以非常愉快地使用 String 对象
作为 key 来使用。

14. ArrayList 的 subList 结果不可强转成 ArrayList ,否则会抛出 ClassCastException

异常,即 java . util . RandomAccessSubList cannot be cast to java . util . ArrayList .
说明: subList 返回的是 ArrayList 的内部类 SubList ,并不是 ArrayList ,而是
ArrayList 的一个视图,对于 SubList 子列表的所有操作最终会反映到原列表上

List list=new  ArrayList<>();
        
        list.add("d");
        list.add("33");
        list.add("44");
        list.add("55");
        list.add("66");
        
        List list2 = list.subList(0, 2);
        
        System.out.println(list.size());//5
        System.out.println(list2.size());//2

总结
使用sublist()返回的只是原list对象的一个视图,因此Sublist内部类和ArrayList的内部保存数据的地址是一样得;即它们在内存中是同一个List(集合),只是parentOffset ,size等参数不同
对SubList子列表的所有操作都会最终反映到原列表上
ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException异常。
如果达到的效果要对子集进行操作,原始list不改变。建议以下方式:

List tempList = new ArrayList(list.subList(2, lists.size()));
 
  

15.使用集合转数组的方法,必须使用集合的 toArray(T[] array) ,传入的是类型完全

一样的数组,大小就是 list . size() 。
说明:使用 toArray 带参方法,入参分配的数组空间不够大时, toArray 方法内部将重新分配
内存空间,并返回新数组地址 ; 如果数组元素个数大于实际所需,下标为 [ list . size() ]
的数组元素将被置为 null ,其它数组元素保持原值,因此最好将方法入参数组大小定义与集
合元素个数一致。
正例:

List list = new ArrayList(2);
list.add("guan");
list.add("bao");
String[] array = new String[list.size()];
array = list.toArray(array);

反例:直接使用 toArray 无参方法存在问题,此方法返回值只能是 Object[] 类,若强转其它
类型数组将出现 ClassCastException 错误。

16.使用工具类 Arrays . asList()

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

String[] str = new String[] { "you", "wu" };
List list = Arrays.asList(str);

第一种情况: list.add("yangguanbao"); 运行时异常。
第二种情况: str[0] = "gujin"; 那么 list.get(0) 也会随之修改。

17.泛型

泛型通配符来接收返回的数据,此写法的泛型集合不能使用 add
法,而 不能使用 get 方法,作为接口调用赋值时易出错。
说明:扩展说一下 PECS(Producer Extends Consumer Super) 原则
第一、频繁往外读取内容的,适合用
第二、经常往里插入的,适合用

18.不要在 foreach 循环(包含for,while)里进行元素的 remove / add 操作。

 remove 元素请使用 Iterator方式,如果并发操作,需要对 Iterator 对象加锁。

正例:

//可以加锁
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (删除元素的条件) {
iterator.remove();
}
}

反例:

List list = new ArrayList();
list.add("1");
list.add("2");
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}

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

19.在 JDK 7 版本及以上, Comparator 要满足如下三个条件

不然 Arrays . sortCollections . 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;
 }
};

20.集合初始化时,指定集合初始值大小。

说明: HashMap 使用 HashMap(int initialCapacity) 初始化,
正例: initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即loader
factor)默认为 0.75, 如果暂时无法确定初始值大小,请设置为 16(即默认值)。
反例: HashMap 需要放置 1024 个元素,由于没有设置容量初始大小,随着元素不断增加,容
量 7 次被迫扩大, resize 需要重建 hash 表,严重影响性能。

21.高度注意 Map 类集合 K / V 能不能存储 null 值的情况

阿里巴巴Java开发手册 wode摘抄_第1张图片

22.合理利用好集合的有序性 (sort) 和稳定性 (order) ,避免集合的无序性 (unsort) 和不稳定性 (unorder) 带来的负面影响。

说明:有序性是指遍历的结果是按某种比较规则依次排列的。稳定性指集合每次遍历的元素次
序是一定的。如:

ArrayList order / unsort
HashMap unorder / unsort
TreeSet order / sort

23.SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量

如果定义为static ,必须加锁,或者使用 DateUtils 工具类。

正例:注意线程安全,使用 DateUtils 。亦推荐如下处理:

private static final ThreadLocal df = new ThreadLocal() {
@ Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};

说明:如果是 JDK 8 的应用,可以使用 Instant 代替 DateLocalDateTime 代替 Calendar
DateTimeFormatter 代替 SimpleDateFormat ,官方给出的解释: simple beautiful strong
immutable thread - safe

24.对象锁 类锁

参考链接
尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用 RPC 方法

25.并发修改同一记录时,避免更新丢失,需要加锁。

要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用 version 作为更新依据。
说明:如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次
数不得小于 3 次

26. 乐观锁 悲观锁

参考链接

悲观锁应用

需要使用数据库的锁机制,比如SQL SERVER 的TABLOCKX(排它表锁) 此选项被选中时,SQL Server 将在整个表上置排它锁直至该命令或事务结束。这将防止其他进程读取或修改表中的数据。

SqlServer中使用

Begin Tran
	  select top 1 @TrainNo=T_NO  from Train_ticket   with (UPDLOCK)   where S_Flag=0
      update Train_ticket set T_Name=user,  T_Time=getdate(), S_Flag=1   where T_NO=@TrainNo
commit

我们在查询的时候使用了with (UPDLOCK)选项,在查询记录的时候我们就对记录加上了更新锁,表示我们即将对此记录进行更新. 注意更新锁和共享锁是不冲突的,也就是其他用户还可以查询此表的内容,但是和更新锁和排它锁是冲突的.所以其他的更新用户就会阻塞.

27.多线程之Timer 和TimerTask

Timer与TimerTask的真正原理&使用介绍
Timer和TimerTask源码解析

28.volatile

用法场景:
1.在并发场景下,通过双重检查锁 (double - checked locking) 实现延迟初始化的优
化问题隐患 ( 可参考 The " Double - Checked Locking is Broken " Declaration),可将目标属性声明为 volatile 型
2.volatile 解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题,
但是如果多写,同样无法解决线程安全问题

异常日志

1. Java 类库中定义的可以通过预检查方式规避的 RuntimeException 异常不应该通过catch 的方式来处理

比如: NullPointerException , IndexOutOfBoundsException 等等

2.try-with-resource:JDK7中新增了try-with-resource语法

当一个外部资源的句柄对象(比如FileInputStream对象)实现了AutoCloseable接口,那么就可以将上面的板式代码简化为如下形式:参考链接

public static void main(String[] args) {
    try (FileInputStream inputStream = new FileInputStream(new File("test"))) {
        System.out.println(inputStream.read());
    } catch (IOException e) {
        throw new RuntimeException(e.getMessage(), e);
    }
}

3.自动拆箱装箱

参考链接

  1. 定义:
装箱 拆箱
(auto)基本数据类型->包装器类型 (auto)包装器类型->基本数据类型
Integer total = Integer.valueOf(99); int totalprim = total.intValue();
  1. Integer的多种常见函数
  • 构造函数,它分两种情况:
    1)i >= 128 || i < -128 =====> new Integer(i) 
    2)i < 128 && i >= -128 =====> SMALL_VALUES[i + 128] 
  • valueOf函数
	 public static Integer valueOf(int i) {
	 return  i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];
	 }	
  • intValue函数
   @Override
	public int intValue() {
	     return value;
	 }	
  • equals函数
   @Override
    public boolean equals(Object o) {
       return (o instanceof Integer) && (((Integer) o).value == value);
    }
  1. Integer派别:Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。
    Double派别:Double、Float的valueOf方法的实现是类似的。每次都返回不同的对象。

4.NullPointerException

防止 NPE ,是程序员的基本修养,注意 NPE 产生的场景:
1)返回类型为基本数据类型, return 包装数据类型的对象时,自动拆箱有可能产生 NPE 。
反例: public int f() { return Integer 对象}, 如果为 null ,自动解箱抛 NPE 。
2 ) 数据库的查询结果可能为 null 。
3 ) 集合里的元素即使 isNotEmpty ,取出的数据元素也可能为 null 。
4 ) 远程调用返回对象时,一律要求进行空指针判断,防止 NPE 。
5 ) 对于 Session 中获取的数据,建议 NPE 检查,避免空指针。
6 ) 级联调用 obj . getA() . getB() . getC(); 一连串调用,易产生 NPE 。
正例:使用 JDK8 的 Optional 类来防止 NPE 问题。
阿里巴巴Java开发手册 wode摘抄_第2张图片

5. Math . random() 这个方法返回是 double 类型

注意取值的范围 0≤ x <1 ( 能够取到零值,注意除零异常 ) ,如果想获取整数类型的随机数,不要将 x 放大 10 的若干倍然后取整,直接使用 Random 对象的 nextInt 或者 nextLong 方法

6.获取当前毫秒数 System . currentTimeMillis();

而不是 new Date() . getTime()

Mysql数据库

1.表必备三字段: id , gmt _ create , gmt _ modified 。

2.单表行数超过 500 万行或者单表容量超过 2 GB ,才推荐进行分库分表

3.当某一列的值全是 NULL 时, count(col) 的返回结果为 0,但 sum(col) 的返回结果为NULL

因此使用 sum() 时需注意 NPE 问题。
正例:可以使用如下方式来避免 sum 的 NPE 问题:

 SELECT IF(ISNULL(SUM(g)) ,0, SUM(g))  FROM table;

4.更新数据表记录时,必须同时更新记录对应的 gmt _ modified 字段值为当前时间

你可能感兴趣的:(Java)