Java 的最佳实践 已翻译 100%

金河 投递于 2015/09/22 15:17 (共 25 段, 翻译完成于 09-23)
阅读 23649
收藏 676
61
加载中

Java is one of the most popular programming languages around, but no one seems to enjoy using it. Well, Java is actually an alright programming language, and since Java 8 came out recently, I decided to compile a list of libraries, practices, and tools to make using Java better.

This article is on Github. Feel free to contribute and add your own Java tips and best practices.

  • Style

    • Javadoc

    • The Builder Pattern

    • Structs

    • Dependency injection

    • Avoid Nulls

    • Immutable-by-default

    • Avoid lots of Util classes

    • Formatting

    • Streams

  • Deploying

    • Dependency Convergence

    • Frameworks

    • Maven

    • Continuous Integration

    • Maven repository

    • Configuration management

  • Libraries

    • jUnit 4

    • jMock

    • AssertJ

    • Apache Commons

    • Guava

    • Gson

    • Java Tuples

    • Joda-Time

    • Lombok

    • Play framework

    • SLF4J

    • jOOQ

    • Missing Features

    • Testing

  • Tools

    • Chronon

    • IntelliJ IDEA

    • JRebel

    • The Checker Framework

    • Eclipse Memory Analyzer

  • Resources

    • Books

    • Podcasts

已有 1 人翻译此段
我来翻译

Style

Traditionally, Java was programmed in a very verbose enterprise JavaBean style. The new style is much cleaner, more correct, and easier on the eyes.

Structs

One of the simplest things we as programmers do is pass around data. The traditional way to do this is to define a JavaBean:

public class DataHolder {
    private String data;

    public DataHolder() {
    }

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

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

This is verbose and wasteful. Even if your IDE automatically generated this code, it's a waste. So, don't do this.

Instead, I prefer the C struct style of writing classes that merely hold data:

public class DataHolder {
    public final String data;

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

This is a reduction in number of lines of code by a half. Further, this class is immutable unless you extend it, so we can reason about it easier as we know that it can't be changed.

If you're storing objects like Map or List that can be modified easily, you should instead use ImmutableMap or ImmutableList, which is discussed in the section about immutability.

已有 1 人翻译此段
我来翻译

The Builder Pattern

If you have a rather complicated object that you want to build a struct for, consider the Builder pattern.

You make a subclass in your object which will construct your object. It uses mutable state, but as soon as you call build, it will emit an immutable object.

Imagine we had a more complicated DataHolder. The builder for it might look like:

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
        }  
    }}

Then to use it:

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

There are better examples of Builders elsewhere but this should give you a taste for what it's like. This ends up with a lot of the boilerplate we were trying to avoid, but it gets you immutable objects and a very fluent interface.

已有 1 人翻译此段
我来翻译

Dependency injection

This is more of a software engineering section than a Java section, but one of the best ways to write testable software is to use dependency injection(DI). Because Java strongly encourages OO design, to make testable software, you need to use DI.

In Java, this is typically done with the Spring Framework. It has a either code-based wiring or XML configuration-based wiring. If you use the XML configuration, it's important that you don't overuse Spring because of its XML-based configuration format. There should be absolutely no logic or control structures in XML. It should only inject dependencies.

Good alternatives to using Spring is Google and Square's Daggerlibrary or Google's Guice. They don't use Spring's XML configuration file format, and instead they put the injection logic in annotations and in code.

已有 1 人翻译此段
我来翻译

Avoid Nulls

Try to avoid using nulls when you can. Do not return null collections when you should have instead returned an empty collection. If you're going to use null, consider the @Nullable annotation. IntelliJ IDEA has built-in support for the @Nullable annotation.

If you're using Java 8, you can use the excellent new Optional type. If a value may or may not be present, wrap it in an Optional class like this:

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;
    }}

So now it's clear that data will never be null, but bar may or may not be present. Optional has methods like isPresent, which may make it feel like not a lot is different from just checking null. But it allows you to write statements like:

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

Which is much better than chained if null checks. The only downside of using Optional is that the standard library doesn't have good Optional support, so dealing with nulls is still required there.

已有 1 人翻译此段
我来翻译

Immutable-by-default

Unless you have a good reason to make them otherwise, variables, classes, and collections should be immutable.

Variable references can be made immutable with 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

Now you can be sure that fooWidget won't be accidentally reassigned. The finalkeyword works with if/else blocks and with try/catch blocks. Of course, if thefooWidget itself isn't immutable you could easily mutate it.

Collections should, whenever possible, use the Guava ImmutableMap,ImmutableList, or ImmutableSet classes. These have builders so that you can build them up dynamically and then mark them immutable by calling the build method.

Classes should be made immutable by declaring fields immutable (via final) and by using immutable collections. Optionally, you can make the class itself final so that it can't be extended and made mutable.

已有 1 人翻译此段
我来翻译

Avoid lots of Util classes

Be careful if you find yourself adding a lot of methods to a Util class.

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

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

These classes, at first, seem attractive because the methods that go in them don't really belong in any one place. So you throw them all in here in the name of code reuse.

The cure is worse than the disease. Put these classes where they belong, or if you must have common methods like this, consider Java 8's default methods on interfaces. Then you could lump common actions into interfaces. And, since they're interfaces, you can implement multiple of them.

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

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

Then every class which needs it can simply implement this interface.

已有 1 人翻译此段
我来翻译

Formatting

Formatting is so much less important than most programmers make it out to be. Does consistency show that you care about your craft and does it help others read? Absolutely. But let's not waste a day adding spaces to if blocks so that it "matches".

If you absolutely need a code formatting guide, I highly recommendGoogle's Java Style guide. The best part of that guide is theProgramming Practices section. Definitely worth a read.

Javadoc

Documenting your user facing code is important. And this means using examples and using sensible descriptions of variables, methods, and classes.

已有 1 人翻译此段
我来翻译

The corollary of this is to not document what doesn't need documenting. If you don't have anything to say about what an argument is, or if it's obvious, don't document it. Boilerplate documentation is worse than no documentation at all, as it tricks your users into thinking that there is documentation.

Streams

Java 8 has a nice stream and lambda syntax. You could write code like this:

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

Instead of this:

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

This allows you to write more fluent code, which is more readable.

Deploying

Deploying Java properly can be a bit tricky. There are two main ways to deploy Java nowadays: use a framework or use a home grown solution that is more flexible.

已有 1 人翻译此段
我来翻译

Frameworks

Because deploying Java isn't easy, frameworks have been made which can help. Two of the best are Dropwizard and Spring Boot. The Play framework can also be considered one of these deployment frameworks as well.

All of them try to lower the barrier to getting your code out the door. They're especially helpful if you're new to Java or if you need to get things done fast. Single JAR deployments are just easier than complicated WAR or EAR deployments.

However, they can be somewhat inflexible and are rather opinionated, so if your project doesn't fit with the choices the developers of your framework made, you'll have to migrate to a more hand-rolled configuration.

已有 1 人翻译此段
我来翻译
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(68)

多多啦啦梦
多多啦啦梦
不错
程序那些年
程序那些年
mark一下
slliver
slliver
谢谢参与翻译的人员,辛苦
19492049
19492049

引用来自“肖滔”的评论

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

引用来自“Yattin_Vong”的评论

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

引用来自“肖滔”的评论

Java在web开发中,已经失败。

引用来自“virusyang”的评论

一味喷语言优越性的人要么是不懂 要么就是最底层的

引用来自“南湖船老大”的评论

恰恰相反,Java在web开发中相当成功。当然如果PHPER的眼光只是局限在wordpress,dedecms这类网站就算了
php就靠wp撑起来的,浮肿的胖子
草根豆1
支持一下.
honghu991
honghu991
不知该类库有了吗?很期待!!
lobtao2
lobtao2

引用来自“肖滔”的评论

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

引用来自“TuWei”的评论

Php 太随意…代码可读性远没Java 好,再说你凭啥说Java web失败,就因为不能快速建垃圾站吗

引用来自“肖滔”的评论

Java一坨坨的类,做一个功能,一陀代码,错误一坨坨看不清楚,真不知道好在哪

引用来自“TuWei”的评论

我还真不知道php 比java更容易debug,再说了,你不知道java可以直接用jsp写程序吗
Debug.也有断点,单步调试等啊
TuWei
TuWei

引用来自“肖滔”的评论

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

引用来自“TuWei”的评论

Php 太随意…代码可读性远没Java 好,再说你凭啥说Java web失败,就因为不能快速建垃圾站吗

引用来自“肖滔”的评论

Java一坨坨的类,做一个功能,一陀代码,错误一坨坨看不清楚,真不知道好在哪
我还真不知道php 比java更容易debug,再说了,你不知道java可以直接用jsp写程序吗
m
mopdzz
mark
lobtao2
lobtao2

引用来自“肖滔”的评论

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

引用来自“TuWei”的评论

Php 太随意…代码可读性远没Java 好,再说你凭啥说Java web失败,就因为不能快速建垃圾站吗
Java一坨坨的类,做一个功能,一陀代码,错误一坨坨看不清楚,真不知道好在哪
返回顶部
顶部