5、遗忘的volatile

  现象

  在DCL模式中,总是忘记加一个Volatile。

private static CacheImpl instance;  //lose volatile
public static CacheImpl getInstance() {
    if (instance == null) {
        synchronized (CacheImpl.class) {
            if (instance == null) {
                instance = new CacheImpl ();
            }
        }
    }
    return instance;
}

  解决

  毋庸置疑,加上一个吧,synchronized 锁的是一块代码(整个方法或某个代码块),保证的是这”块“代码的可见性及原子性,但是instance == null第一次判断时不再范围内的。所以可能读出的是过期的null。

  启示

  我们总是觉得某些低概率的事件很难发生,例如某个时间并发的可能性、某个异常抛出的可能性,所以不加控制,但是如果可以,还是按照前人的“佳实践”来写代码吧。至少不用过多解释为啥另辟蹊径。

  6、不要影响彼此

  现象

  在释放多个IO资源时,都会抛出IOException ,于是可能为了省事如此写:

public static void inputToOutput(InputStream is, OutputStream os,
           boolean isClose) throws IOException {
    BufferedInputStream bis = new BufferedInputStream(is, 1024);
    BufferedOutputStream bos = new BufferedOutputStream(os, 1024);
    ….
    if (isClose) {
       bos.close();
       bis.close();
    }
}

  假设bos关闭失败,bis还能关闭吗?当然不能!

  解决办法

  虽然抛出的是同一个异常,但是还是各自捕获各的为好。否则第一个失败,后一个面没有机会去释放资源了。

  启示

  代码/模块之间可能存在依赖,要充分识别对相互的依赖。

  7、用断言取代参数校验

  现象

  如题所提,作为防御式编程常用的方式:断言,写在产品代码中做参数校验等。例如:

private void send(List< Event> eventList)  {
    assert eventList != null;
}

  解决

  换成正常的统一的参数校验方法。因为断言默认是关闭的,所以起不起作用完全在于配置,如果采用默认配置,经历了eventList != null结果还没有起到作用,徒劳无功。

  启示

  有的时候,代码起不起作用,不仅在于用例,还在于配置,例如断言是否启用、log级别等,要结合真实环境做有用编码。

  8、用户认知负担有时候很重

  现象

  先来比较三组例子,看看那些看着更顺畅?

  示例一:

public void caller(int a, String b, float c, String d) {
    methodOne(d, z, b);
    methodTwo(b, c, d);
}
public void methodOne(String d, float z, String b)
public void methodTwo(String b, float c, String d)

  示例二:

public boolean remove(String key, long timeout) {
             Future< Boolean> future = memcachedClient.delete(key);
public boolean delete(String key, long timeout) {
             Future< Boolean> future = memcachedClient.delete(key);

  示例三:

public static String getDigest(String filePath, DigestAlgorithm algorithm)
public static String getDigest(String filePath, DigestAlgorithm digestAlgorithm)

  解决

  1、保持参数传递顺序;

  2、remove变成了delete,显得突兀了点, 统一表达更好;

  3、保持表达,少缩写也会看起来流畅点。

  启示

  在编码过程中,不管是参数的顺序还是命名都尽量统一,这样用户的认知负担会很少,不要要用户容易犯错或迷惑。例如用枚举代替string从而不让用户迷惑到底传什么string, 诸如此类。