阅读阿里巴巴Java开发手册,截取一些需要注意的地方

原文出自  阿里巴巴Java开发手册,禁止用于商业用途,违者必究

【推荐】集合初始化时,指定集合初始值大小。 
说明:HashMap 使用 HashMap(int initialCapacity) 初始化, 
正例:initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即loader factor)默认为 0.75,如果暂时无法确定初始值大小,请设置为 16(即默认值)。 

反例:HashMap 需要放置 1024 个元素,由于没有设置容量初始大小,随着元素不断增加,容量 7 次被迫扩大,resize 需要重建hash 表,严重影响性能。    原来多次扩大Map容量会影响效率。


【强制】使用工具类 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)也会随之修改。


【推荐】循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。 
说明:反编译出的字节码文件显示
每次循环都会 new 出一个 StringBuilder 对象,然后进行append 操作,最后通过 toString 方法返回String 对象,造成内存资源浪费。 
反例: 
String str = "start";      
    for (int i = 0; i < 100; i++) {          
str = str + "hello";      

}  


【推荐】 类内方法定义顺序依次是:公有方法或保护方法 > 私有方法 > getter/setter方法。 

说明:公有方法是类的调用者和维护者最关心的方法,首屏展示最好;保护方法虽然只是子类关心,也可能是“模板设计模式”下的核心方法;而私有方法外部一般不需要特别关心,是一个黑盒实现;因为承载的信息价值较低,所有 Service 和DAO的getter/setter 方法放在类体最后。

学习一个新名词,黑盒实现


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

 反例:比如显示成交总额涨跌情况,即正负 x%,x 为基本数据类型,调用的 RPC 服务,调用不成功时,返回的是默认值,页面显示为0%,这是不合理的,应该显示成中划线。所以包装数据类型的null 值,能够表示额外的信息,如:远程调用失败,异常退出。 


【参考】各层命名规约:  
A) Service/DAO 层方法命名规约 
 1) 获取单个对象的方法用 get 做前缀。 
 2) 获取多个对象的方法用 list 做前缀。 
 3) 获取统计值的方法用 count 做前缀。 
 4) 插入的方法用 save/insert 做前缀。 
 5) 删除的方法用 remove/delete 做前缀。 
 6) 修改的方法用 update 做前缀。 
B) 领域模型命名规约 
 1) 数据对象:xxxDO,xxx 即为数据表名。 
 2) 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。 
 3) 展示对象:xxxVO,xxx 一般为网页名称。 

 4) POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。


【推荐】使用entrySet 遍历Map 类集合KV,而不是keySet 方式进行遍历。 
说明:keySet 其实是遍历了 2次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出key 所对应的 value。而 entrySet 只是遍历了一次就把 key 和value 都放到了entry 中,效率更高。如果是JDK8,使用Map.foreach方法。 

正例:values()返回的是 V 值集合,是一个list 集合对象;keySet()返回的是K 值集合,是一个Set 集合对象;entrySet()返回的是K-V 值组合集合。 

                Map<String,String> testMap = new HashMap<String, String>();
		
		Iterator<Entry<String, String>> iterator = testMap.entrySet().iterator();
		
		while(iterator.hasNext()){
			Entry<String, String> next = iterator.next();
			String key = next.getKey();
			String value = next.getValue();
		}


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

说明:有序性是指遍历的结果是按某种比较规则依次排列的。稳定性指集合每次遍历的元素次序是一定的。如:ArrayList 是 order/unsort;HashMap 是unorder/unsort;TreeSet 是order/sort。


【强制】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。 

说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。


【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 
说明:Executors 返回的线程池对象的弊端如下: 
1)FixedThreadPool和 SingleThreadPool: 
 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。 
2)CachedThreadPool和 ScheduledThreadPool: 
 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。


【强制】高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。 

说明:尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用 RPC 方法。 


【参考】下列情形,需要进行参数校验: 
 1) 调用频次低的方法。 
 2) 执行时间开销很大的方法。此情形中,参数校验时间几乎可以忽略不计,但如果因为参数错误导致中间执行回退,或者错误,那得不偿失。 
 3) 需要极高稳定性和可用性的方法。 
 4) 对外提供的开放接口,不管是 RPC/API/HTTP 接口。 

 5) 敏感权限入口。


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


【强制】对大段代码进行 try-catch,这是不负责任的表现。catch时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的 catch 尽可能进行区分异常类型,再做对应的异常处理。


【强制】捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。


【参考】在代码中使用“抛异常”还是“返回错误码”,对于公司外的 http/api 开放接口必须使用“错误码”而应用内部推荐异常抛出;跨应用间RPC 调用优先考虑使用Result 方式,封装 isSuccess()方法、“错误码”、“错误简短信息”。 
说明:关于RPC 方法返回方式使用 Result 方式的理由: 
 1)使用抛异常返回方式,调用方如果没有捕获到就会产生运行时错误。 

 2)如果不加栈信息,只是 new 自定义异常,加入自己的理解的 error message,对于调用端解决问题的帮助不会太多。如果加了栈信息,在频繁调用出错的情况下,数据序列化和传输的性能损耗也是问题。 


【强制】表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。 
说明:MySQL 在Windows 下不区分大小写,但在 Linux 下默认是区分大小写。因此,数据库名、表名、字段名,都不允许出现任何大写字母,避免节外生枝。 
正例:aliyun_admin,rdc_config,level3_name 

反例:AliyunAdmin,rdcConfig,level_3_name 


【推荐】字段允许适当冗余,以提高查询性能,但必须考虑数据一致。冗余字段应遵循: 
 1)不是频繁修改的字段。 
 2)不是 varchar 超长字段,更不能是 text字段。 
正例:商品类目名称使用频率高,字段长度短,名称基本一成不变,可在相关联的表中冗余存储类目名称,避免关联查询。

发布了25 篇原创文章 · 获赞 14 · 访问量 7万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章