“Effective Java” 被许多人看做是编写高效且可维护的 Java 代码的重要指导书之一。Android 使用 Java 开发是否意味着里面的建议都要用上?不完全是。 有些人认为这本书给出的大多数建议不适用于 Android 开发。在我看来,情况并非如此。 我认为这本书的一些部分是不适用,不管是因为不是所有 Java 功能都已优化到能与 Android 一起使用(例如枚举、序列化等),还是因为是移动设备的限制例如 Dalvik/ART 表现不同于桌面版的 JVM)。 但不管怎样,本书中的大多数范式还是可以在稍作修改后或直接使用,从而构建一个更健康、更简洁和更易维护的代码库。 这篇博客整合了我在开发 Android 应用时,从 Effective Java 中学习到的重要内容。对于那些读过这本书的人,这篇文章就当是重新回顾一下,对于那些(还)没有阅读它的人,可以把这篇文章当作试读。 强制非实例化(non-instantiability)如果你不想使用 new 关键字来创建对象,强制它使用私有构造函数。 这对那些仅包含静态函数的工具类尤其有用。 class MovieUtils {
private MovieUtils() {} static String titleAndYear(Movie movie) {
[...]
}
} 静态工厂用静态工厂方法 (同时私有化构造器) 代替使用 new 和构造函数。这些工厂方法有自己的名称,不需要每次都返回一个对象的新实例,还可以根据需要返回不同的子对象。 class Movie {
[...]
public static Movie create(String title) {
return new Movie(title);
}
} [更新] 我们的读者 @stsdema28 提供了一个有用的提示:静态工厂方法会造成测试困难。如果遇到这种问题,需要使用在测试中使用可模拟的非静态工厂(或者一个可实现的假接口)。 构建器(Builder)如果你的对象需要超过 3 个构造参数,那就使用构建器来构造对象。这写起来可能有点繁琐,但它扩展性良好而且容易看明白。如果你要创建值类,可以考虑 AutoValue。 class Movie {
static Builder newBuilder() {
return new Builder();
}
static class Builder {
String title;
Builder withTitle(String title) {
this.title = title;
return this;
}
Movie build() {
return new Movie(title);
}
}
private Movie(String title) {
[...]
}
}
// Use like this:
Movie matrix = Movie.newBuilder().withTitle("The Matrix").build(); 避免易变性不可变对象在它的整个生命周期都保持不变。这类对象所需要的数据都是在它创建的时候提供给它的。使用不可变对象有一些优点,比如简单、线程安全以及可共享。 class Movie {
[...]
Movie sequel() {
return Movie.create(this.title + " 2");
}
}
// Use like this:
Movie toyStory = Movie.create("Toy Story");
Movie toyStory2 = toyStory.sequel(); 保持每个类都不可有点困难。确有必要的情况下只能尽量保持类不可变(比如使用 private final 字段和 private class)。在移动端创建对象开销很大,所以注意不要过度创建。 静态成员类如果你定义了一个不依赖于外部类的内部类,千万别忘了把它定义成静态的。如果不这样做,会导致每个内部类的实例都持有外部类的引用。 class Movie {
[...]
static class MovieAward {
[...]
}
} 随处可见的泛型Java 提供了类型安全支持。尽量避免使用原始类型或对象类型。在大多数情况下,泛型提供了编译时让代码类型安全的机制。 // DON'T
List movies = Lists.newArrayList();
movies.add("Hello!");
[...]
String movie = (String) movies.get(0);
// DO
List<String> movies = Lists.newArrayList();
movies.add("Hello!");
[...]
String movie = movies.get(0); 别忘了你可以在方法的参数和返回值中使用泛型: // DON'T
List sort(List input) {
[...]
}
// DO
<T> List<T> sort(List<T> input) {
[...]
} 为获得更佳的灵活性,你可以使用有界通配符(bounded wildcards)来扩展可接受类型的范围。 // Read stuff from collection - use "extends"
void readList(List<? extends Movie> movieList) {
for (Movie movie : movieList) {
System.out.print(movie.getTitle());
[...]
}
}
// Write stuff to collection - use "super"
void writeList(List<? super Movie> movieList) {
movieList.add(Movie.create("Se7en"));
[...]
} 返回空值当必须返回空的 list/collection 时避免使用 null。返回空的集合会使接口变简单(不需要为空返回的函数写文档或注释)并可以避免随机的 NPE。建议返回同一个空集合而不是重新创建一个。 List<Movie> latestMovies() {
if (db.query().isEmpty()) {
return Collections.emptyList();
}
[...]
} 不要使用 + String 操作 要连接几个字符串时,+ 操作也许可行。但不要将其用于大量字符串的连接,这样性能很糟糕。建议用 StringBuilder 替代。 String latestMovieOneLiner(List<Movie> movies) {
StringBuilder sb = new StringBuilder();
for (Movie movie : movies) {
sb.append(movie);
}
return sb.toString();
} 可恢复的异常 我不赞成抛出指示错误的异常,但如果你这样做了,请确保异常可受检查且可恢复: List<Movie> latestMovies() throws MoviesNotFoundException {
if (db.query().isEmpty()) {
throw new MoviesNotFoundException();
}
[...]
} 结论这个列表并没有罗列书中的所有建议,也不是对书中某个建议的深度解析,而仅仅是对部分有用建议的摘录而已。 |