本文主要是介绍阿里巴巴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 ) 【强制】对于 Service
和 DAO
类,基于 SOA
的理念,暴露出来的服务一定是接口,内部
的实现类用 Impl 的后缀与接口区别。
正例: CacheServiceImpl
实现 CacheService
接口。
2 ) 【推荐】 如果是形容能力的接口名称,取对应的形容词为接口名 ( 通常是– able 的形式 ) 。
正例: AbstractTranslator
实现 Translatable
。
2.各层命名规约
2.1领域模型命名规约
1 ) 数据对象: xxxDO , xxx
即为数据表名。
2 ) 数据传输对象: xxxDTO , xxx
为业务领域相关的名称。
3 ) 展示对象: xxxVO , xxx
一般为网页名称。
4 ) POJO
是 DO / 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<String> list=new ArrayList<>();list.add("d");list.add("33");list.add("44");list.add("55");list.add("66");List<String> list2 = list.subList(0, 2);System.out.println(list.size());//5System.out.println(list2.size());//2
总结
使用sublist()返回的只是原list对象的一个视图,因此Sublist内部类和ArrayList的内部保存数据的地址是一样得;即它们在内存中是同一个List(集合),只是parentOffset ,size等参数不同
对SubList子列表的所有操作都会最终反映到原列表上
ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException异常。
如果达到的效果要对子集进行操作,原始list不改变。建议以下方式:
List<Object> tempList = new ArrayList<Object>(list.subList(2, lists.size()));
15.使用集合转数组的方法,必须使用集合的 toArray(T[] array) ,传入的是类型完全
一样的数组,大小就是 list . size() 。
说明:使用 toArray 带参方法,入参分配的数组空间不够大时, toArray 方法内部将重新分配
内存空间,并返回新数组地址 ; 如果数组元素个数大于实际所需,下标为 [ list . size() ]
的数组元素将被置为 null ,其它数组元素保持原值,因此最好将方法入参数组大小定义与集
合元素个数一致。
正例:
List<String> list = new ArrayList<String>(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.泛型
泛型通配符<? extends T >
来接收返回的数据,此写法的泛型集合不能使用 add
方
法,而 <? super T>
不能使用 get
方法,作为接口调用赋值时易出错。
说明:扩展说一下 PECS(Producer Extends Consumer Super) 原则
:
第一、频繁往外读取内容的,适合用<? extends T >。
第二、经常往里插入的,适合用 <? super T>
18.不要在 foreach 循环(包含for,while)里进行元素的 remove / add 操作。
remove 元素请使用 Iterator方式,如果并发操作,需要对 Iterator 对象加锁。
正例:
//可以加锁
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (删除元素的条件) {
iterator.remove();
}
}
反例:
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}
说明:以上代码的执行结果肯定会出乎大家的意料,那么试一下把“1”换成“2”,会是同样的
结果吗?
19.在 JDK 7 版本及以上, 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<Student>() {
@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 值的情况
22.合理利用好集合的有序性 (sort) 和稳定性 (order) ,避免集合的无序性 (unsort) 和不稳定性 (unorder) 带来的负面影响。
说明:有序性是指遍历的结果是按某种比较规则依次排列的。稳定性指集合每次遍历的元素次
序是一定的。如:
ArrayList | order / unsort |
---|---|
HashMap | unorder / unsort |
TreeSet | order / sort |
23.SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量
如果定义为static ,必须加锁,或者使用 DateUtils 工具类。
正例:注意线程安全,使用 DateUtils
。亦推荐如下处理:
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
@ Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
说明:如果是 JDK 8 的应用,可以使用 Instant
代替 Date
, LocalDateTime
代替 Calendar
,
DateTimeFormatter
代替 SimpleDateFormat
,官方给出的解释: simple beautiful strong
immutable thread - safe
24.对象锁 类锁
参考链接
尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用 RPC 方法
25.并发修改同一记录时,避免更新丢失,需要加锁。
要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁
,使用 version 作为更新依据。
说明:如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次
数不得小于 3 次
26. 乐观锁 悲观锁
参考链接
悲观锁应用
需要使用数据库的锁机制,比如SQL SERVER 的TABLOCKX
(排它表锁) 此选项被选中时,SQL Server 将在整个表上置排它锁直至该命令或事务结束。这将防止其他进程读取或修改表中的数据。
SqlServer中使用
Begin Transelect top 1 @TrainNo=T_NO from Train_ticket with (UPDLOCK) where S_Flag=0update 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.自动拆箱装箱
参考链接
- 定义:
装箱 | 拆箱 |
---|---|
(auto)基本数据类型->包装器类型 | (auto)包装器类型->基本数据类型 |
Integer total = Integer.valueOf (99); | int totalprim = total.intValue (); |
- 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函数
@Overridepublic int intValue() {return value;}
- equals函数
@Overridepublic boolean equals(Object o) {return (o instanceof Integer) && (((Integer) o).value == value);}
- 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 问题。
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开发手册 wode摘抄的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!