从Effective Java总结一些有助安卓开发的建议

移动开发 Android 后端
本文关注于《Effective Java》这本书中我认为对于 Android 开发至关重要的知识点。对于阅读过这本书的人来说,本文可以作为书中提到的原则/知识的回顾。对于那些(目前)还没有读过的人来说,本文可以给他们一个机会去尝试下这本书。

从Effective Java总结一些有助安卓开发的建议

在编写长期运行下既易于维护又能保持高效的 Java 代码这方面 ,《Effective Java》 被很多人看做最重要的书之一。 Android 使用的是 Java 语言,这就意味着这本书中所有给出的建议一定都是适用与 Android 的,对吗?答案是:不一定。

有些人认为这本书给出的大多数建议都不适用于 Android 开发。在我看来,也并非如此。我认为这本书中有一部分是不适用的,因为不是所有的 Java 特性都是为了用于 Android 而优化的(比如枚举,序列化等等),也因为移动设备存在自身的限制(比如 Dalvik/ART 表现和 JVM 不同)。尽管如此,这本书中的大多数范例还是能够不加修改或者少量修改地被使用,并能够有助于建立一个更加健康、干净和可维护的代码库。

本文关注于这本书中我认为对于 Android 开发至关重要的知识点。对于阅读过这本书的人来说,本文可以作为书中提到的原则/知识的回顾。对于那些(目前)还没有读过的人来说,本文可以给他们一个机会去尝试下这本书。

强制不可实例化

如果你不希望使用 new 关键字来创建一个对象,那就强制使用私有构造器(private constructor)。这对于仅包含静态方法的工具类来说更加有用。

  1. List<Movie> latestMovies() { 
  2.     if (db.query().isEmpty()) { 
  3.         return Collections.emptyList(); 
  4.     } 
  5.     [...] 
  6.  

静态工厂

不要使用 _new_ 关键字和构造器,使用静态工厂方法(以及一个私有的构造器)。这些工厂方法是命名的,不需要每次都返回一个新的对象实例,而且可以根据需要返回不同的子类型。

  1. class Movie { 
  2.     [...] 
  3.     public static Movie create(String title) { 
  4.         return new Movie(title); 
  5.     } 
  6.  

【更新】读者 @ stsdema28 提出了一个有用的建议:使用静态工厂会使得测试变得困难。如果这样的话,不妨考虑使用一个非静态的工厂用于在测试时进行模拟(或者一个能够实现的工厂接口)。

Builders

当你有需要三个以上的构造参数时,使用 Builder 去构造这个对象。写起来可能有点啰嗦但是这样伸缩性和可读性都很好。如果你创建的是值类型,考虑 AutoValue。

  1. class Movie { 
  2.     static Builder newBuilder() { 
  3.         return new Builder(); 
  4.     } 
  5.     static class Builder { 
  6.         String title; 
  7.         Builder withTitle(String title) { 
  8.             this.title = title; 
  9.             return this; 
  10.         } 
  11.         Movie build() { 
  12.             return new Movie(title); 
  13.         } 
  14.     } 
  15.   
  16.     private Movie(String title) { 
  17.     [...]     
  18.     } 
  19. // Use like this: 
  20. Movie matrix = Movie.newBuilder().withTitle("The Matrix").build();  

避免可变性

不可变对象在其整个生命周期中都保持不变。所有需要的数据都在对象创建时提供。这种方式有着多种优点,如简单,线程安全以及可共享。

  1. class Movie { 
  2.     [...] 
  3.     Movie sequel() { 
  4.         return Movie.create(this.title + " 2"); 
  5.     } 
  6. // Use like this: 
  7. Movie toyStory = Movie.create("Toy Story"); 
  8. Movie toyStory2 = toyStory.sequel();  

或许很难做到每个类都不可变。对于这种情况,尽可能使你的类变得不可变(比如使用 private final 字段,final 类声明)。在移动设备上创建对象代价更加昂贵,因此不要过度使用。

静态成员类

如果你定义了一个不依赖于外部类的内部类,别忘了将其定义为静态的。不这么做的话会导致每个内部类的实例都会拥有对外部类的引用。

  1. class Movie { 
  2.     [...] 
  3.     static class MovieAward { 
  4.         [...] 
  5.     } 
  6.  

泛型(几乎)无处不在

Java 提供了类型安全的特性,我们应对此心怀感激(看看JS吧)。尽量避免使用原始类型 (raw types)或者对象类型。泛型大多数情况下都提供了让你的代码在编译时类型安全的机制。

  1. // DON'T 
  2. List movies = Lists.newArrayList(); 
  3. movies.add("Hello!"); 
  4. [...] 
  5. String movie = (String) movies.get(0); 
  6.   
  7. // DO 
  8. List<String> movies = Lists.newArrayList(); 
  9. movies.add("Hello!"); 
  10. [...] 
  11. String movie = movies.get(0);  

别忘了你能在函数的参数和返回值中使用泛型。

  1. // DON'T 
  2. List sort(List input) { 
  3.     [...] 
  4.   
  5. // DO 
  6. <T> List<T> sort(List<T> input) { 
  7.     [...] 
  8.  

为了更加灵活你可以使用 bounded wildcards 来拓展可接受的类型的范围。

  1. // Read stuff from collection - use "extends" 
  2. void readList(List<? extends Movie> movieList) { 
  3.     for (Movie movie : movieList) { 
  4.         System.out.print(movie.getTitle()); 
  5.         [...] 
  6.     } 
  7.   
  8. // Write stuff to collection - use "super" 
  9. void writeList(List<? super Movie> movieList) { 
  10.     movieList.add(Movie.create("Se7en")); 
  11.     [...] 
  12.  

返回空(列表/集合)

当必须返回空的列表/集合时,避免使用null。返回一个空的集合会产生一个更简单的接口(不需要去进行文档注释并声明函数返回值为 null),还能避免意外的空指针异常。***返回一个相同的空集合而不是创建一个新的。

  1. List<Movie> latestMovies() { 
  2.     if (db.query().isEmpty()) { 
  3.         return Collections.emptyList(); 
  4.     } 
  5.     [...] 
  6.  

不要用 + 连接 String

如果要连接几个字符串,+ 操作符或许可以。但永远不要使用它来进行大量的字符串连接,那样性能会十分糟糕。***使用 StringBuilder 来代替。

  1. String latestMovieOneLiner(List<Movie> movies) { 
  2.     StringBuilder sb = new StringBuilder(); 
  3.     for (Movie movie : movies) { 
  4.         sb.append(movie); 
  5.     } 
  6.     return sb.toString(); 
  7.  

可恢复的异常

我不喜欢通过抛出异常来指明错误,但如果你这样做的话,就要确保异常被检查并且异常的捕获者能够从错误中恢复。

  1. List<Movie> latestMovies() throws MoviesNotFoundException { 
  2.     if (db.query().isEmpty()) { 
  3.         throw new MoviesNotFoundException(); 
  4.     } 
  5.     [...] 
  6.  

总结

列举的这些不是这本书中所给出的完整建议,也不是简短说明深入评价。只是这些有用的建议的一纸小抄而已 :)。 

责任编辑:庞桂玉 来源: 安卓开发精选
相关推荐

2009-06-22 15:36:00

如何学好java

2015-12-08 09:05:41

Java内部类

2013-12-03 10:30:28

iOS开发程序员自我提升

2009-07-09 13:16:54

Java Swing开

2021-08-08 10:44:33

安卓系统开发者手机厂商

2024-01-30 10:11:00

SpringBoot项目开发

2017-11-29 18:52:13

Python新手编码建议

2020-09-21 06:58:56

TS 代码建议

2018-11-20 14:24:46

数据分析数据库统计

2012-11-09 10:46:24

Canonical

2010-06-13 15:18:02

UML实践

2022-05-17 16:56:33

开发工具前端

2011-07-12 09:47:53

WebService

2011-11-28 15:57:26

MySQL数据库主从配置

2009-09-27 11:09:42

API设计

2015-08-26 09:31:26

程序员建议

2023-11-10 08:48:09

Lombok库Java8

2009-07-15 16:16:22

JDBC下载

2011-04-27 09:21:09

程序员

2015-08-26 08:31:35

核心程序员成长
点赞
收藏

51CTO技术栈公众号