第N次读《Effective Java》,肝了一篇到底那些技巧需要你花功夫仔细揣摩~
来源:https://blog.dogchao.cn/?p=70
正文如下:
《Effective Java》Java名著,必读。如果能严格遵从本文的原则,以编写API的质量来苛求自己的代码,会大大提升编码素质。
以下内容只记录了我自己整理的东西,还是建议读原文。为了聚焦知识点,一些说明故意忽略掉了。相当于是一篇摘要。
1、考虑用静态工厂方法替代构造函数
例子:
Integer.valueOf(“1”)、Boolean.valueOf(“true”)等。
优势:
可读性高(方法名)
性能(不一定创建对象)
灵活性高
可读性高
性能
public final class Boolean implements Serializable, Comparable<Boolean> {// 预先设置两个对象public static final Boolean TRUE = new Boolean(true);public static final Boolean FALSE = new Boolean(false);public Boolean(boolean var1) {this.value = var1;}public Boolean(String var1) {this(parseBoolean(var1));}// 工厂方法public static Boolean valueOf(boolean var0) {return var0?TRUE:FALSE; // 返回预先设置的对象,而不是创建对象}// 工厂方法public static Boolean valueOf(String var0) {return parseBoolean(var0)?TRUE:FALSE;}// ... other code}
灵活性高
public class Collections {// 私有,典型工厂private Collections() {}public static final List EMPTY_LIST = new EmptyList<>();// 工厂方法public static final <T> List<T> emptyList() {return (List<T>) EMPTY_LIST;}private static class EmptyList<E> extends AbstractList<E> implements RandomAccess, Serializable {// code}// 工厂方法public static <E> List<E> checkedList(List<E> list, Class<E> type) {// 根据具体情况,获取相应子类return (list instanceof RandomAccess ?new CheckedRandomAccessList<>(list, type) :new CheckedList<>(list, type));}// 子类1static class CheckedRandomAccessList<E> extends CheckedList<E> implements RandomAccess {CheckedRandomAccessList(List<E> list, Class<E> type) {super(list, type);}public List<E> subList(int fromIndex, int toIndex) {return new CheckedRandomAccessList<>(list.subList(fromIndex, toIndex), type);}}// 子类2static class CheckedList<E> extends CheckedCollection<E> implements List<E> {// code}}
2、多个构造函数时,考虑使用构造器
// 非Android中的AlertDialog,便于说明问题,举个例子public class AlertDialog {private int width;private int height;private String title;private String confirmText;private String denyText;private AlertDialog(){}public AlertDialog(int width, int height){ // 空白的警告框AlertDialog(width,height,null);}// 带标题的警告框public AlertDialog(int width, int height, String title){ // 带标题的警告框AlertDialog(width, height, title, "确定");}// 带标题的警告框,有确定按钮public AlertDialog(int width, int height, String title, String confirm){AlertDialog(width, height, title, confirm, null);}// 带标题的警告框,有确定按钮,取消按钮public AlertDialog(int width, int height, String title, String confirm, String denyText){// set every thing.}}
// 非Android中的AlertDialog,便于说明问题,举个例子public class AlertDialog {private int width;private int height;private String title;private String confirmText;private String denyText;public AlertDialog(){}// 空白的构造函数public void setWidth(int width){this.width = width;}// 其他set方法}
并发
无法进行参数校验。
// 非Android中的AlertDialog,便于说明问题,举个例子public class AlertDialog {private int width;private int height;private String title;private String confirmText;private String denyText;// privateprivate AlertDialog(){}// Builder中使用protected AlertDialog(Builder b){width = b.width;height = b.height;// .....if(width==0||height==0) throws new Exception("size must be set");}// 构造器public static class Builder {private int width;private int height;private String title;private String confirmText;private String denyText;// 注意:返回的Builder。public Builder setTitle(String title) {this.title = title;return this;}// 其他set...public AlertDialog build(){return AlertDialog(this);}}}
new AlertDialog.Builder().setTitle("提示").build();
3、用私有化构造器或者枚举型强化Singleton。
public class Elvis{// 注意,公有final对象public static final Elvis INSTANCE = new Elvis();private Elvis(){}}
public enum Elvis{INSTANCE;// some methods}
4、通过私有化构造器强化不可实例化的能力
public class Util{private Util(){// 抛出异常,防止内部误调用throw new AssertionError();}}
5、避免创建不必要的对象
-
对象的重用 -
昂贵的对象,使用对象池 -
廉价的对象,慎用对象池。
6、消除过期的对象引用
自己管理的内存(数组长度减小后,pop出的对象容易导致内存泄漏)
-
缓存 -
监听和回调
自己管理的内存
public class Stack{private Object[] elements;private int size = 0;private static final int DEFAULT_INITIAL_CAPACITY = 16;public Stack(){elements = new Object[DEFAULT_INITIAL_CAPACITY];}public void push(Object e){ensureCapacity();elements[size++]=e; // allocate新的堆内存和栈内存}public Object pop(){if(size==0) throw new EmptyStackException();return element[--size]; // pop出element[size],该对象不再有效。内存泄漏原因。}private void ensureCapacity(){if(elements.length==size)elements = Arrays.copyOf(elements, 2*size+1);}}
public Object pop(){if(size==0) throw new EmptyStackException();elements[size] = null; // 等待回收return element[--size];}
缓存
监听或回调
7、避免显示调用GC
8、覆盖equals方法请遵守通用约定
自反性。 x.equals(x) == true
对称性。 当前仅当y.equals(x)==true时,x.equals(y)==true
传递性。 if(x.equals(y)&&y.equals(z)),y.equals(z)==true
一致性。
非空性。 x.equals(null)==false
9、覆盖equals方法时总要覆盖hashCode
10、始终覆盖toString
11、谨慎覆盖clone
12、考虑实现Comparable接口
13、使类和成员的可访问性最小化
public Object pop(){if(size==0) throw new EmptyStackException();elements[size] = null; // 等待回收return element[--size];}
private static final Thing[] PRIVATE_VALUES = {...}// 此时获取到的才是“常量”public static final List<Thing> VALUS =Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES))
private static final Thing[] PRIVATE_VALUES = {...}// 此时获取到的才是“常量”public static final Thing[] values(){return PRIVATE_VALUES.clone();}
14、在公有类中使用访问方法而非公有成员变量(类似13)
15、使可变性最小化
16、复合优先于继承
17、要么就为继承而设计,并提供文档说明,要么就禁止继承
18、接口优于抽象类
19、接口只用于定义类型
20、类层次优先于标签类
21、用函数对象表示策略
22、优先考虑静态类成员
23、应指定泛型的具体类型,而不是直接使用原生类型。
24、消除非首检警告
25、列表优先于数组
// Fails at runtimeObject[] objectArray = new Long[1];objectArray[0] = "I don't fit in"; // throw exception// won't compileList<Object> ol = new ArrayList<Long>(); // Incompatible typesol.add("I don't fit in");
26、优先考虑泛型
27、优先考虑泛型方法
28、利用有限制通配符来提升API的灵活性
//public class Stack<E>{// public Stack();// public void push(E e);// public E pop();// public boolean isEmpty();//}public void pushAll(Iterator<? extends E> src){for(E e : src)push(e);}public void popAll(Collection<? super E> dst){while(!isEmpty()){dst.add(pop());}}// Get and Put Principle
29、优先考虑类型安全的异构容器
30、用enum代替int常量
public enum Apple { FUJI, PIPPIN, GRANNY_SMITH }public enum Orange { NAVEL, TEMPLE, BLOOD }
31、enum用实例域代替序数
// bad solutionpublic enum Ensemble {SOLO, DUET, TRIO, QUARTET, QUINTET,SEXTET, SEPTET, OCTET, NONET, DECTET;public int numberOfMusicians() { return ordinal() + 1; }}//// improvementpublic enum Ensemble {SOLO(1), DUET(2), TRIO(3), QUARTET(4), QUINTET(5),SEXTET(6), SEPTET(7), OCTET(8), NONET(9), DECTET(10), TRIPLE_QUARTET(12);private final int numberOfMusicians;Ensemble(int size) { this.numberOfMusicians = size; }public int numberOfMusicians() { return numberOfMusicians; }}
32、用EnumSet代替位域
public class Text{public static final int STYLE_BOLD = 1 << 0; // 1public static final int STYLE_ITALIC = 1 << 1; // 2public static final int STYLE_UNDERLINE = 1 << 2; // 4public static final int STYLE_STRIKETHROUGH = 1 << 3; // 8public void applyStyles(int styles){// ...}}//text.applyStyles(STYLE_BOLD | STYLE_ITALIC);以上叫做位图法,但是有更好的方案来传递多组常量——EnumSet。public class Text{public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH }// 注意此处,使用的是Set而不是EnumSetpublic void applyStyles(Set<Style> styles){// ...}}//text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC));
33、用EnumMap代替序数索引
34、用接口模拟可伸缩的枚举
35、注解优先于命名模式
36、坚持使用Override注解
38、检查参数的有效性
39、必要时进行保护性拷贝
public Period(Date start, Date end){this.start = new Date(start); // 使用了值的拷贝,没有使用原对象(指针)this.end = new Date(end);if(this.start.compareTo(this.end)>0)throw new IllegalArgumentException(start + " after " + end)}
public Date start(){return new Date(start);}public Date end(){return new Date(end);}
40、谨慎设计方法签名
41、慎用重载
42、慎用可变参数
43、返回0长度的数组或者集合,而不是null
44、为所有导出的API元素编写文档注释
45、将局部变量的作用域最小化
46、for-each优先于for循环
-
过滤 -
转换 -
平行迭代
48、如果需要精确的答案,请避免使用float和double
System.out.println(1.03-.42);
49、基本类型优先于装箱基本类型
基本类型只有值,装箱类具有与他们值不同的同一性。
基本类型只有功能完备的值,装箱类还具有非功能值:
null。
所以你可能会碰到NPE
基本类型省空间省时间
50、如果有更精确的类型,请避免使用字符串
字符串不适合代替其他值的类型。
-
不适合代替枚举类型(第30条) -
不适合聚集类型
51、当心字符串连接的性能
52、通过接口引用对象
53、接口优先于反射机制
丧失了编译期类型检查
-
代码笨拙冗长 性能损失
54、谨慎使用JNI
55、谨慎进行优化
56、遵守普遍的命名规则
57、只针对异常情况才使用异常
58、对于可恢复的情况使用受检异常,对于编程错误的情况使用运行时异常
如果期望调用者适当的恢复,则需要使用受检异常,强迫调用者食用try-catch代码块,或者将他们抛出去
当调用发生前提违例——违反约定的情况时,使用运行时异常,这个时候程序已经无法再执行下去了。
例如调用数组的-1索引。
热门推荐:
