开源中国

我们不支持 IE 10 及以下版本浏览器

It appears you’re using an unsupported browser

为了获得更好的浏览体验,我们强烈建议您使用较新版本的 Chrome、 Firefox、 Safari 等,或者升级到最新版本的IE浏览器。 如果您使用的是 IE 11 或以上版本,请关闭“兼容性视图”。
Java 的最佳实践 - 技术翻译 - 开源中国社区

Java 的最佳实践 【已翻译100%】

英文原文:Better Java
标签: Java
金河 推荐于 2年前 (共 25 段, 翻译完成于 09-23) 评论 68
收藏  
679
推荐标签: Java 待读

Java 是在世界各地最流行的编程语言之一, 但是看起来没人喜欢使用它。而 Java 事实上还算是一门不错的语言,随着 Java 8 最近的问世,我决定编制一个库,实践和工具的清单,汇集 Java 的一些最佳实践。

本文被放到了 Github 上。你可以随意地提交贡献,并加入自己的有关 Java 方面的建议和最佳实践。

  • 风格

    •  Javadoc

    • 构建器模式

    • 结构

    •  依赖注入

    • 避免空值

    • 默认不可变更

    • 避免大量的工具类

    •  格式化

    •  流

  • 发布

    •   依赖收敛

    •   框架

    •   Maven

    •   持续集成

    •   Maven 资源库

    •   配置管理

    •   jUnit 4

    •   jMock

    •   AssertJ

    •   Apache Commons

    •   Guava

    •   Gson

    •   Java Tuples

    •   Joda-Time

    •   Lombok

    •   Play framework

    •   SLF4J

    •   jOOQ

    •  Missing Features

    •   Testing

  • 工具

    •   Chronon

    •   IntelliJ IDEA

    •   JRebel

    •   Checker 框架

    •   Eclipse 内存分析器

  •   资源

    • 书籍

    • 播客

leoxu
 翻译得不错哦!

风格

通常,我们会以一种非常详细繁杂的企业级 JavaBean 的风格进行 Java 代码的编写。新的风格则更加清晰,正确,且看上去也更加的简单。

结构

作为程序员的我们要做的最简单的事情之一,就是传递数据。一般的方式就是定义一个 JavaBean:

public class DataHolder {
    private String data;

    public DataHolder() {
    }

    public void setData(String data) {
        this.data = data;
    }

    public String getData() {
        return this.data;
    }}

这有点麻烦,并且也有点浪费。尽管你的 IDE 也能自动的生成这样的代码,但那也是种浪费。所以,别这么做

相反,我更愿意选择编写类 C 的结构体风格的类,类里面只容纳数据:

public class DataHolder {
    public final String data;

    public DataHolder(String data) {
        this.data = data;
    }}

这样就在代码行数上减少了一半。此外,这个类是不能被修改的,除非你对它进行了扩展,因此我们可以更加容易的理解它,因为我们明白它不可以被修改。

如果你要保存像 Map 或者 List 这样容易被修改的对象,就应该使用 ImmutableMap 和 ImmutableList,这一点会在不可变性质的那一节被讲到。

leoxu
 翻译得不错哦!

Builder 模式

如果你有一个相当复杂的对象想要去为其构建一个结构,可以考虑使用 Builder 模式。

你可以在对象中创建一个能帮助你构建出这个对象的子类。它使用了可变语句,但是一旦你调用了build,它就会提供给你一个不可变的对象。

想象一下我们要有一个更加复杂的 DataHolder。针对它的构建器看起来可能像是下面这样:

public class ComplicatedDataHolder {
    public final String data;
    public final int num;
    // lots more fields and a constructor

    public static class Builder {
        private String data;
        private int num;

        public Builder data(String data) {
            this.data = data;
            return this;
        }

        public Builder num(int num) {
            this.num = num;
            return this;
        }

        public ComplicatedDataHolder build() {
            return new ComplicatedDataHolder(data, num); // etc
        }  
    }}

然后这样去使用它:

final ComplicatedDataHolder cdh = new ComplicatedDataHolder.Builder()
    .data("set this")
    .num(523)
    .build();

还有其它关于构建器的更好的例子 ,而这里提供给你浅尝辄止。这样做最终会得到许多的我们努力去避免的样板式代码,不过这也让你得到了不可变的对象和一个非常流畅的接口。

leoxu
 翻译得不错哦!

依赖注入

这是更偏向软件工程而不是 Java 的一节。编写可测试软件的最佳方式之一就是使用依赖注入(DI)。因为 Java 非常鼓励 OO 设计,为了创造出可测试的软件,你需要使用DI。

在 Java 中,一般使用 Spring 框架 的 DI 实现。它同时支持基于代码的装配和基于 XML 配置的装配。 如果你使用的是 XML 配置,因为其基于 XML 的配置, 不去过分使用 Spring 这一点很重要。XML 中绝对不能有任何逻辑或者控制结构,只能用来注入依赖。

使用 Spring 的好的选择就是 Google 和 Square 的 Dagger 库以及Google 的 Guice。他们不使用 Spring 的 XML 配置文件格式,而是将依赖逻辑放到注解和代码中。

leoxu
 翻译得不错哦!

避免空值

尽你所能避免空值。如果你可以返回一个空的集合,就不要返回一个空值。如果你要使用空值,就考虑使用 @Nullable 注解。IntelliJ IDEA 内置有对于 @Nullable 注解的支持。

如果你使用的是 Java 8,就可以利用其优秀的新的 Optional 类型。如果一个可能存在也可能不存在,那就像下面这样把它封装到一个 Optional 类中:

public class FooWidget {
    private final String data;
    private final Optional<Bar> bar;

    public FooWidget(String data) {
        this(data, Optional.empty());
    }

    public FooWidget(String data, Optional<Bar> bar) {
        this.data = data;
        this.bar = bar;
    }

    public Optional<Bar> getBar() {
        return bar;
    }}

这样现在就能很确定数据永远都不会是空值了, 不过 bar 可能存在也可能不存在。Optional 有一些诸如 isPresent 这样的方法,这使得其感觉跟只检查空值的做法小同大异。但是它能让你写出像下面这样的语句:

final Optional<FooWidget> fooWidget = maybeGetFooWidget();
final Baz baz = fooWidget.flatMap(FooWidget::getBar)
                         .flatMap(BarWidget::getBaz)
                         .orElse(defaultBaz);

这样就比链条时的 if 空值检查看起来好多了。使用 Optional 的唯一缺陷就是标准库并没有对 Optional 有很好的支持,因此针对空值的处理还是需要的。

leoxu
 翻译得不错哦!

默认不可被改变

除非你有一个好的理由要这样做,那么变量、类和集合都是不应该被修改的。

变量的引用可以用 final 来变成不可被修改的:

final FooWidget fooWidget;if (condition()) {
    fooWidget = getWidget();} else {
    try {
        fooWidget = cachedFooWidget.get();
    } catch (CachingException e) {
        log.error("Couldn't get cached value", e);
        throw e;
    }}// fooWidget is guaranteed to be set here

现在你就可以确信 fooWidget 不会突然被重新赋值了。final 关键字一般同 if/else 块和 try/catch 块一起使用。当然,如果 fooWidget 不是不可被修改的,那你就可以很轻易了修改它了。

集合就应该无论何时都尽量使用 Guava 的 ImmutableMapImmutableList,或者 ImmutableSet 类。这些都拥有构建器,因此你可以动态地构建它们,并通过调用 build 方法来将它们标记为不可变。

类应该(通过 final)声明其属性域不可变和使用不可变的集合而变成不可变的。你也可以选择使得类自身为 final,那样它就不能被扩展和被改变了。

leoxu
 翻译得不错哦!

避免许多的工具类

在你发现自己添加了太多的方法到一个工具类中时要小心。

public class MiscUtil {
    public static String frobnicateString(String base, int times) {
        // ... etc
    }

    public static void throwIfCondition(boolean condition, String msg) {
        // ... etc
    }}

这些类一开始看起来很吸引人,因为它们里面包含的方法并不真的属于任何一块。所以你就以代码重用的名义将它们扔到了一块儿。

治病比生病更糟糕。将这些类放到原本属于它们的地方,要不如果你必须要有像这么一些方法的话,就考虑使用 Java 8 的接口上的默认方法。然后你就可以将公共方法统统扔到接口中去。而因为他们是接口,你就可以多次实现它们。

public interface Thrower {
    default void throwIfCondition(boolean condition, String msg) {
        // ...
    }

    default void throwAorB(Throwable a, Throwable b, boolean throwA) {
        // ...
    }}

然后每个有需要的类都可以简单的实现这个接口。

leoxu
 翻译得不错哦!

格式化

格式化比起大多数程序员所认为的更加不被重视。那么它是不是同你对于自己技术水平的在意目标一致,还有是不是能有助于其他人的对于代码的解读呢?当然是。但我们也不要浪费一整天加空格来使得 if 的括号能“匹配”。

如果你绝对需要一个代码格式手册,我强烈推荐 Google 的 Java 代码风格 指南。该指南的最佳部分就是编程实践这一节。绝对值得一读.

Javadoc

为你的用户所要面对的代码加注文档是很重要的。而这就意味着要使用示例和对于变量、方法和类的极值描述。

leoxu
 翻译得不错哦!

这样做的必然结果就是对于不需要加注文档的就不要去加注文档. 如果就一个参数代表的是什么你不想多费口舌,因为答案很明显,就不要为其加注文档。样板一样的文档比没有文档更糟糕,因为这对于会思考此处为何要加注的文档的用户而言这会是一种戏弄。


Java 8 有了一个不错的和 lambda 语法。你可以像下面这样编写代码:

final List<String> filtered = list.stream()
    .filter(s -> s.startsWith("s"))
    .map(s -> s.toUpperCase());

而不是再像以前这样写:

final List<String> filtered = Lists.newArrayList();for (String str : list) {
    if (str.startsWith("s") {
        filtered.add(str.toUpperCase());
    }}

这就让你能写出更加流畅的代码,更具可读性。

发布

发布 Java 通常有点棘手。如今有两种主要的 Java 发布方式 : 使用一套框架,或者根据灵活性的本地增量方案。

leoxu
 翻译得不错哦!

框架

因为发布 Java 并不容易,现有的框架可能会有所帮助。最好的两个就是 Dropwizard 和 Spring BootPlay 框架 也可以被考虑也作为这些部署框架的其中之一。

它们全都试图降低让你的代码发布出去的门槛. 它们在你是名Java新手或者希望能快速运行起来时特别有帮助. 单个的JAR部署比复杂的WAR和EAR部署更简单.

不过,它们可能不怎么灵活,而且详单笨拙,因此如果你的项目不适合框架开发者为你的框架所做出选择,你就得自己集成一个更加手动的配置了。

leoxu
 翻译得不错哦!
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们
评论(68)
Ctrl/CMD+Enter

挺不错的文章。谢谢翻译者。
面很广,很实用
nice 终于有人做这样的事情了
支持下
Java中概念太多,做起事来啰嗦,像php多简洁

引用来自“肖滔”的评论

Java中概念太多,做起事来啰嗦,像php多简洁
^_^不是一个重量级的嘛

引用来自“肖滔”的评论

Java中概念太多,做起事来啰嗦,像php多简洁

引用来自“Yattin_Vong”的评论

^_^不是一个重量级的嘛
Java在web开发中,已经失败。
每种语言都有自己的存在的理由和范围,说java失败的人,你们真搞过java么?只是这几年来Oracle不断把java推向商业化,不符合开源的准则
为啥没有testng
为什么看完就更不想用java了5

引用来自“肖滔”的评论

Java中概念太多,做起事来啰嗦,像php多简洁

引用来自“Yattin_Vong”的评论

^_^不是一个重量级的嘛

引用来自“肖滔”的评论

Java在web开发中,已经失败。
一味喷语言优越性的人要么是不懂 要么就是最底层的
get set 都是ide自动生成
lz 速度慢了,提前已阅翻译版. http://segmentfault.com/a/1190000003771603
提供了新的思考,学习了。
不知道有生之年是否能用到JAVA8
腻害
J8事实上表示想用都不敢,TMD的你会发现一大堆都不会所谓的拉达姆,流,可选,等那些J8的新特性.

引用来自“益达先生”的评论

不知道有生之年是否能用到JAVA8
不要太悲观,好多公司的正式线上都在用JAVA8了。
说的很不错,这篇文章翻译得很有价值
以前叫人家小甜甜,现在新人胜旧人了叫人家java
顶部