后知后觉,那些码了百万行代码后才知道的低级错误!!!

后知后觉,那些码了百万行代码后才知道的低级错误!!!

菜鸟的成长是艰辛的,是不堪的,是独自打拼的。但是请你和我一样不要放弃,先看下博主自己喜欢的这个小故事:

                   《卖油翁》
               宋.欧阳修

陈康肃公尧咨善射,当世无双 ,公亦以此自矜。

尝射于家圃,有卖油翁释担而立,睨之,久而不去。

见其发矢十中八九,但微颔之。

康肃问曰:”汝亦知射乎?吾射不亦精乎?”

翁曰:”无他, 但手熟尔。”

康肃忿然曰:”尔安敢轻吾射!”

翁曰:”以我酌油知之。”

乃取一葫芦置于地,以钱覆其口,徐以杓酌油沥之,自钱孔入,而钱不湿。

因曰:”我亦无他,唯手熟尔。”康肃笑而遣之。

此与庄生所谓解牛斫轮者何异?

这是一篇浅显的文言文,初中水平,相信大家都能看懂吧,具体意思不做详细说明,我想让你记住的是“无他,但手熟尔”。

世界上哪有那么多的天赋秉异的编码高手,每个人光鲜的背后都是努力的汗水,大学时教我java基础的老师曾在课堂上说过:“你亲自敲过的代码不会背叛你”。愿你我共勉吧。

当你亲自参与项目,并实际编码行数超过100W行时(不用追求精确啦,只是为了说明量大的泛指),你会渐渐明白并且尝试去规避一些低级错误,尽管这些错误在刚入门时觉得理所当然。

- 1.多次频繁操作字符串时,不要使用String类进行字符串的拼接

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

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

- 2.使用集合转数组的方法,必须使用集合的 toArray(T[] array)

使用集合转数组的方法,必须使用集合的 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 错误。

- 3.不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator方式,如果并发操作,需要对 Iterator 对象加锁。

正例:

List<String> list = new ArrayList<>(); 
list.add("1"); 
list.add("2"); 
Iterator<String> iterator = list.iterator(); 
while (iterator.hasNext()) { 
String item = iterator.next(); 
if (删除元素的条件) { 
iterator.remove(); 
} 
}

反例:

for (String item : list) { 
if ("1".equals(item)) { 
list.remove(item); 
} 
} 

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

- 4.使用 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 值组合集合。

- 5.对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁

说明:线程一需要对表 A、B、C 依次全部加锁后才可以进行更新操作,那么线程二的加锁顺序也必须是 A、B、C,否则可能出现死锁。

- 6.在高并发场景中,避免使用”等于”判断作为中断或退出的条件。

说明:如果并发控制没有处理好,容易产生等值判断被“击穿”的情况,使用大于或小于的区间判断条件来代替。
反例:判断剩余奖品数量等于 0 时,终止发放奖品,但因为并发处理错误导致奖品数量瞬间变成了负数,这样的话,活动无法终止

- 7.代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑等的修改

说明:代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后,就失去了导航的意义。

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

- 9.获取当前毫秒数 System.currentTimeMillis(); 而不是newDate().getTime();

说明:如果想获取更加精确的纳秒级时间值,使用 System.nanoTime()的方式。在 JDK8 中,针对统计时间等场景,推荐使用 Instant 类。

- 10.catch 时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的 catch 尽可能进行区分异常类型,再做对应的异常处理。

说明:对大段代码进行 try-catch,使程序无法根据不同的异常做出正确的应激反应,也不利于定位问题,这是一种不负责任的表现。
正例:用户注册的场景中,如果用户输入非法字符,或用户名称已存在,或用户输入密码过于简单,在程序上作出分门别类的判断,并提示给用户。

- 11.不要在 finally 块中使用 return。

说明:finally 块中的 return 返回后方法结束执行,不会再执行 try 块中的 return 语句。

- 12.避免出现重复的代码(Don’t Repeat Yourself),即 DRY 原则。

说明:随意复制和粘贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副本,容易遗漏。必要时抽取共性方法,或者抽象公共类,甚至是组件化。

正例:一个类中有多个 public 方法,都需要进行数行相同的参数校验操作,这个时候请抽取:
private boolean checkParam(DTO dto) {...}

- 13.不要使用 count(列名)或 count(常量)来替代 count(),count()是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关

说明:count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。

- 14.使用 ISNULL()来判断是否为 NULL 值。

说明:NULL 与任何值的直接比较都为 NULL。
1) NULL<>NULL 的返回结果是 NULL,而不是 false。
2) NULL=NULL 的返回结果是 NULL,而不是 true。
3) NULL<>1 的返回结果是 NULL,而不是 true。

- 15.在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明。

说明:
1)增加查询分析器解析成本。
2)增减字段容易与 resultMap 配置不一致。
3)无用字段增加网络消耗,尤其是 text 类型的字段。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值