`
usenrong
  • 浏览: 514936 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Java实战之文章翻译:Better Java —— 教你如何编写现代化的Java程式

 
阅读更多

Java是目前世界上最流行的编程语言之一,但是并不是所有人都乐于使用它。不过Java其实是一个还不错的语言,也别是自从Java 8正式发布之后,所以我决定将我的一些实践、库和工具列举下来以供大家参考。

Style(样式)

传统而言,Java是习惯以冗长的JavaBean方式来定义某个数据对象,新的样式可能会更清晰和保证准确性。

Structs(结构体)

在编程中程序员最常见的操作就是进行数据传递,传统的方式是JavaBean,如下所示:

public class DataHolder {
    private String data;

    public DataHolder() {
    }

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

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

不过这种方式是冗长并且浪费资源的,即便你的编辑器能够自动生成这种代码。作为替代方法,我宁可选择使用C风格里的结构体样式去编写这种容器数据:

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

这种方式几乎可以使得代码的行数减半,除此之外,这种类是不可变的类,所以在某些情况下我们可以放心的使用它。如果你是希望能够用Map或者List这些结构体去存储对象而使得修改变得简单一点,可以使用ImmutableMap或者ImmutableList,这部分会在下面讨论。

Builder模式

如果你的数据元结构比较复杂,可以考虑使用如下的Builder模式。Builder模式在数据类构造器中定义了一个子类,使用可变状态,不过一旦创建之后就会变得不可改变:

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

Dependency injection(依赖注入)

依赖注入更多的从属于软件工程的范畴而不是Java的范畴,但是去撰写可测试的软件的最好的方式就是使用依赖注入。因为Java强烈推荐OO的设计方式,为了让软件具有较好的可测试性,可以去使用依赖注入。

在Java领域,最经典的DI框架当属Spring,它提供了基于代码的注入以及基于XML配置的注入方式。不过Spring确实有点繁琐,如果单纯的只是想使用依赖注入这个功能,可以选择Google 和 Square的 Dagger 库 或者 Google's Guice.

避免空指针

尽可能地避免使用空指针。特别是在可能返回空的集合的情况下务必返回一个内容为空的集合而不是一个null。如果使用的是Java 8 ,可以使用新的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;
    }
}

根据上述代码可以知道,返回的数据肯定不会为null类型,不过bar不一定是present的。

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

Immutable-by-default(默认不可变)

除非有特殊的理由,否则变量、类以及集合应该默认设置为不可变。其中变量类型可以使用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代码块。对于集合类型,应该在任何允许的情况下使用Guava 的ImmutableMap,ImmutableList, 或者 ImmutableSet 类。他们都含有构造器类型,可以使用Builder进行动态构造最终调用build方法设置为不可变。

而对于类,可以通过设置其成员变量为final类型来将其变为不可变类型。另外,也可以将类本身设置为final类型来保证其不可以被扩展或者设置为可变类型。

Avoid lots of Util Classes(避免使用大量工具类)

一不注意,就会发现自己写了N多的Util类,譬如:

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的Google's Java Style ,特别是其中的Programming Practices非常有意义。

JavaDoc

为你的代码添加JavaDoc同样非常重要,可以参考这个示例: using examples

Streams

Java 8提供了非常Nice的Stream API,可以用如下的写法:

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

来替代:

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

这样可以帮助你写更多的高可读性的、流畅的代码。

Deploying(部署)

部分Java代码可能需要一定的技巧性,目前一般来说部署Java主要有两种方式:使用某个框架或者是有一个本地化的可伸缩框架。

Frameworks(框架)

框架是你部署Java代码的一个很好地方式,其中较好的选择有DropwizardSpring Boot。另外Play framework 也是一个不错的选择。

Maven

Maven是一个非常优秀的Java编译与依赖管理工具,通过如下方式可以方便的添加Maven依赖项:

<dependencies>
    <dependency>
        <groupId>org.third.party</groupId>
        <artifactId>some-artifact</artifactId>
    </dependency>
</dependencies>

关于Maven的具体使用可以参考笔者的其余文章

Dependence Convergence(依赖收敛)

Java中一个巨大的魅力即在于有大量的第三方类库可供参考,有必要将所有用到的API或者SDK置于Maven最后那个。不过各种类库之间往往也是相互依赖的,譬如:

Foo library depends on Bar library v1.0
Widget library depends on Bar library v0.9

利用Maven dependency convergence plugin,在编译的时候会告警有一个依赖项依赖不同的版本,一般来说,可以用如下方式处理:

1.在dependenceManagement块选择一个特定的版本。

2.在Foo或者Widget依赖项中使用Exclude移除Bar。

Continuous Integration(持续集成)

在大型项目开发中,往往需要一些持续集成工具来不断基于git构建测试版本,其中Jenkins 和Travis-CI 是较常见的选择。另外,在正式的构建之前往往需要使用代码测试工具,Cobertura就是一个非常好用的测试覆盖率校验工具。

Maven Repository

在大型项目开发中,往往会需要一个Repo去存放私人的Jars、Wars以及EARs。Artifactory 与 Nexus都是不错的选择。

Configuration Management(配置管理)

ChefPuppet, 以及 Ansible 都是不错的选择。

Libraries

可能Java最优秀的属性就是它的大量的扩展库,本部分列举了部分常用的扩展库。

Missing Features(遗失的特性)

Apache Commons

The Apache Commons project 包含了一些列常用的库.

Guava

Guava is Google's excellent here's-what-Java-is-missing library.

Gson

Google's Gson library is a simple and fast JSON parsing library. Itworks like this:

final Gson gson = new Gson();
final String json = gson.toJson(fooWidget);

final FooWidget newFooWidget = gson.fromJson(json, FooWidget.class);

It's really easy and a pleasure to work with. The Gson user guidehas many more examples.

Java Tuples

Java的标准库未能提供Tuples相关的数据结构是一个很大的遗憾。幸亏 Java tuples项目填补了这个空白:

Pair<String, Integer> func(String input) {
    // something...
    return Pair.with(stringResult, intResult);
}

Lombok

Lombok 是一个非常有趣的类库,通过注解方式可以允许减少Java存在的冗余代码,譬如以下的常见的Getter/Setter代码的功能:

public class Foo {
    @Getter @Setter private int var;
}

而现在可以这么写:

final Foo foo = new Foo();
foo.setVar(5);

Play framework

Good alternativesJersey or Spark

There are two main camps for doing RESTful web services in Java: JAX-RS and everything else.

JAX-RS is the traditional way. You combine annotations with interfaces andimplementations to form the web service using something like Jersey.What's nice about this is you can easily make clients out of just the interface class.

The Play framework is a radically different take on web services onthe JVM: you have a routes file and then you write the classes referenced inthose routes. It's actually an entire MVC framework, but you caneasily use it for just REST web services.

It's available for both Java and Scala. It suffers slightly from being Scala-first, but it's still good to use in Java.

If you're used to micro-frameworks like Flask in Python, Spark willbe very familiar. It works especially well with Java 8.

SLF4J

There are a lot of Java logging solutions out there. My favorite isSLF4J because it's extremely pluggable and can combine logs from manydifferent logging frameworks at the same time. Have a weird project that usesjava.util.logging, JCL, and log4j? SLF4J is for you.

The two-page manual is pretty much all you'll need to getstarted.

JOOQ

I dislike heavy ORM frameworks because I like SQL. So I wrote a lot ofJDBC templates and it was sort of hard to maintain. jOOQ is amuch better solution.

It lets you write SQL in Java in a type safe way:

// Typesafely execute the SQL statement directly with jOOQ
Result<Record3<String, String, String>> result = 
create.select(BOOK.TITLE, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME)
    .from(BOOK)
    .join(AUTHOR)
    .on(BOOK.AUTHOR_ID.equal(AUTHOR.ID))
    .where(BOOK.PUBLISHED_IN.equal(1948))
    .fetch();

Using this and the DAO pattern, you can make database access a breeze.

Testing

jUnit 4

jUnit needs no introduction. It's the standard tool for unit testingin Java.

But you're probably not using jUnit to its full potential. jUnit supportsparametrized testsrules to stop you from writingso much boilerplate, theories to randomly test certain code,and assumptions.

jMock

If you've done your dependency injection, this is where it pays off: mockingout code which has side effects (like talking to a REST server) and stillasserting behavior of code that calls it.

jMock is the standard mocking tool for Java. It looks like this:

public class FooWidgetTest {
    private Mockery context = new Mockery();

    @Test
    public void basicTest() {
        final FooWidgetDependency dep = context.mock(FooWidgetDependency.class);

        context.checking(new Expectations() {{
            oneOf(dep).call(with(any(String.class)));
            atLeast(0).of(dep).optionalCall();
        }});

        final FooWidget foo = new FooWidget(dep);

        Assert.assertTrue(foo.doThing());
        context.assertIsSatisfied();
    }
}

This sets up a FooWidgetDependency via jMock and then adds expectations. Weexpect that dep's call method will be called once with some String and thatdep's optionalCall method will be called zero or more times.

If you have to set up the same dependency over and over, you should probablyput that in a test fixture and putassertIsSatisfied in an@After fixture.

AssertJ

Do you ever do this with jUnit?

final List<String> result = some.testMethod();
assertEquals(4, result.size());
assertTrue(result.contains("some result"));
assertTrue(result.contains("some other result"));
assertFalse(result.contains("shouldn't be here"));

This is just annoying boilerplate. AssertJ solves this. You cantransform the same code into this:

assertThat(some.testMethod()).hasSize(4)
                             .contains("some result", "some other result")
                             .doesNotContain("shouldn't be here");

This fluent interface makes your tests more readable. What more could you want?

Tools

IntelliJ IDEA

Good alternativesEclipse and Netbeans

The best Java IDE is IntelliJ IDEA. It has a ton of awesomefeatures, and is really the main thing that makes the verbosity of Javabareable. Autocomplete is great, the inspections are top notch, and the refactoringtools are really helpful.

The free community edition is good enough for me, but there are loads of greatfeatures in the Ultimate edition like database tools, Spring Framework supportand Chronon.

Chronon

One of my favorite features of GDB 7 was the ability to travel back in timewhen debugging. This is possible with theChronon IntelliJ pluginwhen you get the Ultimate edition.

You get variable history, step backwards, method history and more. It's alittle strange to use the first time, but it can help debug some reallyintricate bugs, Heisenbugs and the like.

JRebel

Continuous integration is often a goal of software-as-a-service products. Whatif you didn't even need to wait for the build to finish to see code changeslive?

That's what JRebel does. Once you hook up your server to your JRebelclient, you can see changes on your server instantly. It's a huge time savingswhen you want to experiment quickly.

The Checker Framework

Java's type system is pretty weak. It doesn't differentiate between Stringsand Strings that are actually regular expressions, nor does it do anytaint checking. However, the Checker Frameworkdoes this and more.

It uses annotations like @Nullable to check types. You can even define your own annotations to make the static analysis done evenmore powerful.

Eclipse Memory Analyzer

Memory leaks happen, even in Java. Luckily, there are tools for that. The besttool I've used to fix these is the Eclipse Memory Analyzer. It takes aheap dump and lets you find the problem.

There's a few ways to get a heap dump for a JVM process, but I usejmap:

$ jmap -dump:live,format=b,file=heapdump.hprof -F 8152
Attaching to process ID 8152, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 23.25-b01
Dumping heap to heapdump.hprof ...
... snip ...
Heap dump file created

Then you can open the heapdump.hprof file with the Memory Analyzer and seewhat's going on fast.

分享到:
评论

相关推荐

    Head First Java In English

    - **第七章:在对象城生活得更好——规划未来**(Better Living in Objectville: Planning for the Future) - 讨论软件工程的最佳实践。 - 学习如何设计可维护的软件架构。 - 探讨模块化编程的重要性。 - **第八...

    qing-hua-java-study.rar_better_hua_java 清华_java教程

    这些特性让Java更加强大,也更加灵活,有助于开发出高效、现代化的应用程序。 总之,《清华JAVA教程》是一份全面且深度的学习资料,无论你是Java的新手还是希望进一步提升的开发者,都能从中受益。通过系统的学习和...

    [Java] Java 编程面试揭秘 英文版

    t as simple as sitting down and answering questions The technical ...Java coders can prepare themselves for this daunting process and better arm themselves with the knowledge and interviewing skills ...

    Better Faster Lighter Java

    &lt;&lt;Better Faster Lighter Java&gt;&gt;, the book for advanced Java development.

    better-java:编写现代Java的资源

    在Java编程领域,"better-java"通常指的是遵循最佳实践、高效和可维护的方式来编写Java代码。这个主题旨在提升代码质量,提高开发效率,并确保代码的可读性和可扩展性。以下是一些关于如何编写现代Java的详细知识点...

    Better Faster Lighter Java 轻松快速学习java编程

    Java 的开发者正深陷于复杂性的泥沼中而无法自拔。我们正接近一个极限,程序设计师为了撰写程序来支持所选择的结构比解决真正的问题所花的时间更多,但是有必要把 Java 搞的这么困难吗? 不,不是这样的。《轻快的...

    最新Java JDK 8免安装版(Linux 64位)

    1. **Lambda表达式**:Java 8引入了函数式编程的概念,Lambda表达式是最显著的特征之一。它允许开发者以简洁的方式处理集合,简化多线程编程,并在需要时定义匿名函数。 2. **方法引用和构造器引用**:与Lambda...

    Java.Crash.Course.1541019245.epub

    "Java Crash Course" contains a multitude of tips and tricks, examples and exercises you can do to grow your Java programming skills to unprecedented levels. We chose the content of this book carefully...

    Better,Faster,Lighter Java mobi格式

    Writtenfor intermediate to advanced Java developers, Better, Faster,Lighter Java, offers fresh ideas--often unorthodox--to help yourethink the way you work, and techniques and principles you'll useto...

    Core Java Volume I Fundamentals, 11th Edition

    Core Java Volume I–Fundamentals, 1 (11th Edition) By 作者: Cay S. Horstmann ISBN-10 书号: 0135166306 ISBN-13 书号: 9780135166307 Edition 版本: 11 出版日期: 2018-09-06 pages 页数: 928 The #1 Java ...

    Java学习必须看的书籍

    Brian Goetz等人撰写的《Java并发编程实战》是Java并发编程领域的经典之作。随着多核处理器的普及,Java并发编程变得越来越重要。本书深入浅出地讲解了Java并发编程的基础理论和实践技巧,包括但不限于线程安全、锁...

    CSS SECRETS BETTER SOLUTIONS TO EVERYDAY WEB DESIGN PROBLEMS

    本书《CSS SECRETS: BETTER SOLUTIONS TO EVERYDAY WEB DESIGN PROBLEMS》由Lea Verou撰写,是一本面向新一代CSS的著作。作者不仅是设计师,同时也是前端开发者,她在CSS界有着丰富的经验和权威地位。本书不仅仅停留...

    Better And Better 16.47——【一款超强的触摸板、鼠标、键盘快捷手势+截图+剪切板管理+自动输入法等功能,装机必备】

    Better And Better mac版是一款集合众多优秀功能的Mac手势神器,可以帮助用户自定义的设置mac触控板所支持的手势操作,并且还支持单个应用的手势操作。BetterAndBetter Mac版除了手势快捷设定和操作以外,还兼有显示...

    Big Java, 3nd

    Description: No one brews up a better Java guide than Cay Horstmann and in this Third Edition of Big Java he's perfected his recipe. Thoroughly updated to include Java 6, the Third Edition of ...

Global site tag (gtag.js) - Google Analytics