原文地址:這裏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