以下为译文:
  1. NULL 的过度使用
  避免过度使用 null 值是一个佳实践。例如,更好的做法是让方法返回空的 array 或者 collection 而不是 null 值,因为这样可以防止程序抛出 NullPointerException。下面代码片段会从另一个方法获得一个集合:
  </>
  List<String> accountIds = person.getAccountIds();
  for (String accountId : accountIds) {
  processAccount(accountId);
  }
  当一个 person 没有 account 的时候,getAccountIds() 将返回 null 值,程序会抛出 NullPointerException 异常。因此需要加入空检查来解决这个问题。如果将返回的 null 值替换成一个空的 list,那么 NullPointerException 也不会出现。而且,因为我们不再需要对变量 accountId 做空检查,代码将变得更加简洁。
  当你想避免 null 值的时候,不同场景可能采取不同做法。其中一个方法是使用 Optional 类型,它既可以是一个空对象,也可以是一些值的封装。
  </>
  Optional<String> optionalString = Optional.ofNullable(nullableString);
  if(optionalString.isPresent()) {
  System.out.println(optionalString.get());
  }
  事实上,Java8 提供了一个更简洁的方法:
  </>
  Optional<String> optionalString = Optional.ofNullable(nullableString);
  optionalString.ifPresent(System.out::println);
  Java 是从 Java8 版本开始支持 Optional 类型,但是它在函数式编程世界早已广为人知。在此之前,它已经在 Google Guava 中针对 Java 的早期版本被使用。
  2. 忽视异常
  我们经常对异常置之不理。然而,针对初学者和有经验的 Java 程序员,佳实践仍是处理它们。异常抛出通常是带有目的性的,因此在大多数情况下需要记录引起异常的事件。别小看这件事,如果必要的话,你可以重新抛出它,在一个对话框中将错误信息展示给用户或者将错误信息记录在日志中。至少,为了让其它开发者知晓前因后果,你应该解释为什么没有处理这个异常。
  </>
  selfie = person.shootASelfie();
  try {
  selfie.show();
  } catch (NullPointerException e) {
  // Maybe, invisible man. Who cares, anyway?
  }
  强调某个异常不重要的一个简便途径是将此信息作为异常的变量名,像这样:
  </>
  try { selfie.delete(); } catch (NullPointerException unimportant) {  }
  3. 并发修改异常
  这种异常发生在集合对象被修改,同时又没有使用 iterator 对象提供的方法去更新集合中的内容。例如,这里有一个 hats 列表,并想删除其中所有含 ear flaps 的值:
  </>
  List<IHat> hats = new ArrayList<>();
  hats.add(new Ushanka()); // that one has ear flaps
  hats.add(new Fedora());
  hats.add(new Sombrero());
  for (IHat hat : hats) {
  if (hat.hasEarFlaps()) {
  hats.remove(hat);
  }
  }
  如果运行此代码,ConcurrentModificationException 将会被抛出,因为代码在遍历这个集合的同时对其进行修改。当多个进程作用于同一列表,在其中一个进程遍历列表时,另一个进程试图修改列表内容,同样的异常也可能会出现。
  在多线程中并发修改集合内容是非常常见的,因此需要使用并发编程中常用的方法进行处理,例如同步锁、对于并发修改采用特殊的集合等等。Java 在单线程和多线程情况下解决这个问题有微小的差别。