小型库的契约往往比 API 的表面积更能决定正确性。以下规则可以避免常见的 Java 集成失败。
对象契约 #
equals 必须满足自反性、对称性、传递性和一致性,并且对null返回 false。
如果两个对象相等,它们必须具有相同的hashCode;因此,在将值用作哈希键之前,
应同时重写这两个方法。对象作为映射键期间,不要修改参与相等性判断的字段。
如果继承会破坏对称性,应优先使用组合而非继承。
Object.clone执行字段复制,并且与构造函数、继承和可变字段的配合较差。
应优先使用复制构造函数或命名为copyOf的工厂方法。数组是一个范围有限的例外,
因为它们的clone具有可预测的浅数组契约。
文本、正则表达式和字符串 #
对于会重复使用的Pattern,应进行编译。Matcher.matches()要求整个输入都匹配,
而find()搜索下一个匹配区域。Pattern.quote(value)会转义放入正则表达式的
字面文本;Matcher.quoteReplacement(value)会转义替换字符串。两者不能互换。
从 JDK 9 开始,HotSpot 的String实现使用紧凑字符串:通过byte[]加编码标记,
可以紧凑地表示 Latin-1 文本,否则使用 UTF-16。这属于实现细节;Java String
仍然是 UTF-16 代码单元序列,并且必须在 I/O 边界显式进行Charset转换。
计数器和枚举集合 #
AtomicLong提供一个精确且线性一致的值。LongAdder在存在竞争时将更新分散到
多个单元中,因此更新具有更好的扩展性,但与更新并发执行的sum()不是原子快照。
对于度量指标使用LongAdder;当每个观测到的总数都会驱动正确性判断时,使用
AtomicLong。
Enum<E extends Enum<E>>为单个枚举类型提供类型安全的排序和操作。EnumSet和
EnumMap利用这一封闭的键空间,并保留枚举声明顺序;对于枚举域,应优先使用
它们而不是HashSet和HashMap。
集合和线程 #
Thread.State报告BLOCKED、WAITING和TIMED_WAITING等 JVM 级别的状态;
它用于观察,而不是同步协议。应使用join、future、闩锁或结构化所有权来协调线程。
Map是否允许null键或值取决于具体实现:HashMap允许一个 null 键,而
ConcurrentHashMap拒绝 null 键和值;在自然排序下,TreeMap也可能拒绝 null
键。不要假定Map接口的可选操作一定成功,而应验证所选实现的契约。
Arrays.asList(array)返回一个由所提供对象数组支持的固定大小列表。set会更新
该数组,但add和remove会失败。传入int[]会创建包含一个元素的List<int[]>,
因为可变参数不会将基本类型数组装箱。需要装箱值列表时,请使用
Arrays.stream(ints).boxed().toList()。
评论
在 GitHub 上阅读或评论(发表评论需要登录)