一、 静态工厂方法
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<>
十二、消除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