Effective Java

一、 静态工厂方法
public static Boolean valueOf(boolean b) {
  return b ? Boolean.TRUE : Boolean.FALSE;
}
二、 创建对象的时候使用Builder

Builder 方式创建的对象,在调用 build() 方法之前是不会创建Request 对象的,所有的属性设置都必须在 build() 方法之前,而且创建了 Request 对象后就不可以更改其属性了,这就保证了对象状态的唯一性,而且代码的可读性也提高了。


public final class Request {
  final HttpUrl url;
 
  Request(Builder builder) {
    this.url = builder.url;
  }

public static class Builder {
    HttpUrl url;
 ...
    /* 
     * Request 对象创建器,想得到一个Request 对象必须使用build 方法, 
     * 在方法中增加对Builder参数的验证,并以异常的形式告诉给开发人员。 
     */         
    public Request build() {
    /**
     * 比如下面判断如果 url 是null的话就会抛出异常
     */
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);
    }
  }
三、 序列化和反序列化的概念
  • 把对象转换为字节序列的过程称为对象的序列化。
  • 把字节序列恢复为对象的过程称为对象的反序列化。
    对象的序列化主要有两种用途:
  • 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
  • 在网络上传送对象的字节序列。
// Serialize
        try {
            FileOutputStream fileOut = new FileOutputStream("out.ser");
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            out.writeObject(singleton);
            out.close();
            fileOut.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        singleton.setValue(2);
        // Deserialize
        Singleton singleton2 = null;
        try {
            FileInputStream fileIn = new FileInputStream("out.ser");
            ObjectInputStream in = new ObjectInputStream(fileIn);
            singleton2 = (Singleton) in.readObject();
            in.close();
            fileIn.close();
        } catch (IOException i) {
            i.printStackTrace();
        } catch (ClassNotFoundException c) {
            System.out.println("singletons.SingletonEnum class not found");
            c.printStackTrace();
        }
四、 用枚举创建单例

普通方法创建单例,构造函数不能是public

  • 普通创建单例方法,解决反序列化造成新建对象的解决方法如下:
public class Singleton implements Serializable{
    public static final Singleton INSTANCE = new Singleton();
    private Singleton() {
    }
    protected Object readResolve() {
        return INSTANCE;
    }
}
  • 普通创建单例方法,通过发射原理可以新建对象
    通过这种方式,不可访问的私有构造函数变得可访问,并且使类成为单例的整个想法中断。
Singleton singleton = Singleton.INSTANCE;
        Constructor constructor = singleton.getClass().getDeclaredConstructor(new Class[0]);
        constructor.setAccessible(true);
        Singleton singleton2 = (Singleton) constructor.newInstance();
        if (singleton == singleton2) {
            System.out.println("Two objects are same");
        } else {
            System.out.println("Two objects are not same");
        }
        singleton.setValue(1);
        singleton2.setValue(2);
        System.out.println(singleton.getValue());
        System.out.println(singleton2.getValue());
    }
  • 用枚举创建单例
public enum Singleton {
    INSTANCE;
}

上面的三行构成一个单例,不会出现上面提到的问题。由于枚举本质上是可序列化的,因此我们不需要使用可序列化的接口来实现它。反射问题也不存在。因此,100%保证JVM中只存在一个单例实例。因此,建议将此方法作为在Java中制作单例的最佳方法。
在序列化枚举时,字段变量不会被序列化。例如,如果我们对SingletonEnum 类进行序列化和反序列化 ,我们将丢失该int value 字段的值
Demo

public enum SingletonEnum {
    INSTANCE;
    int value;
    public int getValue() {
        return value;
    }
    public void setValue(int value) {
        this.value = value;
    }
}


public class EnumDemo {
    public static void main(String[] args) {
        SingletonEnum singleton = SingletonEnum.INSTANCE;
        System.out.println(singleton.getValue());
        singleton.setValue(2);
        System.out.println(singleton.getValue());
    }
}
五、创建不能被实例化的类
public class UtilityClass {
// Suppress default constructor for noninstantiability
private UtilityClass() {
throw new AssertionError();
}
... // Remainder omitted }
六、使用依赖注入,不用硬编码

依赖是指一个对象持有其他对象的引用。依赖注入则是将这些依赖对象传递给被依赖对象,而不是被依赖对象自己创建这些对象。
依赖注入框架有Dagger等

七、及时消除对过时对象的引用
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
}
八、避免使用FINALIZERS和CLEANERS
九、优先使用try-with-resources而不是try-finally

详解:http://tutorials.jenkov.com/java-exception-handling/try-with-resources.html



        int data = input.read();
        while(data != -1){
            System.out.print((char) data);
            data = input.read();
        }
    }

注意方法内的第一行:
try(FileInputStream input = new FileInputStream(“file.txt”)){
这是try-with-resources构造。该FileInputStream 变量在try括号内声明,并分配给变量。

当try块完成时,FileInputStream将自动关闭,因为FileInputStream实现了Java接口java.lang.AutoCloseable。实现此接口的所有类都可以在try-with-resources构造中使用。

如果从try-with-resources块内部抛出异常,并且在FileInputStream关闭时close()(调用时),则try块内抛出的异常将抛出到外部世界。FileInputStream禁止关闭时抛出的异常。这与本文中第一个示例中发生的情况相反,使用旧样式异常处理(关闭finally块中的资源)。
使用多种资源
您可以在try-with-resources块中使用多个资源,并使它们全部自动关闭。这是一个例子:

private static void printFileJava7()throws IOException { 

    try(FileInputStream input = new FileInputStream(“file.txt”); 
          BufferedInputStream bufferedInput = new BufferedInputStream(input)
    ){ 

        int data = bufferedInput.read(); 
        while(data!= -1){ 
            System.out.print((char)data); 
    data = bufferedInput.read(); 
        } 
    } 
}

该try-with-resources构造不仅适用于Java的内置类。您还可以java.lang.AutoCloseable在自己的类中实现接口,并将它们与try-with-resources构造一起使用。

十、如果重写Equals()方法,必须也重写HashCode()方法

Equal objects必须有相同的哈希码。

十一、不要使用原生态类型

原生态类型的泛型:不带任何实际参数的泛型名称,例如List的原生态类型就是List
可以使用通配符?或者写成List<>

十二、消除unchecked警告
  • 添加本地变量,以减少@SuppressWarnings范围
    *每次使用@SuppressWarnings("unchecked")都应注解为什么
十三、使用列表(List)而不使用数组(T[])
  • 对于类型问题,列表在编译期间报错,数组在运行期间报错
  • 集合功能多,数组效率高
十四、可变参数和泛型结合的注意点

详细内容:https://minei.me/archives/340.html

static  T[] pickTwo(T a, T b, T c) {
    switch(ThreadLocalRandom.current().nextInt(3)) {
         case 0: return toArray(a, b);
         case 1: return toArray(a, c);
         case 2: return toArray(b, c);
    }
    throw new AssertionError(); // Can't get here
}

这个调用看起来也是没什么毛病,但是执行 toArray方法之后就会报 ClassCastException,因为 toArray的返回类型是由传入的参数决定的,
编译器是无法精确的预测具体类型,所以在 pickTwo方法中会统一返回 Object[] 数组,但是pickTwo方法传入的是String,所以返回也应该是String数组,
这里隐藏了一个类型转换并且转换失败了。

上面的例子说明了把可变参数传递到另一个方法是不安全的。

十五、存储多种类型数据可以考虑类型安全的异构容器
public class Favorites {
    private Map, Object> favorites = new HashMap<>();

    public  void putFavorite(Class type, T instance) {
        favorites.put(Objects.requireNonNull(type), instance);
    }
// Achieving runtime type safety with a dynamic cast
//public  void putFavorite(Class type, T instance) { 
//favorites.put(type, type.cast(instance));
//}
    public  T getFavorite(Class type) {
        return type.cast(favorites.get(type));
    }
}

public static void main(String[] args) {

    Favorites f = new Favorites();
    f.putFavorite(String.class, "Java");
    f.putFavorite(Integer.class, 0xcafebabe);
    f.putFavorite(Class.class, Favorites.class);
     String favoriteString = f.getFavorite(String.class);

    int favoriteInteger = f.getFavorite(Integer.class);
    Class favoriteClass = f.getFavorite(Class.class);
    System.out.printf("%s %x %s%n", favoriteString, favoriteInteger, favoriteClass.getName());
}
十六、用实例域代替序数
//不建议使用
// Abuse of ordinal to derive an associated value - DON'T DO THIS
public enum Ensemble {
   SOLO,   DUET,   TRIO, QUARTET, QUINTET,
   SEXTET, SEPTET, OCTET, NONET,  DECTET;

   public int numberOfMusicians() {
      return ordinal() + 1;
   }
}
//建议使用
public enum Ensemble {
   SOLO(1), DUET(2), TRIO(3), QUARTET(4), QUINTET(5),
   SEXTET(6), SEPTET(7), OCTET(8), DOUBLE_QUARTET(8),
   NONET(9), DECTET(10), TRIPLE_QUARTET(12);

   private final int numberOfMusicians;

   Ensemble(int size) {
      this.numberOfMusicians = size;
   }

   public int numberOfMusicians() {
      return numberOfMusicians;
   }
}
十七、使用EnumSet而不是位字段

位字段

// Bit field enumeration constants - OBSOLETE!
public class Text {
   public static final int STYLE_BOLD
   public static final int STYLE_ITALIC
   public static final int STYLE_UNDERLINE
   public static final int STYLE_STRIKETHROUGH = 1 << 3;  // 8

   // Parameter is bitwise OR of zero or more STYLE_ constants
   public void applyStyles(int styles) { ... }
}

text.applyStyles(STYLE_BOLD | STYLE_ITALIC);

** EnumSet**

// EnumSet - a modern replacement for bit fields
public class Text {
   public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH }

   // Any Set could be passed in, but EnumSet is clearly best
   public void applyStyles(Set