10 个精妙的 Java 编码最佳实践

前言:
这是一个比Josh Bloch的Effective Java规则更精妙的10条Java编码实践的列表。和Josh Bloch的列表容易学习并且关注日常情况相比,这个列表将包含涉及API/SPI设计中不常见的情况,可能有很大影响。
让我与你分享10个微妙的Java编码最佳实践:

1. 牢记C++的析构函数

是否还记得C++的析构函数?不记得?那你真幸运,因为你不必去调试那些由于对象删除后分配的内存没有被释放而导致内存泄露的代码。尽管如此,析构函数仍提供了一个有趣的特征,它理解逆分配顺序释放内存。记住在Java中也是这样的,当你操作类析构函数语法:

使用JUnit的@Before和@After注释
分配,释放JDBC资源
调用super方法

这里有一个具体的例子,说明如何实现一些事件侦听器的SPI:

@Override
public void beforeEvent(EventContext e) {
super.beforeEvent(e);
// Super code before my code
}

@Override
public void afterEvent(EventContext e) {
// Super code after my code
super.afterEvent(e);
}
规则:无论何时使用before/after, allocate/free, take/return语义实现逻辑时,考虑是否逆序执行after/free/return操作。

2. 不要相信你早期的SPI演进判断

向客户提供SPI可以使他们轻松地向你的库/代码中注入自定义行为的方法,当心你的SPI演进判断可能会迷惑你,使你认为你 (不)打算需要附加参数。 当然,不应当过早增加功能。但一旦你发布了你的SPI,一旦你决定遵循语义版本控制,当你意识到在某种情况下你可能需要另外一个参数时,你会真的后悔在SPI中增加一个愚蠢的单参数的方法:

interface EventListener {
// Bad
void message(String message);
}

如果你也需要消息ID和消息源,怎么办?API演进将会阻止你向上面的类型添加参数。当然,有了Java8,你可以添加一个defender方法,“防御”你早期糟糕的设计决策:

interface EventListener {
// Bad
default void message(String message) {
message(message, null, null);
}
// Better?
void message(
String message,
Integer id,
MessageSource source
);
}

注意,不幸的是,defender方法不能使用final修饰符。
但是比起使用许多方法污染你的SPI,使用上下文对象(或者参数对象)会好很多。

interface MessageContext {
String message();
Integer id();
MessageSource source();
}

interface EventListener {
// Awesome!
void message(MessageContext context);
}

比起EventListner SPI你可以更容易演进MessageContext API,因为很少用户会实现它。

规则: 无论何时指定SPI时,考虑使用上下文/参数对象,而不是写带有固定参数的方法。

备注: 通过专用的MessageResult类型交换结果也是一个好主意,该类型可以使用建设者API构造它。这样将大大增加SPI进化的灵活性。

3. 避免返回匿名,本地或者内部类

Swing程序员通常只要按几下快捷键即可生成成百上千的匿名类。在多数情况下,只要遵循接口、不违反SPI子类型的生命周期(SPI subtype lifecycle),这样做也无妨。 但是不要因为一个简单的原因——它们会保存对外部类的引用,就频繁的使用匿名、局部或者内部类,因为无论它们走到哪,外部类就得跟到哪。例如,在局部类的域外操作不当的话,那么整个对象图就会发生微妙的变化从而可能引起内存泄露。

规则:在编写匿名、局部或内部类前请三思能否将它转化为静态的或普通的顶级类,从而避免方法将它们的对象返回到更外层的域中。

注意:使用双层花括号来初始化简单对象:

new HashMap

猜你喜欢

转载自blog.csdn.net/qq_42894896/article/details/81674552