关注:清晰、正确、可用、健壮、灵活、可维护
一、创建和销毁对象
1.用静态工厂方法代替构造器:类可以提供一个公有地静态工厂方法,代替构造器。
2.考虑使用构建器:如果类的可选参数过多,可使用构建器(Builder)代替构造器。
3.用私有构造器或枚举类型强化Singleton属性:将构造方法私有化,保证实例可控。
4.通过私用构造器强化不可实例化的能力:将构造方法私有化,保证实例可控。
5.优先考虑依赖注入来引用资源:类不用在定义时就决定依赖资源的具体实现,只需要在使用时确定具体实现类即可。
6.避免创建不必要的对象:能不创建新对象就尽量避免资源的浪费。
7.消除过期的对象引用:确定不用的对象,可显示的消除对它的引用,尽早释放空间。
8.避免使用终结方法和清除方法:finalizer和cleaner方法能不用就不要用
9.try-with-resource 优于try-finally:声明需要关闭的资源时,优先使用try-with-resource
二、对所有对象都通用的方法
10.覆盖equals时需遵守通用约定:equals()方法要保证自反性、对称性、传递性、一致性、null不等性
11.覆盖equals必须覆盖hashCode:保证基于散列的集合能结合该类正常使用
12.始终要覆盖toString:以更清晰的方式描述实例数据
13.谨慎的覆盖clone:实现cloneable接口,并注意是深拷贝还是浅拷贝。
14.考虑实现Comparable接口:可实现comparable接口,用于后续对该类型的集合排序
三、类和接口
15.使类和成员的可访问性最小化:迪米特法则:最小知道远原则
16.要在共有类而非公有域中使用访问方法:如果要对某些属性设置公有属性,提供公有方法,而不是将属性直接设为public
17.使可变性最小化:保证实例可变性最小。
18.复合优于继承:优先使用复合,而不是继承:将要复用的类作为一个属性放在扩展类中。
19.要么为继承提供文档说明,要么禁止继承:要对每一个可继承的类及可覆盖的方法提供文档说明,避免覆盖导致的错误
20.接口优先于抽象类:优先使用接口定义方法,而不是抽象类
21.为后代设计接口:为避免新的方法导致的错误,建议在类定义时即设计一些必要的接口。
22.接口只能用于定义类型:接口只应该用来定义类型,而不是用来导出常量
23.类层次优于标签类:明确类中层次结构,而不是根据某些标签属性设置其他属性的值。
24.静态成员类优于非静态成员类:静态成员类、非静态成员类、匿名类、局部类。
25.限制源文件为单个顶级类:一个源文件只应该声明一个顶级类。
四、泛型
26.不要使用原生态类型:原生态类型(List demo)只是为了保证历史版本兼容性,所有新声明都应该是参数化类型(List<String> demo),如有必要可使用通配符类型(List<?> demo)
27.消除非受检的警告: 非受检的警告都应该消除,实在消除不了且可保证安全的情况可使用@SuppressWarnings注解
28.列表优于数组:List<String> list 列表优于String[] 数组,因为数组是协变的,即String[]被认为是Obejct[]的字类,而List不是。故列表能更早的在编译期识别出错误的类型转换。
29.优先考虑泛型:如果某些类的参数具有不确定性,优先使用泛型支持此参数。
30.优先考虑泛型方法:如果某些类的方法参数具有不确定性,优先使用泛型支持此方法。
31.利用有限制通配符来提升API的灵活性:约束泛型可变范围
32.谨慎并用泛型和可变参数:泛型和可变参数不要一起使用,会导致一些不可控的运行时异常。
33.优先考虑类型安全的异构容器:(..)
五、枚举和注解
34.用enum代替int常量:优先使用枚举类定义某些常量信息
35.用实例域代替序数:不要使用enum自带的ordinal()方法作为排序
36.用EnumSet代替位域:
37.用EnumMap代替序数索引:
38.用接口模拟可扩展的枚举:
39.注解优于命名模式:命名模式不可靠,使用注解标明具有某些特性的类/方法/属性
40.坚持使用Override注解:覆盖的方法使用@Override方法,会使方法受检,避免不必要的低级错误。
41.用标记接口定义类型:标明实现了具有某种属性的接口
六、Lambda和表达式
42.Lambda优先于匿名类:函数接口指只具有一个方法的接口。优先使用Lambda表达式,而不是new XXX{}格式的匿名类。
43.方法引用优先于Lambda:String::new 等格式的方法引用,优于s->new String()格式的lambada
44.坚持使用标准的函数接口:使用java自带的标准函数接口(Function<T>, Consumer<T>等),不要重复造轮子。
45.谨慎使用Stream:Stream虽然语法简洁,但有时代码逻辑不太清晰,注意结合使用
46.优先使用Stream中无副作用的函数:(啥叫无副作用..)
47.Stream要优先用Collection作为返回类型:优先返回Collection格式
48.谨慎使用Sream并行:效率不一定高,且有可能会因为某些并行条件导致假死,以及一些并发问题。
七、方法
49.检查参数的有有效性:不要相信客户端的输入,校验参数合法性
50.必要时进行保护性拷贝:深拷贝还是浅拷贝,避免对原实例某些属性进行更改。
51.谨慎设计方法签名:开闭原则要求对修改关闭,故方法设计之初很重要。
52.慎用重载:重载之前若对原方法调用过程不了解,可能导致某些意想不到的错误。
53.慎用可变参数: String...args格式的可变参数
54.返回零长度数组或集合,而不是null:避免客户端需要对null额外处理或者抛出异常
55.谨慎返回optional: 还是要额外处理没有值的情况。
56.为所有导出的API元素编写文档注释: 编写文档注释
八、通用编程
57.将局部变量的作用域最小化:避免局部变量作用域过大及过早声明。
58.for-each优先于传统的for循环:优先使用forEach语句,避免一些再次获取单元素item的引用以及有可能造成的低级错误。
59.了解和使用类库:优先使用最新可靠的代码。
60.如需准确答案,避免使用float和double:0.1等无法使用float、double准确表示的值,会导致某些精度丢失。可使用BigDecimal
61.基本类型优先装箱类型:避免不必要的对象操作,简化操作。
62.如需其他类型更合适,避免使用String类型:使用更准确的类型。
63.了解String的连接性能:str1+str2这种格式的操作虽然简单,但性能很差,如有必要可使用StringBuilder
64.通过接口引用对象:Interface1 interface1 = new InterfaceImpl()格式。
65.接口优先于反射机制:反射代码臃肿且性能不高。
66.谨慎使用本地方法:尽量避免直接调用native方法
67.谨慎地进行优化: 考虑性能与优化的成本。
68.遵守普遍接受地命名惯例:遵守通用的命名格式,减少沟通理解成本
九、异常
69.只对异常的情况才使用异常:不要用试图异常处理正常流程的扭转。
70.对可恢复的情况使用受检异常,对编程错误使用运行时异常:明确异常的类型。
71.避免不必要地使用受检异常:能捕获处理就处理,尽量避免重复及不必要的检查
72.优先使用标准的异常:捕获的异常要尽量标准且准确
73.抛出与抽象对应的异常:
74.每个方法抛出的所有异常都要建立文档:文档说明
75.在细节消息中包含失败-捕获信息:当异常发生时,记录信息,有迹可查
76.努力使失败保持原子性:保证程序异常时的原子性,回滚某些状态变化
77.不要忽略异常:捕获到异常需要处理,或记录、或处理,不要不作为
十、并发
78.同步访问共享的可变数据:可变数据在并发下,要保证能同步访问,synchronized、violate
79.避免过度同步:细化需要同步的代码块
80.executor、task和stream优先于线程:避免创建消毁线程的性能
81.并发工具优先于wait和notify:优先使用concurrent包下的工具类,而不是重量级加锁工具。
82.线程安全性的文档化: 文档说明
83.慎用延迟初始化
84.不要依赖线程调度器
十一、序列化
85.其他方法优于java序列化
86.谨慎地实现Serializable接口
87.考虑使用自定义的序列化方式
88.保护性地编写readObject方法
89.对于实例控制,枚举类型优先于readResolve
90.考虑用序列化代理代替序列化