原文地址:这里html
Java是目前世界上最流行的编程语言之一,可是并非全部人都乐于使用它。不过Java实际上是一个还不错的语言,也别是自从Java 8正式发布以后,因此我决定将个人一些实践、库和工具列举下来以供你们参考。java
传统而言,Java是习惯以冗长的JavaBean方式来定义某个数据对象,新的样式可能会更清晰和保证准确性。git
在编程中程序员最多见的操做就是进行数据传递,传统的方式是JavaBean,以下所示:程序员
public class DataHolder { private String data; public DataHolder() { } public void setData(String data) { this.data = data; } public String getData() { return this.data; } }
不过这种方式是冗长而且浪费资源的,即使你的编辑器可以自动生成这种代码。做为替代方法,我宁肯选择使用C风格里的结构体样式去编写这种容器数据:github
public class DataHolder { public final String data; public DataHolder(String data) { this.data = data; } }
这种方式几乎可使得代码的行数减半,除此以外,这种类是不可变的类,因此在某些状况下咱们能够放心的使用它。若是你是但愿可以用Map或者List这些结构体去存储对象而使得修改变得简单一点,可使用ImmutableMap或者ImmutableList,这部分会在下面讨论。web
若是你的数据元结构比较复杂,能够考虑使用以下的Builder模式。Builder模式在数据类构造器中定义了一个子类,使用可变状态,不过一旦建立以后就会变得不可改变:spring
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 } } }
而后能够按照以下去使用:express
final ComplicatedDataHolder cdh = new ComplicatedDataHolder.Builder() .data("set this") .num(523) .build();
依赖注入更多的从属于软件工程的范畴而不是Java的范畴,可是去撰写可测试的软件的最好的方式就是使用依赖注入。由于Java强烈推荐OO的设计方式,为了让软件具备较好的可测试性,能够去使用依赖注入。apache
在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);
除非有特殊的理由,不然变量、类以及集合应该默认设置为不可变。其中变量类型可使用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类型来保证其不能够被扩展或者设置为可变类型。
一不注意,就会发现本身写了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一样很是重要,能够参考这个示例: using examples
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()); } }
这样能够帮助你写更多的高可读性的、流畅的代码。
部分Java代码可能须要必定的技巧性,目前通常来讲部署Java主要有两种方式:使用某个框架或者是有一个本地化的可伸缩框架。
框架是你部署Java代码的一个很好地方式,其中较好的选择有Dropwizard与Spring Boot。另外Play framework 也是一个不错的选择。
Maven是一个很是优秀的Java编译与依赖管理工具,经过以下方式能够方便的添加Maven依赖项:
<dependencies> <dependency> <groupId>org.third.party</groupId> <artifactId>some-artifact</artifactId> </dependency> </dependencies>
关于Maven的具体使用能够参考笔者的其他文章
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。
在大型项目开发中,每每须要一些持续集成工具来不断基于git构建测试版本,其中Jenkins 和Travis-CI 是较常见的选择。另外,在正式的构建以前每每须要使用代码测试工具,Cobertura就是一个很是好用的测试覆盖率校验工具。
在大型项目开发中,每每会须要一个Repo去存放私人的Jars、Wars以及EARs。Artifactory 与 Nexus都是不错的选择。
Chef, Puppet, 以及 Ansible 都是不错的选择。
可能Java最优秀的属性就是它的大量的扩展库,本部分列举了部分经常使用的扩展库。
The Apache Commons project 包含了一些列经常使用的库.
Commons Codec 包含了大量有用的编码与解码的方法。
Commons Lang 包含了大量的字符串处理以及字符编码相关的方法。
Commons IO 包含了大量与文件相关的操做。 It has FileUtils.copyDirectory, FileUtils.writeStringToFile,IOUtils.readLines and much more.
Guava is Google's excellent here's-what-Java-is-missing library.
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项目填补了这个空白:
Pair<String, Integer> func(String input) { // something... return Pair.with(stringResult, intResult); }
Lombok 是一个很是有趣的类库,经过注解方式能够容许减小Java存在的冗余代码,譬如如下的常见的Getter/Setter代码的功能:
public class Foo { @Getter @Setter private int var; }
而如今能够这么写:
final Foo foo = new Foo(); foo.setVar(5);
Good alternatives: Jersey 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.
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.
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.
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 tests, rules to stop you from writingso much boilerplate, theories to randomly test certain code,and assumptions.
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 put assertIsSatisfied in an@After fixture.
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?
Good alternatives: Eclipse 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.
One of my favorite features of GDB 7 was the ability to travel back in timewhen debugging. This is possible with the Chronon 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.
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.
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.
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.
[Java Concurrency in Practice