Effective Java笔记(三)
5、枚举和注解
34、用enum代替int常量
- 枚举所有的域都应该为final的;
- 特定于常量的方法实现;
- 声明一个抽象的apply方法,并在特定于常量的类主体中,用具体的方法覆盖每个常量的抽象apply方法。
plus("+") {
@Override
public double apply(double x, double y){return x+y;}},
minus("-") {
@Override
public double apply(double x, double y){return x-y;}};
public abstract double apply(double x,double y);
private final String fuhao;
demo_34(String fuhao) {
this.fuhao = fuhao;
}
35、用实例域代替序数
- 永远不要根据枚举的序数(ordinal())导出与它关联的值,而是要它保存在一个实例域中
36、用EnumSet代替位域
final EnumSet<demo_34> demo_34s = EnumSet.allOf(demo_34.class);
System.out.println(demo_34s);
final EnumSet<demo_34> set = EnumSet.of(plus, minus);
System.out.println(set);
37、用EnumMap代替索引
- 映射键知道如何将自己翻译成可打印字符串的枚举;
- 最好不要使用序号来索引数组,而要使用EnumMap;
Map<DayOfWeek, String> map = new EnumMap<>(DayOfWeek.class);
map.put(DayOfWeek.MONDAY, "星期一");
map.put(DayOfWeek.TUESDAY, "星期二");
38、用接口模拟可扩展的枚举
- 虽然无法编写可扩展的枚举类型,却可以通过编写接口以及实现该接口基础的枚举类型来对它进行模拟;
interface Operation{
public double apply(double x,double y);
}
public enum demo_34 implements Operation{
plus("+") {
@Override
public double apply(double x, double y){return x+y;}},
minus("-") {
@Override
public double apply(double x, double y){return x-y;}};
private final String fuhao;
demo_34(String fuhao) {
this.fuhao = fuhao;
}
}
39、注解模式优先于命名模式
- 命名模式例如早期JUnit3 测试类命名Test*,
40、坚持使用Override注解
- 在你想要覆盖超类声明的每个方法声明中使用Override注解。
41、用标记接口定义类型
- 标记接口是不包含方法声明的接口,它只是指明一个类实现了某种属性的接口。例如:Serializable接口,通过实现这个接口,类表明它的实例可以被写道ObjectOutputStream中;
- 标记接口定义的类型是有被标记类的实例实现的;标记注解则没有定义这样的类型;
- 标记接口胜过标记注解的一个优点:它们可以被更加精确的进行锁定。
- 标记注解胜过标记接口的最大有点在于:它们是更大的注解机制的一部分;
6、Lambda和Stream
42、Lambda优先于匿名类
- Java8中 带有单个抽象方法的接口是特殊的,值得特殊对待的观点,这些接口现在被称作函数接口,允许利用Lambda表达式创建这些接口的实例;
- 类型推导 根据上下文推导这些类型;
- 尽可能不要序列化一个Lambda(或匿名类实例)
43、方法引用优先于Lambda
- 比Lanbda更简洁
44、检查使用标准的函数接口
- 只要标准的函数接口能够满足需求,通常应该优先考虑,而不是专门再构建一个新的函数接口。
- java.util.Function共有43个接口。
- 必须始终使用@FunctionalImterface注解对自己编写的函数接口进行标注。
45、谨慎使用Stream
- 直到调用终止操作才会开始计算,对于完成终止操作不需要的数据元素,将永远不会计算。
46、优先选择Stream中无副作用的函数
- 纯函数:指其结果只取决于输入的函数,它不依赖任何可变的状态,也不更新任何状态;
47、Stream要优先用Collection作为返回类型
48、谨慎使用Stream并行
- 如果源头是来自Stream.iterate,或者使用了中间操作的limit,那么并行也不可能提升性能;
- 在Stream通过并行获得的性能,最好通过ArrayList、HashMap、HashSet、和ConcurrentHashMap实例、数组、int范围或long范围
- 并行的最佳终止操作是做减法;
7、方法
49、检查参数的有效性
- 可使用Object.requireNonNull 进行null检查;
50、必要时进行保护性拷贝
- 假设类的客户端会尽其所能破坏这个类的约束条件,因此你必须保护地设计程序;
- 保护性拷贝时检查参数的有效性之前进行的,并且有效性检查是针对拷贝之后的对象,而不是针对原始的对象;
51、谨慎设计方法签名
- 谨慎选择方法的名称
- 不要过于追求提供便利的方法
- 如果不能确定,最好不要提供快捷方式
- 避免过长的参数列表(四个参数或更少)
- 参数类型优先使用接口,而不是类
- boolean参数,优先使用两个元素的枚举类型
52、慎用重载
- 要使用哪个重载方法是在编译时做出决定的
53、慎用可变参数
54、返回零长度的数组或者集合,而不是null
- 永远不要返回null,而不返回一个零长度的数组或集合
55、谨慎返回optional
- 永远不要通过返回Optinal的方法返回null;
- 永远不应该返回基本包装类型的optional;
- 如果无法返回结果并且没有返回结果时客户端必须执行特殊的处理,那么就应该声明方法返回Optional;
- 容器类型包括集合、映射、Stream、数组和Optional,都不应该被包装在optional中。
56、为所有导出的API元素编写文档注释
- < p> < i> html 标签
- @param @return @throws 标签后面的短语都不能用句点来结束
- @throws 标签之后的文字应该包含 if
参考《Effective Java》