[关闭]
@SmartDengg 2016-12-14T06:50:21.000000Z 字数 2561 阅读 1132

Effective Java for Android (cheatsheet).

from : Effective Java for Android (cheatsheet).

如果不希望某各类被实例化,那么应该私有化它的构造方法,或者说你应该在那些已经私有的构造函数中主动抛出异常,来避免通过反射创建这些对象的现象发生,这尤其适用于那些只有静态方法的工具类。在基础组件中有很多这样的类,比如:

  1. public class TrafficUtils {
  2. private TrafficUtils() {
  3. throw new AssertionError("No instance");
  4. }
  5. ...
  6. }

公开一些创建实例的静态方法,来代替客户端使用new关键字直接创建。这也是我最喜欢使用的创建对象的方式,尤其当一个类有多个构造器时,你必须要通过查看构造函数的签名信息才能构造出正确的对象,这就变得非常的不可读,而使用静态工厂方法,我们可以随意的命名,只要你喜欢 :-P 。另外,在静态工厂模式下我们能将构造器用private私有化,从而保护我们的类不会客户端错误的继承,达到与final相同的作用。如:

  1. class ResponseHelper {
  2. static ResponseHelper newInstance(Response response) {
  3. return new ResponseHelper(response);
  4. }
  5. static ResponseHelper nullResponse() {
  6. return new ResponseHelper(null);
  7. }
  8. private ResponseHelper(Response response) {
  9. this.mResponse = response;
  10. }
  11. ...
  12. }

如果我们的构造函数有超过三个需要传递的参数,或者说有很多重叠构造器,那么就应该考虑使用builder来创建对象,这种模式至少三个优点,可读性强,安全性好,当然最重要的是,它能够保证生成的对象是不可变的,因为一旦创建好,外部几乎没有修改的机会。

对于对象的可变性,这一点说来话长,原作者所涉及到的点也很有限,但我们始终应该知道,一个不可变对象应该是简单的,因为一旦对象创建成功,其内部元素的约束关系在实例的整个生命周期中都不能够再发生变化,从而有效降低我们的维护成本;而且本质上是线程安全的,允许多线程并发访问对象,并且内部结构不会遭到破坏;由于不可变性的存在,它应该能够被复用,因此不可变对象理应被自由的共享。

这也就是作者在原文中提到的:

simplicitythread-safety and shareability

我只是做了些通俗的解释。

静态内部类不会持有外部类的引用,额。。。已经被说烂的老梗,避免内存泄漏的发生,如果内部类的生命周期比外部类更长的话,可怕的内存泄漏就要发生了 :-(

在开发基础类库的时候,我一直倡导能够尽早的抛出那些打破约束的异常,比如传参异常等,但很可惜,我只能在运行时去做动态检查,如果能够在编译时就发现,那简直是太好了。然而泛型做到了,虽然它也不是万能,但是它至少能够让我们尽快发现问题,如果直到运行时才发现的话,那说明我们已经出错很久了: (

我们需要记住的是,泛型只在编译时起作用,它在运行时会被擦除,不过这也足够了,编译器已经做了它力所能及的所有事情。不得不提的是泛型的出现(JDK 5),不仅提高了程序的可读性,还保证了程序的安全性(如果你能很好的解决“非受检警告”)。

原作者所提到的这一点,称得上是一个好习惯,尤其是在做基础组件开发的过程中,如果某个函数的泛型类型是集合,那么即便没有匹配的数据被检索到,也不应该返回null,而应该返回一个
空集合,恰好我手边有这样的一个例子,在做流量监控模块时,如果想要查看之前设置过得"白名单地址"集合以及内部元素,而恰好客户端在初始化的时候并没传入这样的参数,那么它始终应该返回一个空的集合,这样也能避免做额外的非空校验。

  1. public class TrafficAPIInterceptor implements Interceptor {
  2. private static final URL[] EMPTY_URLS = new URL[0];
  3. private List<URL> mURLs;
  4. ...
  5. URL[] whitelistingAsArray() {
  6. if (mURLs == null) return EMPTY_URLS;
  7. return mURLs.toArray(new URL[mURLs.size()]);
  8. }
  9. List<URL> whitelistingAsList() {
  10. if (mURLs == null) return Collections.emptyList();
  11. return Collections.unmodifiableList(mURLs);
  12. }
  13. ...
  14. }

额。。。。。看来原作者的实践也不多啊。其实我们的编译器已经对字符串的连接做了足够的优化,对于那些能够在编译时就明确取值的字符串操作,在编译时会进行计算,而那些变量字符串的累加,编译器也做了优化,它会为我们用StringBuilder进行连接,想了解更多的童鞋可以用javap反编译一下。我就不在此赘述了 : )

这一点与作者的观点保持一致“如果希望调用者能够适当地恢复数据,那么这种场景下就应该使用受检异常(checked exception)”,因为它会强迫调用者通过try-catch进行捕获,然后进行数据恢复或其他退避策略。而且对于异常的使用,我们应该始终牢记,异常应该只出现在异常状况发生的情况下,它不应该被用来作为控制数据的手段。而且,我们在开发过程中,应该慎用throwable.printStackTrace();,它会输出日志到控制台,甚至是线上环境。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注