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 内存分析器

  资源

    书籍

    播客

风格

通常,我们会以一种非常详细繁杂的企业级 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,这一点会在不可变性质的那一节被讲到。

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();

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

依赖注入

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

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

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

避免空值

尽你所能避免空值。如果你可以返回一个空的集合,就不要返回一个空值。如果你要使用空值,就考虑使用 @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 有很好的支持,因此针对空值的处理还是需要的。

默认不可被改变

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

变量的引用可以用 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 的 ImmutableMap,ImmutableList,或者 ImmutableSet 类。这些都拥有构建器,因此你可以动态地构建它们,并通过调用 build 方法来将它们标记为不可变。

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

避免许多的工具类

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

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) {
        // ...
    }}

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

格式化

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

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

Javadoc

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

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

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 发布方式 : 使用一套框架,或者根据灵活性的本地增量方案。

框架

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

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

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

文章转载自 开源中国社区[https://www.oschina.net]

时间: 2024-11-05 07:25:27

Java 的最佳实践的相关文章

最重要的Java EE最佳实践

引言 在过去的几乎整整十年中,人们编写了很多有关 Java Platform, Enterprise Edition (Java EE) 最佳实践的内容.现在有十多本书籍 和数以百计(可能更多)的文章,提供了关于应该如何编写 Java EE 应用程序的 见解.事实上,这方面的参考资料如此之多,并且这些参考资料之间往往还存在 着一些矛盾的建议,以至于在这些混杂的内容中进行学习本身也成为了采用 Java EE 的障碍.因此,为了给刚进入这个领域的客户提供一些简单的指导,我们汇编 了这个最重要的最佳实

Android异常处理最佳实践_Android

一个好的app 异常处理机制 我认为应该至少包含以下几个功能: 1.能把错误信息上传到服务器  让开发者可以持续改进app 2.错误信息至少应该包含 是否在主进程 是否在主线程 等可以帮助程序员定位的信息 3.最好包含手机硬件及软件信息. 4.主进程引发的异常 最好交由系统自己处理 也就是让用户可以感知到 那种(当然你也可以自己定义一套更有意思的感知系统对话框等,具体可参考各种有意思的404界面) 5.子进程引发的异常最好别让用户感知到.比如push之类的 这种 和用户感知弱关联的这种.最好发生

避免Java应用中NullPointerException的技巧和最佳实践

Java应用中抛出的空指针异常是解决空指针的最好方式,也是写出能顺利工作的健壮程序的关键.俗话说"预防胜于治疗",对于这么令人讨厌的空指针异常,这句话也是成立的.值得庆幸的是运用一些防御性的编码技巧,跟踪应用中多个部分之间的联系,你可以将Java中的空指针异常控制在一个很好的水平上.顺便说一句,这是Javarevisited上的第二个空指针异常的帖子.在上个帖子中我们讨论了Java中导致空指针异常的常见原因,而在本教程中我们将会学习一些Java的编程技巧和最佳实践.这些技巧可以帮助你避

Java中关于异常处理的10个最佳实践

Java 编程中异常处理的最佳实践 这里是我收集的10个Java编程中进行异常处理的10最佳实践.在Java编程中对于检查异常有褒有贬,强制处理异常是一门语言的功能.在本文中,我们将尽量减少使用检查型异常,同时学会在Java编程中使用检查型VS非检查型异常. 1.为可恢复的错误使用检查型异常,为编程错误使用非检查型错误 选择检查型还是非检查型异常,对于Java编程人员来说,总是让人感到困惑.检查型异常保证你对错误条件提供异常处理代码,这是一种从语言到强制你编写健壮的代码的一种方式,但同时会引入大

【译】11条Java异常处理的最佳实践

本文翻译自Top 11 Java Exception Best Practices 在之前关于Java异常的文章中,已经探讨过suppressed exceptions和Java Exceptions Tutorial两个方面的内容.要想在实际项目中正确处理Java异常,你应该熟练掌握一些Java异常处理的最佳实践. Java 异常处理的最佳实践 不要 在catch语句块中压制异常 public class ExceptionExample { public FileInputStream te

深入理解Java虚拟机:JVM高级特性与最佳实践

目 录 [ - ] <深入理解Java虚拟机:JVM高级特性与最佳实践>前言 <深入理解Java虚拟机:JVM高级特性与最佳实践>内容特色 <深入理解Java虚拟机:JVM高级特性与最佳实践>目录 第1章 走近Java 1.1 概述 1.2 Java技术体系 1.3 Java发展史 1.4 展望Java技术的未来 1.4.1 模块化 1.4.2 混合语言 1.4.3 多核并行 1.4.4 进一步丰富语法 1.4.5 64位虚拟机 1.5 实战:自己编译JDK 1.5.1

Java程序优化的一些最佳实践

摘要:本文介绍了Java代码优化的过程,总结了优化Java程序的一些最佳实践,分析了进行优化的方法并解释了性能提升的原因.多角度分析导致性能低的原因并逐个进行优化使得程序性能得到极大提升,代码可读性.可扩展性更强. 作者通过经历的一个项目实例,介绍Java代码优化的过程,总结了优化Java程序的一些最佳实践,分析了进行优化的方法,并解释了性能提升的原因.作者从多个角度分析导致性能低的原因,并逐个进行优化,最终使得程序的性能得到极大提升,增强了代码的可读性.可扩展性. 一.衡量程序的标准衡量一个程

介绍在Java程序中记录日志的最佳实践

本文介绍了在Java程序中记录日志的最佳实践,同时也介绍了如何使用开源软件对日志进行聚合和分析.对于现在的应用程序来说,日志的重要性是不言而喻的.很难想象没有任何日志记录功能的应用程序运行在生产环境中.日志所能提供的功能是多种多样的,包括记录程序运行时产生的错误信息.状态信息.调试信息和执行http://www.aliyun.com/zixun/aggregation/18195.html">时间信息等.在生产环境中,日志是 查找问题来源的重要依据.应用程序运行时的产生的各种信息,都应该通

Java字符串拼接效率分析及最佳实践

本文来源于问题 Java字符串连接最佳实践? java连接字符串有多种方式,比如+操作符,StringBuilder.append方法,这些方法各有什么优劣(可以适当说明各种方式的实现细节)? 按照高效的原则,那么java中字符串连接的最佳实践是什么? 有关字符串处理,都有哪些其他的最佳实践? 废话不多说,直接开始, 环境如下: JDK版本: 1.8.0_65 CPU: i7 4790 内存: 16G 直接使用+拼接 看下面的代码: @Test      public void test() {