在Java编码中,我们容易犯一些错误,也容易忽略一些问题,所以笔者将总结日常编码中遇到的一些经典情况进行成文并共同探讨。
1 .纠结的同名
现象
许多类的命名相同,常见于例如异常、常量、日志等类。 因此,import时有时会冠冕堂皇,这种错误有时会被掩盖。 由于同名的类功能往往也很相似,因此IDE不提示warn。
解决
写完代码后,环顾import部分,看看有没有不熟悉的东西。 请注意,替换为正确的导入后,注释是否也进行了相应的修改。
启示录
请尽量避免命名重复,特别是避免与JDK中的类重复。 否则,容易发生导入错误,同时存在多个重复名称类,搜索时也需要识别时间。
2 .考虑理所当然的API
现象
调用API时,自然会想到用名字直接自信地调用,从而导致令人吃惊的一些错误:
示例标志为真吗?
布尔标志=布尔.获取布尔(' true ); 总是可能是假的。
例2 (这是去年的今天吗)今年是2012年吗? 结果是2012年:
日历日历=绿色日历. getinstance (; 接下来是去年:
calendar.add (年日,-365; 解决方案
我问几个问题,这个方法你熟悉吗? 有同样的API吗? 区别是什么? 在示例1中,必须以如下方式进行区分:
布尔值(b ) VS布尔.透视布尔(b ) B ) VS布尔. Get布尔(b ) B; 启示录
命名更详细,评论更清晰,不知道,测试,当然不是用API,如果时间有限,就用自己最熟悉的API。
3 .溢出有时并不困难
现象
溢出并不难,但很少重现。
样本1 :
长x=整数.最大值1; x是多少钱? 竟然是-2147483648,加1却在long的范围内。 同样的东西在时间计算中很常见:
数字1数字2数字3…样本2 :
在检查正数的参数检查中,选择参数number以避免过载。 因此,以下代码的结果小于0也是因为溢出。
数字I=长.最大值; 解决
将第一个操作数设置为long类型。 例如,添加l或l。 (因为和数字1太像了,所以不推荐小写的l。 )
不知道的话,就使用过载吧。 如果参数是BigDecimal参数,使用doubleValue ()也无法解决问题。
启示录
必须对数字的运用敏感:在涉及数字计算时,如果涉及必须考虑溢出的除法运算时,必须考虑被除数为0,否则将考虑BigDecimal等。
4 .日志跑到哪里了?
现象
有时候我觉得log被击中了,为什么找不到?
示例没有堆栈跟踪!
}catch(exceptionex )例2 :找不到日志!
} catch (配置扩展)解决方案
替换为log.error(ex.getmessage )、ex )。
不是System.out,换成普通的log4j吧。
启示录
API的定义应该避免让人犯错误,如果添加重载的log.error(exception )当然不会发生错误
在产品代码中,有几种方法可以考虑是否有效。 e .打印堆栈跟踪) )来考虑终端(控制台)的位置。
5 .忘记的volatile
现象
在DCL模式下,总是忘记添加Volatile。
私有静态缓存impl实例; //lose电压解决方案
当然,加一个吧。 同步会锁定一个代码(整个方法或某个代码块),该)块(保证代码的可见性和原子性,但instance==null在第一次判断时不在范围内。 所以读取的可能是过期的空值。
启示录
我们总是认为不容易发生特定的低概率事件,例如某个时间同时发生的可能性、某个异常被抛出的可能性等,所以不控制,但是如果可能的话,按照前人的“最佳实践”写代码吧。 至少没有必要太解释为什么会有别的路。
6 .互不影响
现象
释放多个IO资源时,会抛出IOException,因此为了节省时间,有时会这样写。
publicstaticvoidinputtooutput (输入流is,输出流操作系统,假设bos关闭失败,bis是否可以关闭? 当然不能!
解决方案
扔出同样的异常,但最好分别捕获。 否则,第一个失败,下一个方面就没有释放资源的机会了。
启示录
代码/模块之间可能存在依赖关系,必须充分识别对彼此的依赖关系。
7 .用断言替换参数检查
/p>现象
如题所提,作为防御式编程常用的方式:断言,写在产品代码中做参数校验等。例如:
private void send(List< Event> eventList) {解决
换成正常的统一的参数校验方法。因为断言默认是关闭的,所以起不起作用完全在于配置,如果采用默认配置,经历了eventList != null结果还没有起到作用,徒劳无功。
启示
有的时候,代码起不起作用,不仅在于用例,还在于配置,例如断言是否启用、log级别等,要结合真实环境做有用编码。
8. 用户认知负担有时候很重
现象
先来比较三组例子,看看那些看着更顺畅?
示例一:
public void caller(int a, String b, float c, String d) {示例二:
public boolean remove(String key, long timeout) {示例三:
public static String getDigest(String filePath, DigestAlgorithm algorithm)解决
保持参数传递顺序;
remove变成了delete,显得突兀了点, 统一表达更好;
保持表达,少缩写也会看起来流畅点。
启示
在编码过程中,不管是参数的顺序还是命名都尽量统一,这样用户的认知负担会很少,不要要用户容易犯错或迷惑。例如用枚举代替string从而不让用户迷惑到底传什么string, 诸如此类。
9. 忽视日志记录时机、级别
现象
存在下面两则示例:
示例一:该不该记录日志?
catch (SocketException e)示例二:记什么级别日志?
在用户登录系统中,每次失败登录:
LOG.warn("Failed to login by "+username+");解决
移除日志记录:在遇到需要re-throw的异常时,如果每个人都按照先记录后throw的方式去处理,那么对一个错误会记录太多的日志,所以不推荐如此做;但是如果re-throw出去的exception没有带完整的trace( 即cause),那么最好还是记录下。
如果恶意登录,那系统内部会出现太多WARN,从而让管理员误以为是代码错误。可以反馈用户以错误,但是不要记录用户错误的行为,除非想达到控制的目的。
启示
日志改不改记?记成什么级别?如何记?这些都是问题,一定要根据具体情况,需要考虑:
是用户行为错误还是代码错误?
记录下来的日志,能否能给别人在不造成过多的干扰前提下提供有用的信息以快速定位问题。
10. 忘设初始容量
现象
在JAVA中,我们常用Collection中的Map做Cache,但是我们经常会遗忘设置初始容量。
cache = new LRULinkedHashMap< K, V>(maxCapacity);解决
初始容量的影响有多大?拿LinkedHashMap来说,初始容量如果不设置默认是16,超过16×LOAD_FACTOR,会resize(2 * table.length),扩大2倍:采用 Entry[] newTable = new Entry[newCapacity]; transfer(newTable),即整个数组Copy, 那么对于一个需要做大容量CACHE来说,从16变成一个很大的数量,需要做多少次数组复制可想而知。如果初始容量就设置很大,自然会减少resize, 不过可能会担心,初始容量设置很大时,没有Cache内容仍然会占用过大体积。其实可以参考以下表格简单计算下, 初始时还没有cache内容, 每个对象仅仅是4字节引用而已。
memory for reference fields (4 bytes each);
memory for primitive fields
Java typeBytes requiredboolean1bytechar2shortint4floatlong8double启示
不仅是map, 还有stringBuffer等,都有容量resize的过程,如果数据量很大,就不能忽视初始容量可以考虑设置下,否则不仅有频繁的 resize还容易浪费容量。
1、具有1-5工作经验的,面对目前流行的技术不知从何下手,需要突破技术瓶颈的可以加群。
2、在公司待久了,过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽拿高薪的可以加群。
3、如果没有工作经验,但基础非常扎实,对java工作机制,常用设计思想,常用java开发框架掌握熟练的,可以加群。
4、觉得自己很牛B,一般需求都能搞定。但是所学的知识点没有系统化,很难在技术领域继续突破的可以加群。
有需要的加群:71859422 我们一起学习一起交流