├── LICENSE ├── README.md └── README.zh-cn.md /LICENSE: -------------------------------------------------------------------------------- 1 | This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *Note*: I'm working on version 2 of this guide and I need your help! Please use [this form to give me feedback](https://goo.gl/forms/yoWihX9ZjPI9x24o1) on what you think should go in the next version. Thanks! 2 | 3 | 4 | # Better Java 5 | 6 | Java is one of the most popular programming languages around, but no one seems 7 | to enjoy using it. Well, Java is actually an alright programming language, and 8 | since Java 8 came out recently, I decided to compile a list of libraries, 9 | practices, and tools to make using Java better. "Better" is subjective, so I 10 | would recommend taking the parts that speak to you and use them, rather than 11 | trying to use all of them at once. Feel free to submit pull requests 12 | suggesting additions. 13 | 14 | This article was originally posted on 15 | [my blog](https://www.seancassidy.me/better-java.html). 16 | 17 | Read this in other languages: [English](README.md), [简体中文](README.zh-cn.md) 18 | 19 | ## Table Of Contents 20 | 21 | * [Style](#style) 22 | * [Structs](#structs) 23 | * [The Builder Pattern](#the-builder-pattern) 24 | * [Immutable Object Generation](#immutable-object-generation) 25 | * [Exceptions](#exceptions) 26 | * [Dependency injection](#dependency-injection) 27 | * [Avoid Nulls](#avoid-nulls) 28 | * [Immutable-by-default](#immutable-by-default) 29 | * [Avoid lots of Util classes](#avoid-lots-of-util-classes) 30 | * [Formatting](#formatting) 31 | * [Javadoc](#javadoc) 32 | * [Streams](#streams) 33 | * [Deploying](#deploying) 34 | * [Frameworks](#frameworks) 35 | * [Maven](#maven) 36 | * [Dependency Convergence](#dependency-convergence) 37 | * [Continuous Integration](#continuous-integration) 38 | * [Maven repository](#maven-repository) 39 | * [Configuration management](#configuration-management) 40 | * [Libraries](#libraries) 41 | * [Missing Features](#missing-features) 42 | * [Apache Commons](#apache-commons) 43 | * [Guava](#guava) 44 | * [Gson](#gson) 45 | * [Java Tuples](#java-tuples) 46 | * [Javaslang](#javaslang) 47 | * [Joda-Time](#joda-time) 48 | * [Lombok](#lombok) 49 | * [Play framework](#play-framework) 50 | * [SLF4J](#slf4j) 51 | * [jOOQ](#jooq) 52 | * [Testing](#testing) 53 | * [jUnit 4](#junit-4) 54 | * [jMock](#jmock) 55 | * [AssertJ](#assertj) 56 | * [Tools](#tools) 57 | * [IntelliJ IDEA](#intellij-idea) 58 | * [Chronon](#chronon) 59 | * [JRebel](#jrebel) 60 | * [The Checker Framework](#the-checker-framework) 61 | * [Code Quality](#code-quality) 62 | * [Eclipse Memory Analyzer](#eclipse-memory-analyzer) 63 | * [Resources](#resources) 64 | * [Books](#books) 65 | * [Podcasts](#podcasts) 66 | * [Videos](#videos) 67 | 68 | ## Style 69 | 70 | Traditionally, Java was programmed in a very verbose enterprise JavaBean style. 71 | The new style is much cleaner, more correct, and easier on the eyes. 72 | 73 | ### Structs 74 | 75 | One of the simplest things we as programmers do is pass around data. The 76 | traditional way to do this is to define a JavaBean: 77 | 78 | ```java 79 | public class DataHolder { 80 | private String data; 81 | 82 | public DataHolder() { 83 | } 84 | 85 | public void setData(String data) { 86 | this.data = data; 87 | } 88 | 89 | public String getData() { 90 | return this.data; 91 | } 92 | } 93 | ``` 94 | 95 | This is verbose and wasteful. Even if your IDE automatically generated this 96 | code, it's a waste. So, [don't do this][dontbean]. 97 | 98 | Instead, I prefer the C struct style of writing classes that merely hold data: 99 | 100 | ```java 101 | public class DataHolder { 102 | public final String data; 103 | 104 | public DataHolder(String data) { 105 | this.data = data; 106 | } 107 | } 108 | ``` 109 | 110 | This is a reduction in number of lines of code by a half. Further, this class 111 | is immutable unless you extend it, so we can reason about it easier as we know 112 | that it can't be changed. 113 | 114 | If you're storing objects like Map or List that can be modified easily, you 115 | should instead use ImmutableMap or ImmutableList, which is discussed in the 116 | section about immutability. 117 | 118 | #### The Builder Pattern 119 | 120 | If you have a rather complicated object that you want to build a struct for, 121 | consider the Builder pattern. 122 | 123 | You make a static inner class which will construct your object. It uses 124 | mutable state, but as soon as you call build, it will emit an immutable 125 | object. 126 | 127 | Imagine we had a more complicated *DataHolder*. The builder for it might look 128 | like: 129 | 130 | ```java 131 | public class ComplicatedDataHolder { 132 | public final String data; 133 | public final int num; 134 | // lots more fields and a constructor 135 | 136 | public static class Builder { 137 | private String data; 138 | private int num; 139 | 140 | public Builder data(String data) { 141 | this.data = data; 142 | return this; 143 | } 144 | 145 | public Builder num(int num) { 146 | this.num = num; 147 | return this; 148 | } 149 | 150 | public ComplicatedDataHolder build() { 151 | return new ComplicatedDataHolder(data, num); // etc 152 | } 153 | } 154 | } 155 | ``` 156 | 157 | Then to use it: 158 | 159 | ```java 160 | final ComplicatedDataHolder cdh = new ComplicatedDataHolder.Builder() 161 | .data("set this") 162 | .num(523) 163 | .build(); 164 | ``` 165 | 166 | There are [better examples of Builders elsewhere][builderex] but this should 167 | give you a taste for what it's like. This ends up with a lot of the boilerplate 168 | we were trying to avoid, but it gets you immutable objects and a very fluent 169 | interface. 170 | 171 | Instead of creating builder objects by hand, consider using one of the many 172 | libraries which can help you generate builders. 173 | 174 | #### Immutable Object Generation 175 | 176 | If you create many immutable objects by hand, consider using the annotation 177 | processor to generate them from interfaces automatically. This minimizes 178 | boilerplate code, reduces probability of bugs and promotes immutability. See 179 | this [presentation](https://docs.google.com/presentation/d/14u_h-lMn7f1rXE1nDiLX0azS3IkgjGl5uxp5jGJ75RE/edit#slide=id.g2a5e9c4a8_00) 180 | for an interesting discussion of some of the problems with normal Java coding 181 | patterns. 182 | 183 | Some great code generation libraries are [immutables](https://github.com/immutables/immutables), Google's 184 | [auto-value](https://github.com/google/auto/tree/master/value) and 185 | [Lombok][lombok]. 186 | 187 | ### Exceptions 188 | 189 | [Checked exceptions][checkedex] should be used with caution, if at all. They 190 | force your users to add many try/catch blocks and wrap your exceptions in their 191 | own. Better is to make your exceptions extend RuntimeException instead. This 192 | allows your users to handle your exceptions in the way they would like, rather 193 | than forcing them to handle/declare that it throws every time, which pollutes 194 | the code. 195 | 196 | One nifty trick is to put RuntimeExceptions in your method's throws declaration. 197 | This has no effect on the compiler, but will inform your users via documentation 198 | that these exceptions can be thrown. 199 | 200 | ### Dependency injection 201 | 202 | This is more of a software engineering section than a Java section, but one of 203 | the best ways to write testable software is to use [dependency injection][di] 204 | (DI). Because Java strongly encourages OO design, to make testable software, 205 | you need to use DI. 206 | 207 | In Java, this is typically done with the [Spring Framework][spring]. It has a 208 | either code-based wiring or XML configuration-based wiring. If you use the XML 209 | configuration, it's important that you [don't overuse Spring][springso] because 210 | of its XML-based configuration format. There should be absolutely no logic or 211 | control structures in XML. It should only inject dependencies. 212 | 213 | Good alternatives to using Spring is Google and Square's [Dagger][dagger] 214 | library or Google's [Guice][guice]. They don't use Spring's XML 215 | configuration file format, and instead they put the injection logic in 216 | annotations and in code. 217 | 218 | ### Avoid Nulls 219 | 220 | Try to avoid using nulls when you can. Do not return null collections when you 221 | should have instead returned an empty collection. If you're going to use null, 222 | consider the [@Nullable][nullable] annotation. [IntelliJ IDEA][intellij] has 223 | built-in support for the @Nullable annotation. 224 | 225 | Read more about why not to use nulls in 226 | [The worst mistake of computer science][the-worst-mistake-of-computer-science]. 227 | 228 | If you're using [Java 8][java8], you can use the excellent new 229 | [Optional][optional] type. If a value may or may not be present, wrap it in 230 | an *Optional* class like this: 231 | 232 | ```java 233 | public class FooWidget { 234 | private final String data; 235 | private final Optional bar; 236 | 237 | public FooWidget(String data) { 238 | this(data, Optional.empty()); 239 | } 240 | 241 | public FooWidget(String data, Optional bar) { 242 | this.data = data; 243 | this.bar = bar; 244 | } 245 | 246 | public Optional getBar() { 247 | return bar; 248 | } 249 | } 250 | ``` 251 | 252 | So now it's clear that *data* will never be null, but *bar* may or may not be 253 | present. *Optional* has methods like *isPresent*, which may make it feel like 254 | not a lot is different from just checking *null*. But it allows you to write 255 | statements like: 256 | 257 | ```java 258 | final Optional fooWidget = maybeGetFooWidget(); 259 | final Baz baz = fooWidget.flatMap(FooWidget::getBar) 260 | .flatMap(BarWidget::getBaz) 261 | .orElse(defaultBaz); 262 | ``` 263 | 264 | Which is much better than chained if null checks. The only downside of using 265 | Optional is that the standard library doesn't have good Optional support, so 266 | dealing with nulls is still required there. 267 | 268 | ### Immutable-by-default 269 | 270 | Unless you have a good reason to make them otherwise, variables, classes, and 271 | collections should be immutable. 272 | 273 | Variables can be made immutable with *final*: 274 | 275 | ```java 276 | final FooWidget fooWidget; 277 | if (condition()) { 278 | fooWidget = getWidget(); 279 | } else { 280 | try { 281 | fooWidget = cachedFooWidget.get(); 282 | } catch (CachingException e) { 283 | log.error("Couldn't get cached value", e); 284 | throw e; 285 | } 286 | } 287 | // fooWidget is guaranteed to be set here 288 | ``` 289 | 290 | Now you can be sure that fooWidget won't be accidentally reassigned. The *final* 291 | keyword works with if/else blocks and with try/catch blocks. Of course, if the 292 | *fooWidget* itself isn't immutable you could easily mutate it. 293 | 294 | Collections should, whenever possible, use the Guava 295 | [ImmutableMap][immutablemap], [ImmutableList][immutablelist], or 296 | [ImmutableSet][immutableset] classes. These have builders so that you can build 297 | them up dynamically and then mark them immutable by calling the build method. 298 | 299 | Classes should be made immutable by declaring fields immutable (via *final*) 300 | and by using immutable collections. Optionally, you can make the class itself 301 | *final* so that it can't be extended and made mutable. 302 | 303 | ### Avoid lots of Util classes 304 | 305 | Be careful if you find yourself adding a lot of methods to a Util class. 306 | 307 | ```java 308 | public class MiscUtil { 309 | public static String frobnicateString(String base, int times) { 310 | // ... etc 311 | } 312 | 313 | public static void throwIfCondition(boolean condition, String msg) { 314 | // ... etc 315 | } 316 | } 317 | ``` 318 | 319 | These classes, at first, seem attractive because the methods that go in them 320 | don't really belong in any one place. So you throw them all in here in the 321 | name of code reuse. 322 | 323 | The cure is worse than the disease. Put these classes where they belong and 324 | refactor aggressively. Don't name classes, packages, or libraries anything 325 | too generic, such as "MiscUtils" or "ExtrasLibrary". This encourages dumping 326 | unrelated code there. 327 | 328 | ### Formatting 329 | 330 | Formatting is so much less important than most programmers make it out to be. 331 | Does consistency show that you care about your craft and does it help others 332 | read? Absolutely. But let's not waste a day adding spaces to if blocks so that 333 | it "matches". 334 | 335 | If you absolutely need a code formatting guide, I highly recommend 336 | [Google's Java Style][googlestyle] guide. The best part of that guide is the 337 | [Programming Practices][googlepractices] section. Definitely worth a read. 338 | 339 | #### Javadoc 340 | 341 | Documenting your user facing code is important. And this means 342 | [using examples][javadocex] and using sensible descriptions of variables, 343 | methods, and classes. 344 | 345 | The corollary of this is to not document what doesn't need documenting. If you 346 | don't have anything to say about what an argument is, or if it's obvious, 347 | don't document it. Boilerplate documentation is worse than no documentation at 348 | all, as it tricks your users into thinking that there is documentation. 349 | 350 | ### Streams 351 | 352 | [Java 8][java8] has a nice [stream][javastream] and lambda syntax. You could 353 | write code like this: 354 | 355 | ```java 356 | final List filtered = list.stream() 357 | .filter(s -> s.startsWith("s")) 358 | .map(s -> s.toUpperCase()) 359 | .collect(Collectors.toList()); 360 | ``` 361 | 362 | Instead of this: 363 | 364 | ```java 365 | final List filtered = new ArrayList<>(); 366 | for (String str : list) { 367 | if (str.startsWith("s") { 368 | filtered.add(str.toUpperCase()); 369 | } 370 | } 371 | ``` 372 | 373 | This allows you to write more fluent code, which is more readable. 374 | 375 | ## Deploying 376 | 377 | Deploying Java properly can be a bit tricky. There are two main ways to deploy 378 | Java nowadays: use a framework or use a home grown solution that is more 379 | flexible. 380 | 381 | ### Frameworks 382 | 383 | Because deploying Java isn't easy, frameworks have been made which can help. 384 | Two of the best are [Dropwizard][dropwizard] and [Spring Boot][springboot]. 385 | The [Play framework][play] can also be considered one of these deployment 386 | frameworks as well. 387 | 388 | All of them try to lower the barrier to getting your code out the door. 389 | They're especially helpful if you're new to Java or if you need to get things 390 | done fast. Single JAR deployments are just easier than complicated WAR or EAR 391 | deployments. 392 | 393 | However, they can be somewhat inflexible and are rather opinionated, so if 394 | your project doesn't fit with the choices the developers of your framework 395 | made, you'll have to migrate to a more hand-rolled configuration. 396 | 397 | ### Maven 398 | 399 | **Good alternative**: [Gradle][gradle]. 400 | 401 | Maven is still the standard tool to build, package, and run your tests. There 402 | are alternatives, like Gradle, but they don't have the same adoption that Maven 403 | has. If you're new to Maven, you should start with 404 | [Maven by Example][mavenexample]. 405 | 406 | I like to have a root POM with all of the external dependencies you want to 407 | use. It will look something [like this][rootpom]. This root POM has only one 408 | external dependency, but if your product is big enough, you'll have dozens. 409 | Your root POM should be a project on its own: in version control and released 410 | like any other Java project. 411 | 412 | If you think that tagging your root POM for every external dependency change 413 | is too much, you haven't wasted a week tracking down cross project dependency 414 | errors. 415 | 416 | All of your Maven projects will include your root POM and all of its version 417 | information. This way, you get your company's selected version of each 418 | external dependency, and all of the correct Maven plugins. If you need to pull 419 | in external dependencies, it works just like this: 420 | 421 | ```xml 422 | 423 | 424 | org.third.party 425 | some-artifact 426 | 427 | 428 | ``` 429 | 430 | If you want internal dependencies, that should be managed by each individual 431 | project's **** section. Otherwise it would be difficult 432 | to keep the root POM version number sane. 433 | 434 | #### Dependency Convergence 435 | 436 | One of the best parts about Java is the massive amount of third party 437 | libraries which do everything. Essentially every API or toolkit has a Java SDK 438 | and it's easy to pull it in with Maven. 439 | 440 | And those Java libraries themselves depend on specific versions of other 441 | libraries. If you pull in enough libraries, you'll get version conflicts, that 442 | is, something like this: 443 | 444 | Foo library depends on Bar library v1.0 445 | Widget library depends on Bar library v0.9 446 | 447 | Which version will get pulled into your project? 448 | 449 | With the [Maven dependency convergence plugin][depconverge], the build will 450 | error if your dependencies don't use the same version. Then, you have two 451 | options for solving the conflict: 452 | 453 | 1. Explicitly pick a version for Bar in your *dependencyManagement* section 454 | 2. Exclude Bar from either Foo or Widget 455 | 456 | The choice of which to choose depends on your situation: if you want to track 457 | one project's version, then exclude makes sense. On the other hand, if you 458 | want to be explicit about it, you can pick a version, although you'll need to 459 | update it when you update the other dependencies. 460 | 461 | ### Continuous Integration 462 | 463 | Obviously you need some kind of continuous integration server which is going 464 | to continuously build your SNAPSHOT versions and tag builds based on git tags. 465 | 466 | [Jenkins][jenkins] and [Travis-CI][travis] are natural choices. 467 | 468 | Code coverage is useful, and [Cobertura][cobertura] has 469 | [a good Maven plugin][coberturamaven] and CI support. There are other code 470 | coverage tools for Java, but I've used Cobertura. 471 | 472 | ### Maven repository 473 | 474 | You need a place to put your JARs, WARs, and EARs that you make, so you'll 475 | need a repository. 476 | 477 | Common choices are [Artifactory][artifactory] and [Nexus][nexus]. Both work, 478 | and have their own [pros and cons][mavenrepo]. 479 | 480 | You should have your own Artifactory/Nexus installation and 481 | [mirror your dependencies][artifactorymirror] onto it. This will stop your 482 | build from breaking because some upstream Maven repository went down. 483 | 484 | ### Configuration management 485 | 486 | So now you've got your code compiled, your repository set up, and you need to 487 | get your code out in your development environment and eventually push it to 488 | production. Don't skimp here, because automating this will pay dividends for a 489 | long time. 490 | 491 | [Chef][chef], [Puppet][puppet], and [Ansible][ansible] are typical choices. 492 | I've written an alternative called [Squadron][squadron], which I, of course, 493 | think you should check out because it's easier to get right than the 494 | alternatives. 495 | 496 | Regardless of what tool you choose, don't forget to automate your deployments. 497 | 498 | ## Libraries 499 | 500 | Probably the best feature about Java is the extensive amount of libraries it 501 | has. This is a small collection of libraries that are likely to be applicable 502 | to the largest group of people. 503 | 504 | ### Missing Features 505 | 506 | Java's standard library, once an amazing step forward, now looks like it's 507 | missing several key features. 508 | 509 | #### Apache Commons 510 | 511 | [The Apache Commons project][apachecommons] has a bunch of useful libraries. 512 | 513 | **Commons Codec** has many useful encoding/decoding methods for Base64 and hex 514 | strings. Don't waste your time rewriting those. 515 | 516 | **Commons Lang** is the go-to library for String manipulation and creation, 517 | character sets, and a bunch of miscellaneous utility methods. 518 | 519 | **Commons IO** has all the File related methods you could ever want. It has 520 | [FileUtils.copyDirectory][copydir], [FileUtils.writeStringToFile][writestring], 521 | [IOUtils.readLines][readlines] and much more. 522 | 523 | #### Guava 524 | 525 | [Guava][guava] is Google's excellent here's-what-Java-is-missing library. It's 526 | almost hard to distill everything that I like about this library, but I'm 527 | going to try. 528 | 529 | **Cache** is a simple way to get an in-memory cache that can be used to cache 530 | network access, disk access, memoize functions, or anything really. Just 531 | implement a [CacheBuilder][cachebuilder] which tells Guava how to build your 532 | cache and you're all set! 533 | 534 | **Immutable** collections. There's a bunch of these: 535 | [ImmutableMap][immutablemap], [ImmutableList][immutablelist], or even 536 | [ImmutableSortedMultiSet][immutablesorted] if that's your style. 537 | 538 | I also like writing mutable collections the Guava way: 539 | 540 | ```java 541 | // Instead of 542 | final Map map = new HashMap<>(); 543 | 544 | // You can use 545 | final Map map = Maps.newHashMap(); 546 | ``` 547 | 548 | There are static classes for [Lists][lists], [Maps][maps], [Sets][sets] and 549 | more. They're cleaner and easier to read. 550 | 551 | If you're stuck with Java 6 or 7, you can use the [Collections2][collections2] 552 | class, which has methods like filter and transform. They allow you to write 553 | fluent code without [Java 8][java8]'s stream support. 554 | 555 | 556 | Guava has simple things too, like a **Joiner** that joins strings on 557 | separators and a [class to handle interrupts][uninterrupt] by ignoring them. 558 | 559 | #### Gson 560 | 561 | Google's [Gson][gson] library is a simple and fast JSON parsing library. It 562 | works like this: 563 | 564 | ```java 565 | final Gson gson = new Gson(); 566 | final String json = gson.toJson(fooWidget); 567 | 568 | final FooWidget newFooWidget = gson.fromJson(json, FooWidget.class); 569 | ``` 570 | 571 | It's really easy and a pleasure to work with. The [Gson user guide][gsonguide] 572 | has many more examples. 573 | 574 | #### Java Tuples 575 | 576 | One of my on going annoyances with Java is that it doesn't have tuples built 577 | into the standard library. Luckily, the [Java tuples][javatuples] project fixes 578 | that. 579 | 580 | It's simple to use and works great: 581 | 582 | ```java 583 | Pair func(String input) { 584 | // something... 585 | return Pair.with(stringResult, intResult); 586 | } 587 | ``` 588 | 589 | #### Javaslang 590 | 591 | [Javaslang][javaslang] is a functional library, designed to add missing features 592 | that should have been part of Java 8. Some of these features are 593 | 594 | * an all-new functional collection library 595 | * tightly integrated tuples 596 | * pattern matching 597 | * throughout thread-safety because of immutability 598 | * eager and lazy data types 599 | * null-safety with the help of Option 600 | * better exception handling with the help of Try 601 | 602 | There are several Java libraries which depend on the original Java collections. 603 | These are restricted to stay compatible to classes which were created with an 604 | object-oriented focus and designed to be mutable. The Javaslang collections for 605 | Java are a completely new take, inspired by Haskell, Clojure and Scala. They are 606 | created with a functional focus and follow an immutable design. 607 | 608 | Code like this is automatically thread safe and try-catch free: 609 | 610 | ```java 611 | // Success/Failure containing the result/exception 612 | public static Try getUser(int userId) { 613 | return Try.of(() -> DB.findUser(userId)) 614 | .recover(x -> Match.of(x) 615 | .whenType(RemoteException.class).then(e -> ...) 616 | .whenType(SQLException.class).then(e -> ...)); 617 | } 618 | 619 | // Thread-safe, reusable collections 620 | public static List sayByeBye() { 621 | return List.of("bye, "bye", "collect", "mania") 622 | .map(String::toUpperCase) 623 | .intersperse(" "); 624 | } 625 | ``` 626 | 627 | #### Joda-Time 628 | 629 | [Joda-Time][joda] is easily the best time library I've ever used. Simple, 630 | straightforward, easy to test. What else can you ask for? 631 | 632 | You only need this if you're not yet on Java 8, as that has its own new 633 | [time][java8datetime] library that doesn't suck. 634 | 635 | #### Lombok 636 | 637 | [Lombok][lombok] is an interesting library. Through annotations, it allows you 638 | to reduce the boilerplate that Java suffers from so badly. 639 | 640 | Want setters and getters for your class variables? Simple: 641 | 642 | ```java 643 | public class Foo { 644 | @Getter @Setter private int var; 645 | } 646 | ``` 647 | 648 | Now you can do this: 649 | 650 | ```java 651 | final Foo foo = new Foo(); 652 | foo.setVar(5); 653 | ``` 654 | 655 | And there's [so much more][lombokguide]. I haven't used Lombok in production 656 | yet, but I can't wait to. 657 | 658 | #### Play framework 659 | 660 | **Good alternatives**: [Jersey][jersey] or [Spark][spark] 661 | 662 | There are two main camps for doing RESTful web services in Java: 663 | [JAX-RS][jaxrs] and everything else. 664 | 665 | JAX-RS is the traditional way. You combine annotations with interfaces and 666 | implementations to form the web service using something like [Jersey][jersey]. 667 | What's nice about this is you can easily make clients out of just the 668 | interface class. 669 | 670 | The [Play framework][play] is a radically different take on web services on 671 | the JVM: you have a routes file and then you write the classes referenced in 672 | those routes. It's actually an [entire MVC framework][playdoc], but you can 673 | easily use it for just REST web services. 674 | 675 | It's available for both Java and Scala. It suffers slightly from being 676 | Scala-first, but it's still good to use in Java. 677 | 678 | If you're used to micro-frameworks like Flask in Python, [Spark][spark] will 679 | be very familiar. It works especially well with Java 8. 680 | 681 | #### SLF4J 682 | 683 | There are a lot of Java logging solutions out there. My favorite is 684 | [SLF4J][slf4j] because it's extremely pluggable and can combine logs from many 685 | different logging frameworks at the same time. Have a weird project that uses 686 | java.util.logging, JCL, and log4j? SLF4J is for you. 687 | 688 | The [two-page manual][slf4jmanual] is pretty much all you'll need to get 689 | started. 690 | 691 | #### jOOQ 692 | 693 | I dislike heavy ORM frameworks because I like SQL. So I wrote a lot of 694 | [JDBC templates][jdbc] and it was sort of hard to maintain. [jOOQ][jooq] is a 695 | much better solution. 696 | 697 | It lets you write SQL in Java in a type safe way: 698 | 699 | ```java 700 | // Typesafely execute the SQL statement directly with jOOQ 701 | Result> result = 702 | create.select(BOOK.TITLE, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME) 703 | .from(BOOK) 704 | .join(AUTHOR) 705 | .on(BOOK.AUTHOR_ID.equal(AUTHOR.ID)) 706 | .where(BOOK.PUBLISHED_IN.equal(1948)) 707 | .fetch(); 708 | ``` 709 | 710 | Using this and the [DAO][dao] pattern, you can make database access a breeze. 711 | 712 | ### Testing 713 | 714 | Testing is critical to your software. These packages help make it easier. 715 | 716 | #### jUnit 4 717 | 718 | **Good alternative**: [TestNG][testng]. 719 | 720 | [jUnit][junit] needs no introduction. It's the standard tool for unit testing 721 | in Java. 722 | 723 | But you're probably not using jUnit to its full potential. jUnit supports 724 | [parametrized tests][junitparam], [rules][junitrules] to stop you from writing 725 | so much boilerplate, [theories][junittheories] to randomly test certain code, 726 | and [assumptions][junitassume]. 727 | 728 | #### jMock 729 | 730 | If you've done your dependency injection, this is where it pays off: mocking 731 | out code which has side effects (like talking to a REST server) and still 732 | asserting behavior of code that calls it. 733 | 734 | [jMock][jmock] is the standard mocking tool for Java. It looks like this: 735 | 736 | ```java 737 | public class FooWidgetTest { 738 | private Mockery context = new Mockery(); 739 | 740 | @Test 741 | public void basicTest() { 742 | final FooWidgetDependency dep = context.mock(FooWidgetDependency.class); 743 | 744 | context.checking(new Expectations() {{ 745 | oneOf(dep).call(with(any(String.class))); 746 | atLeast(0).of(dep).optionalCall(); 747 | }}); 748 | 749 | final FooWidget foo = new FooWidget(dep); 750 | 751 | Assert.assertTrue(foo.doThing()); 752 | context.assertIsSatisfied(); 753 | } 754 | } 755 | ``` 756 | 757 | This sets up a *FooWidgetDependency* via jMock and then adds expectations. We 758 | expect that *dep*'s *call* method will be called once with some String and that 759 | *dep*'s *optionalCall* method will be called zero or more times. 760 | 761 | If you have to set up the same dependency over and over, you should probably 762 | put that in a [test fixture][junitfixture] and put *assertIsSatisfied* in an 763 | *@After* fixture. 764 | 765 | #### AssertJ 766 | 767 | Do you ever do this with jUnit? 768 | 769 | ```java 770 | final List result = some.testMethod(); 771 | assertEquals(4, result.size()); 772 | assertTrue(result.contains("some result")); 773 | assertTrue(result.contains("some other result")); 774 | assertFalse(result.contains("shouldn't be here")); 775 | ``` 776 | 777 | This is just annoying boilerplate. [AssertJ][assertj] solves this. You can 778 | transform the same code into this: 779 | 780 | ```java 781 | assertThat(some.testMethod()).hasSize(4) 782 | .contains("some result", "some other result") 783 | .doesNotContain("shouldn't be here"); 784 | ``` 785 | 786 | This fluent interface makes your tests more readable. What more could you want? 787 | 788 | ## Tools 789 | 790 | ### IntelliJ IDEA 791 | 792 | **Good alternatives**: [Eclipse][eclipse] and [Netbeans][netbeans] 793 | 794 | The best Java IDE is [IntelliJ IDEA][intellij]. It has a ton of awesome 795 | features, and is really the main thing that makes the verbosity of Java 796 | bareable. Autocomplete is great, 797 | [the inspections are top notch][intellijexample], and the refactoring 798 | tools are really helpful. 799 | 800 | The free community edition is good enough for me, but there are loads of great 801 | features in the Ultimate edition like database tools, Spring Framework support 802 | and Chronon. 803 | 804 | #### Chronon 805 | 806 | One of my favorite features of GDB 7 was the ability to travel back in time 807 | when debugging. This is possible with the [Chronon IntelliJ plugin][chronon] 808 | when you get the Ultimate edition. 809 | 810 | You get variable history, step backwards, method history and more. It's a 811 | little strange to use the first time, but it can help debug some really 812 | intricate bugs, Heisenbugs and the like. 813 | 814 | ### JRebel 815 | 816 | **Good alternative**: [DCEVM](https://github.com/dcevm/dcevm) 817 | 818 | Continuous integration is often a goal of software-as-a-service products. What 819 | if you didn't even need to wait for the build to finish to see code changes 820 | live? 821 | 822 | That's what [JRebel][jrebel] does. Once you hook up your server to your JRebel 823 | client, you can see changes on your server instantly. It's a huge time savings 824 | when you want to experiment quickly. 825 | 826 | ### The Checker Framework 827 | 828 | Java's type system is pretty weak. It doesn't differentiate between Strings 829 | and Strings that are actually regular expressions, nor does it do any 830 | [taint checking][taint]. However, [the Checker Framework][checker] 831 | does this and more. 832 | 833 | It uses annotations like *@Nullable* to check types. You can even define 834 | [your own annotations][customchecker] to make the static analysis done even 835 | more powerful. 836 | 837 | ### Code Quality 838 | 839 | Even when following best practices, even the best developer will make mistakes. 840 | There are a number of tools out there that you can use to validate your Java 841 | code to detect problems in your code. Below is a small selection of some of the 842 | most popular tools. Many of these integrate with popular IDE's such as Eclipse 843 | or IntelliJ enabling you to spot mistakes in your code sooner. 844 | 845 | * **[Checkstyle](http://checkstyle.sourceforge.net/ "Checkstyle")**: A static 846 | code analyzer whose primary focus is to ensure that your code adheres to a 847 | coding standard. Rules are defined in an XML file that can be checked into 848 | source control alongside your code. 849 | * **[FindBugs](http://findbugs.sourceforge.net/ "FindBugs")**: Aims to spot code 850 | that can result in bugs/errors. Runs as a standalone process but has good 851 | integration into modern IDE's and build tools. 852 | * **[PMD](https://pmd.github.io/ "PMD")**: Similar to FindBugs, PMD aims to spot 853 | common mistakes & possible tidy-ups in your code. What rules are run against 854 | your code can be controlled via an XML file you can commit alongside your code. 855 | * **[SonarQube](http://www.sonarqube.org/ "SonarQube")**: Unlike the previous 856 | tools that run locally, SonarQube runs on a server that you submit your code to 857 | for analysis. It provides a web GUI where you are able to gain a wealth of 858 | information about your code such as bad practices, potential bugs, percentage 859 | test coverage and the level of 860 | [technical debt](https://en.wikipedia.org/wiki/Technical_debt "Technical Debt on Wikipedia") 861 | in your code. 862 | 863 | As well as using these tools during development, it's often a good idea to also 864 | have them run during your build stages. They can be tied into build tools such 865 | as Maven or Gradle & also into continuous integration tools. 866 | 867 | ### Eclipse Memory Analyzer 868 | 869 | Memory leaks happen, even in Java. Luckily, there are tools for that. The best 870 | tool I've used to fix these is the [Eclipse Memory Analyzer][mat]. It takes a 871 | heap dump and lets you find the problem. 872 | 873 | There's a few ways to get a heap dump for a JVM process, but I use 874 | [jmap][jmap]: 875 | 876 | ```bash 877 | $ jmap -dump:live,format=b,file=heapdump.hprof -F 8152 878 | Attaching to process ID 8152, please wait... 879 | Debugger attached successfully. 880 | Server compiler detected. 881 | JVM version is 23.25-b01 882 | Dumping heap to heapdump.hprof ... 883 | ... snip ... 884 | Heap dump file created 885 | ``` 886 | 887 | Then you can open the *heapdump.hprof* file with the Memory Analyzer and see 888 | what's going on fast. 889 | 890 | ## Resources 891 | 892 | Resources to help you become a Java master. 893 | 894 | ### Books 895 | 896 | * [Effective Java](http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp/0321356683) 897 | * [Java Concurrency in Practice](http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601) 898 | * [Clean Code](http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/) 899 | 900 | ### Podcasts 901 | 902 | * [The Java Posse](http://www.javaposse.com/) (*discontinued*) 903 | * [vJUG](http://virtualjug.com/) 904 | * [Les Cast Codeurs](https://lescastcodeurs.com/) (*French*) 905 | * [Java Pub House](http://www.javapubhouse.com/) 906 | * [Java Off Heap](http://www.javaoffheap.com/) 907 | * [Enterprise Java Newscast](http://www.enterprisejavanews.com) 908 | 909 | ### Videos 910 | 911 | * [Effective Java - Still Effective After All These Years](https://www.youtube.com/watch?v=V1vQf4qyMXg) 912 | * [InfoQ](http://www.infoq.com/) - see especially [presentations](http://www.infoq.com/java/presentations/) and [interviews](http://www.infoq.com/java/interviews/) 913 | * [Parleys](https://www.parleys.com/) 914 | 915 | [immutablemap]: http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/ImmutableMap.html 916 | [immutablelist]: http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/ImmutableList.html 917 | [immutableset]: http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/ImmutableSet.html 918 | [depconverge]: https://maven.apache.org/enforcer/enforcer-rules/dependencyConvergence.html 919 | [copydir]: http://commons.apache.org/proper/commons-io/javadocs/api-release/org/apache/commons/io/FileUtils.html#copyDirectory(java.io.File,%20java.io.File) 920 | [writestring]: http://commons.apache.org/proper/commons-io/javadocs/api-release/org/apache/commons/io/FileUtils.html#writeStringToFile(java.io.File,%20java.lang.String) 921 | [readlines]: http://commons.apache.org/proper/commons-io/javadocs/api-release/org/apache/commons/io/IOUtils.html#readLines(java.io.InputStream) 922 | [guava]: https://github.com/google/guava 923 | [cachebuilder]: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/cache/CacheBuilder.html 924 | [immutablesorted]: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/ImmutableSortedMultiset.html 925 | [uninterrupt]: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/Uninterruptibles.html 926 | [lists]: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Lists.html 927 | [maps]: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html 928 | [sets]: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html 929 | [collections2]: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Collections2.html 930 | [rootpom]: https://gist.github.com/cxxr/10787344 931 | [mavenexample]: http://books.sonatype.com/mvnex-book/reference/index.html 932 | [jenkins]: http://jenkins-ci.org/ 933 | [travis]: https://travis-ci.org/ 934 | [cobertura]: http://cobertura.github.io/cobertura/ 935 | [coberturamaven]: http://mojo.codehaus.org/cobertura-maven-plugin/usage.html 936 | [nexus]: http://www.sonatype.com/nexus 937 | [artifactory]: http://www.jfrog.com/ 938 | [mavenrepo]: http://stackoverflow.com/questions/364775/should-we-use-nexus-or-artifactory-for-a-maven-repo 939 | [artifactorymirror]: http://www.jfrog.com/confluence/display/RTF/Configuring+Artifacts+Resolution 940 | [gson]: https://github.com/google/gson 941 | [gsonguide]: https://sites.google.com/site/gson/gson-user-guide 942 | [joda]: http://www.joda.org/joda-time/ 943 | [lombokguide]: http://jnb.ociweb.com/jnb/jnbJan2010.html 944 | [play]: https://www.playframework.com/ 945 | [chef]: https://www.chef.io/chef/ 946 | [puppet]: https://puppetlabs.com/ 947 | [ansible]: http://www.ansible.com/home 948 | [squadron]: http://www.gosquadron.com 949 | [googlestyle]: http://google.github.io/styleguide/javaguide.html 950 | [googlepractices]: http://google.github.io/styleguide/javaguide.html#s6-programming-practices 951 | [di]: https://en.wikipedia.org/wiki/Dependency_injection 952 | [spring]: http://projects.spring.io/spring-framework/ 953 | [springso]: http://programmers.stackexchange.com/questions/92393/what-does-the-spring-framework-do-should-i-use-it-why-or-why-not 954 | [java8]: http://www.java8.org/ 955 | [javaslang]: http://javaslang.com/ 956 | [javastream]: http://blog.hartveld.com/2013/03/jdk-8-33-stream-api.html 957 | [slf4j]: http://www.slf4j.org/ 958 | [slf4jmanual]: http://www.slf4j.org/manual.html 959 | [junit]: http://junit.org/ 960 | [testng]: http://testng.org 961 | [junitparam]: https://github.com/junit-team/junit/wiki/Parameterized-tests 962 | [junitrules]: https://github.com/junit-team/junit/wiki/Rules 963 | [junittheories]: https://github.com/junit-team/junit/wiki/Theories 964 | [junitassume]: https://github.com/junit-team/junit/wiki/Assumptions-with-assume 965 | [jmock]: http://www.jmock.org/ 966 | [junitfixture]: https://github.com/junit-team/junit/wiki/Test-fixtures 967 | [initializingbean]: http://docs.spring.io/spring/docs/3.2.6.RELEASE/javadoc-api/org/springframework/beans/factory/InitializingBean.html 968 | [apachecommons]: http://commons.apache.org/ 969 | [lombok]: https://projectlombok.org/ 970 | [javatuples]: http://www.javatuples.org/ 971 | [dontbean]: http://www.javapractices.com/topic/TopicAction.do?Id=84 972 | [nullable]: https://github.com/google/guice/wiki/UseNullable 973 | [optional]: http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html 974 | [jdbc]: http://docs.spring.io/spring/docs/4.0.3.RELEASE/javadoc-api/org/springframework/jdbc/core/JdbcTemplate.html 975 | [jooq]: http://www.jooq.org/ 976 | [dao]: http://www.javapractices.com/topic/TopicAction.do?Id=66 977 | [gradle]: http://gradle.org/ 978 | [intellij]: http://www.jetbrains.com/idea/ 979 | [intellijexample]: http://i.imgur.com/92ztcCd.png 980 | [chronon]: http://blog.jetbrains.com/idea/2014/03/try-chronon-debugger-with-intellij-idea-13-1-eap/ 981 | [eclipse]: https://www.eclipse.org/ 982 | [dagger]: http://square.github.io/dagger/ 983 | [guice]: https://github.com/google/guice 984 | [netbeans]: https://netbeans.org/ 985 | [mat]: http://www.eclipse.org/mat/ 986 | [jmap]: http://docs.oracle.com/javase/7/docs/technotes/tools/share/jmap.html 987 | [jrebel]: http://zeroturnaround.com/software/jrebel/ 988 | [taint]: https://en.wikipedia.org/wiki/Taint_checking 989 | [checker]: http://types.cs.washington.edu/checker-framework/ 990 | [customchecker]: http://types.cs.washington.edu/checker-framework/tutorial/webpages/encryption-checker-cmd.html 991 | [builderex]: http://jlordiales.me/2012/12/13/the-builder-pattern-in-practice/ 992 | [javadocex]: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/ImmutableMap.Builder.html 993 | [dropwizard]: https://dropwizard.github.io/dropwizard/ 994 | [jersey]: https://jersey.java.net/ 995 | [springboot]: http://projects.spring.io/spring-boot/ 996 | [spark]: http://sparkjava.com/ 997 | [assertj]: http://joel-costigliola.github.io/assertj/index.html 998 | [jaxrs]: https://en.wikipedia.org/wiki/Java_API_for_RESTful_Web_Services 999 | [playdoc]: https://www.playframework.com/documentation/2.3.x/Anatomy 1000 | [java8datetime]: http://www.oracle.com/technetwork/articles/java/jf14-date-time-2125367.html 1001 | [checkedex]: http://docs.oracle.com/javase/7/docs/api/java/lang/Exception.html 1002 | [the-worst-mistake-of-computer-science]: https://www.lucidchart.com/techblog/2015/08/31/the-worst-mistake-of-computer-science/ 1003 | -------------------------------------------------------------------------------- /README.zh-cn.md: -------------------------------------------------------------------------------- 1 | # Better Java 2 | 3 | Java 虽作为最流行的编程语言之一,但是似乎并没有什么人很享受用它。好吧,Java 确实是这样的一门编程语言,从最近发布不久的 Java 8 开始,为了更好的使用 Java,我决定收集一些库,实践和工具等相关资料。“更好” 是主观的,所以推荐使用我所说的建议的某些部分,而不是一下子全部按照这些建议来做。请尽情添加其他意见并提交 PR。 4 | 5 | 这篇文章原始发布在 6 | [我的博客](https://www.seancassidy.me/better-java.html). 7 | 8 | 其他语言版本: [English](README.md), [简体中文](README.zh-cn.md) 9 | 10 | ## 目录 11 | 12 | * [Style(风格)](#style) 13 | * [Structs(结构)](#structs) 14 | * [The Builder Pattern(构建器模式)](#the-builder-pattern) 15 | * [Immutable Object Generation(不可变对象生成)](#immutable-object-generation) 16 | * [Exceptions(异常)](#exceptions) 17 | * [Dependency injection(依赖注入)](#dependency-injection) 18 | * [Avoid Nulls(避免空值)](#avoid-nulls) 19 | * [Immutable-by-default(默认不可变)](#immutable-by-default) 20 | * [Avoid lots of Util classes(避免过多的工具类)](#avoid-lots-of-util-classes) 21 | * [Formatting(格式)](#formatting) 22 | * [Javadoc](#javadoc) 23 | * [Streams(流)](#streams) 24 | * [Deploying(部署)](#deploying) 25 | * [Frameworks(框架)](#frameworks) 26 | * [Maven](#maven) 27 | * [Dependency Convergence(依赖收敛)](#dependency-convergence) 28 | * [Continuous Integration(持续集成)](#continuous-integration) 29 | * [Maven repository(Maven 仓库)](#maven-repository) 30 | * [Configuration management(配置管理)](#configuration-management) 31 | * [Libraries(库)](#libraries) 32 | * [Missing Features(缺失的特性)](#missing-features) 33 | * [Apache Commons](#apache-commons) 34 | * [Guava](#guava) 35 | * [Gson](#gson) 36 | * [Java Tuples(Java 元组)](#java-tuples) 37 | * [Javaslang](#javaslang) 38 | * [Joda-Time](#joda-time) 39 | * [Lombok](#lombok) 40 | * [Play framework(Play 框架)](#play-framework) 41 | * [SLF4J](#slf4j) 42 | * [jOOQ](#jooq) 43 | * [Testing(测试)](#testing) 44 | * [jUnit 4](#junit-4) 45 | * [jMock](#jmock) 46 | * [AssertJ](#assertj) 47 | * [Tools(工具)](#tools) 48 | * [IntelliJ IDEA](#intellij-idea) 49 | * [Chronon](#chronon) 50 | * [JRebel](#jrebel) 51 | * [The Checker Framework(检查框架)](#the-checker-framework) 52 | * [Code Quality(代码质量)](#code-quality) 53 | * [Eclipse Memory Analyzer(Eclipse 内存分析)](#eclipse-memory-analyzer) 54 | * [Resources(资源)](#resources) 55 | * [Books(书)](#books) 56 | * [Podcasts(播客)](#podcasts) 57 | * [Videos(视频)](#videos) 58 | 59 | ## Style 60 | 61 | Java 传统的代码风格是被用来编写非常复杂的企业级 JavaBean。新的代码风格看起来会更加整洁,更加正确,并且更加简单。 62 | 63 | ### Structs 64 | 65 | 对我们程序员来说,包装数据是最简单的事情之一。下面是传统的通过定义一个 JavaBean 的实现方式: 66 | 67 | ```java 68 | public class DataHolder { 69 | private String data; 70 | 71 | public DataHolder() { 72 | } 73 | 74 | public void setData(String data) { 75 | this.data = data; 76 | } 77 | 78 | public String getData() { 79 | return this.data; 80 | } 81 | } 82 | ``` 83 | 84 | 这种方式既繁琐又浪费代码。即使你的 IDE 可以自动生成这些代码,也是浪费。因此,[别这么干][dontbean]. 85 | 86 | 相反,我更喜欢 C 语言保存数据的风格来写一个类: 87 | 88 | ```java 89 | public class DataHolder { 90 | public final String data; 91 | 92 | public DataHolder(String data) { 93 | this.data = data; 94 | } 95 | } 96 | ``` 97 | 98 | 这样不仅减少了近一半的代码行数。并且,这个类里面保存的数据除了你去继承它,否则不会改变,由于它不可变性,我们可以认为这会更加简单。 99 | 100 | 如果你想保存很容易修改的对象数据,像 Map 或者 List,你应该使用 ImmutableMap 或者 ImmutableList,这些会在不变性那一部分讨论。 101 | 102 | #### The Builder Pattern 103 | 104 | 如果你想用这种构造的方式构造更复杂的对象,请考虑构建器模式。 105 | 106 | 你可以建一个静态内部类来构建你的对象。构建器构建对象的时候,对象的状态是可变的,但是一旦你调用了 build 方法之后,构建的对象就变成了不可变的了。 107 | 108 | 想象一下我们有一个更复杂的 *DataHolder*。那么它的构建器看起来应该是这样的: 109 | 110 | ```java 111 | public class ComplicatedDataHolder { 112 | public final String data; 113 | public final int num; 114 | // lots more fields and a constructor 115 | 116 | public static class Builder { 117 | private String data; 118 | private int num; 119 | 120 | public Builder data(String data) { 121 | this.data = data; 122 | return this; 123 | } 124 | 125 | public Builder num(int num) { 126 | this.num = num; 127 | return this; 128 | } 129 | 130 | public ComplicatedDataHolder build() { 131 | return new ComplicatedDataHolder(data, num); // etc 132 | } 133 | } 134 | } 135 | ``` 136 | 137 | 然后调用它: 138 | 139 | ```java 140 | final ComplicatedDataHolder cdh = new ComplicatedDataHolder.Builder() 141 | .data("set this") 142 | .num(523) 143 | .build(); 144 | ``` 145 | 146 | 这有[关于构建器更好的例子][builderex],他会让你感受到构建器到底是怎么回事。它没有使用许多我们尽力避免使用的样板,并且它会给你不可变的对象和非常好用的接口。 147 | 148 | 可以考虑下在众多的库中选择一个来帮你生成构建器,取代你亲手去写构建器的方式。 149 | 150 | #### Immutable Object Generation 151 | 152 | 如果你要手动创建许多不可变对象,请考虑用注解处理器的方式从它们的接口自动生成。它使样板代码减少到最小化,减少产生 bug 的可能性,促进了对象的不可变性。看这 [presentation](https://docs.google.com/presentation/d/14u_h-lMn7f1rXE1nDiLX0azS3IkgjGl5uxp5jGJ75RE/edit#slide=id.g2a5e9c4a8_00) 有常见的 Java 设计模式中一些问题的有趣的讨论。 153 | 154 | 一些非常棒的代码生成库如 [immutables] 155 | (https://github.com/immutables/immutables), 谷歌的 156 | [auto-value](https://github.com/google/auto/tree/master/value) 和 157 | [Lombok][lombok] 158 | 159 | ### Exceptions 160 | 161 | 使用[检查][checkedex]异常的时候一定要注意,或者干脆别用。它会强制你去用 try/catch 代码块包裹住可能抛出异常的部分。比较好的方式就是使你自定义的异常继承自运行时异常来取而代之。这样,可以让你的用户使用他们喜欢的方式去处理异常,而不是每次抛出异常的时候都强制它们去处理/声明,这样会污染代码。 162 | 163 | 一个比较漂亮的绝招是在你的方法异常声明中声明 RuntimeExceptions。这对编译器没有影响,但是可以通过文档告诉你的用户在这里可能会有异常抛出。 164 | 165 | ### Dependency injection 166 | 167 | 在软件工程领域,而不仅是在 Java 领域,使用[依赖注入][di]是编写可测试软件最好的方法之一。 168 | 由于 Java 强烈鼓励使用面向对象的设计,所以在 Java 中为了开发可测试软件,你不得不使用依赖注入。 169 | 170 | 在 Java 中,通常使用[Spring 框架][spring]来完成依赖注入。Spring 有基于代码的和基于 XML 配置文件的两种连接方式。如果你使用基于 XML 配置文件的方式,注意不要[过度使用 Spring][springso],正是由于它使用的基于 XML 配置文件的格式。在 XML 配置文件中绝对不应该有逻辑或者控制结构。它应该仅仅被用来做依赖注入。 171 | 172 | 使用 Google 和 Square 的 [Dagger][dagger] 或者 Google 的 [Guice][guice] 库是 Spring 比较好的替代品。它们不使用像 Spring 那样的 XML 配置文件的格式,相反它们把注入逻辑以注解的方式写到代码中。 173 | 174 | ### Avoid Nulls 175 | 176 | 尽量避免使用空值。不要返回 null 的集合,你应该返回一个 empty 的集合。如果你确实准备使用 null 请考虑使用 [@Nullable][nullable] 注解。[IntelliJ IDEA][intellij] 内置支持 @Nullable 注解。 177 | 178 | 阅读[计算机科学领域最糟糕的错误][the-worst-mistake-of-computer-science]了解更多为何不使用 null。 179 | 180 | 如果你使用的是 Java 8,你可以用新出的优秀的 [Optional][optional] 类型。如果有一个值你不确定是否存在,你可以像这样在类中用 *Optional* 包裹住它们: 181 | 182 | ```java 183 | public class FooWidget { 184 | private final String data; 185 | private final Optional bar; 186 | 187 | public FooWidget(String data) { 188 | this(data, Optional.empty()); 189 | } 190 | 191 | public FooWidget(String data, Optional bar) { 192 | this.data = data; 193 | this.bar = bar; 194 | } 195 | 196 | public Optional getBar() { 197 | return bar; 198 | } 199 | } 200 | ``` 201 | 202 | 这样,现在你可以清晰地知道 *data* 肯定不为 null,但是 *bar* 不清楚是不是存在。*Optional* 有如 *isPresent* 这样的方法,可以用来检查是否为 *null*,感觉和原来的方式并没有太大区别。但是它允许你可以这样写: 203 | 204 | ```java 205 | final Optional fooWidget = maybeGetFooWidget(); 206 | final Baz baz = fooWidget.flatMap(FooWidget::getBar) 207 | .flatMap(BarWidget::getBaz) 208 | .orElse(defaultBaz); 209 | ``` 210 | 211 | 这样比写一连串的判断是否为空的检查代码更好。使用 Optional 唯一不好的是标准库对 Optional 的支持并不是很好,所以对 null 的处理仍然是必要的。 212 | 213 | ### Immutable-by-default 214 | 215 | 变量,类和集合应该设置为不可变的,除非你有很好的理由去修改他们。 216 | 217 | 变量可以用 *final* 关键字使起不可变: 218 | 219 | ```java 220 | final FooWidget fooWidget; 221 | if (condition()) { 222 | fooWidget = getWidget(); 223 | } else { 224 | try { 225 | fooWidget = cachedFooWidget.get(); 226 | } catch (CachingException e) { 227 | log.error("Couldn't get cached value", e); 228 | throw e; 229 | } 230 | } 231 | // fooWidget is guaranteed to be set here 232 | ``` 233 | 234 | 现在你可以确定 fooWidget 对象不会意外地被重新赋值了。*final* 关键词也可以在 if/else 和 try/catch 代码块中使用。当然,如果 *fooWidget* 对象本身不是不可变的,你可以很容易去修改它。 235 | 236 | 使用集合的时候,任何可能的情况下尽量使用 Guava 的 [ImmutableMap][immutablemap], [ImmutableList][immutablelist], 或者 237 | [ImmutableSet][immutableset] 类。这些类都有构建器,你可以很容易地动态构建集合,一旦你执行了 build 方法,集合就变成了不可变的。 238 | 239 | 类应该声明不可变的字段(通过 *final* 实现)和不可变的集合使该类不可变。或者,可以对类本身使用 *final* 关键词,这样这个类就不会被继承也不会被修改了。 240 | 241 | ### Avoid lots of Util classes 242 | 243 | 如果你发现在你正在往工具类中添加很多方法,就要注意了。 244 | 245 | ```java 246 | public class MiscUtil { 247 | public static String frobnicateString(String base, int times) { 248 | // ... etc 249 | } 250 | 251 | public static void throwIfCondition(boolean condition, String msg) { 252 | // ... etc 253 | } 254 | } 255 | ``` 256 | 257 | 乍一看这些工具类似乎很不错,因为里面的那些方法放在别处确实都不太合适。因此,你以可重用代码的名义全放这了。 258 | 259 | 这个想法比本身这么做还要糟糕。请把这些类放到它应该在的地方去并积极重构。不要命名一些像 "MiscUtils" 或者 "ExtrasLibrary" 这样的很普通的类,包或者库。这会鼓励产生无关代码。 260 | 261 | ### Formatting 262 | 263 | 格式化代码对大多数程序员来说并没有它应有的那么重要。统一化你的代码格式对阅读你的代码的人有帮助吗?当然了。但是别在为了 if 代码块匹配添加空格上耗一天。 264 | 265 | 如果你确实需要一个代码格式风格的教程,我高度推荐 [Google's Java Style][googlestyle] 这个教程。写的最好的部分是 [Programming Practices][googlepractices]。绝对值得一读。 266 | 267 | #### Javadoc 268 | 269 | 文档对对你代码的阅读着来说也很重要。这意味着你要给出[使用示例][javadocex],并且给出你的变量,方法和类清晰地描述。 270 | 271 | 这样做的必然结果是不要对不需要写文档的地方填写文档。如果你对一个参数的含义没什么可说的,或者它本身已经很明显是什么意思了,就不要为其写文档了。统一样板的文档比没有文档更加糟糕,这样会让读你代码的人误以为那就是文档。 272 | 273 | ### Streams 274 | 275 | [Java 8][java8] 有很棒的 [stream][javastream] and lambda 语法。你可以像这样来写代码: 276 | ```java 277 | final List filtered = list.stream() 278 | .filter(s -> s.startsWith("s")) 279 | .map(s -> s.toUpperCase()) 280 | .collect(Collectors.toList()); 281 | ``` 282 | 283 | 取代这样的写法: 284 | 285 | ```java 286 | final List filtered = new ArrayList<>(); 287 | for (String str : list) { 288 | if (str.startsWith("s") { 289 | filtered.add(str.toUpperCase()); 290 | } 291 | } 292 | ``` 293 | 294 | 它让你可以写更多的流畅的代码,并且可读性更高。 295 | 296 | ## Deploying 297 | 298 | Java 的部署问题确实有点棘手。现如今有两种主流的方式:使用框架或者灵活性更高的内部研发的解决方案。 299 | 300 | ### Frameworks 301 | 302 | 由于 Java 的部署并不容易,所以使用框架还是很有帮助的。最好的两个框架是 [Dropwizard][dropwizard] 和 [Spring Boot][springboot]。[Play 框架][play] 也可以被看作为一种部署框架。 303 | 304 | 这些框架都是尽力地降低你部署代码的壁垒。它们对 Java 新手或者想提高效率的人尤有帮助。单独的 JAR 包部署会比复杂的 WAR 包或者 EAR 包部署更简单一点。 305 | 306 | 然而,这些框架并没有你想象的那么灵活,如果你的项目的开发者选择的框架并不合适,你不得不迁移到手动配置更多的部署方案上来。 307 | 308 | ### Maven 309 | 310 | **不错的替代工具**: [Gradle][gradle]. 311 | 312 | Maven 仍然是构建,打包和测试的标准。有很多不错的替代工具,如 Gradle,但是他们同样都没有像 Maven 那样的适应性。如果你是 Maven 新手,你应该从[Maven 实例][mavenexample]这里开始。 313 | 314 | 我喜欢用一个根 POM(Project Object Model,项目对象模型)来管理所有用到的外部依赖。它会像[这个样子][rootpom]。这个根 POM 仅仅包含一个外部依赖,但是如果你的产品足够大,你将会有几十个外部依赖了。你的根 POM 应该像其他 Java 项目一样采用版本控制和发布的方式,有一个自己的项目。 315 | 316 | 如果你认为你的根 POM 每添加一个外部依赖都打上一个标签很麻烦,那你肯定没有遇到过为了排查依赖错误引起的问题,浪费一周的时间翻遍整个项目的情况。 317 | 318 | 你所有的 Maven 项目都应该包含你的根 POM,以及这些项目的所有版本信息。这样你会清除地了解到你们公司选择的每一个外部依赖的版本,以及所有正确的 Maven 插件。如果你要引入很多的外部依赖,它将会是这样子的: 319 | 320 | ```xml 321 | 322 | 323 | org.third.party 324 | some-artifact 325 | 326 | 327 | ``` 328 | 329 | 如果你想使用内部依赖,它应该被每一个单独项目的 **** 部分来管理。否则那将会很难保持根 POM 的版本号是正常的。 330 | 331 | #### Dependency Convergence 332 | 333 | Java 最好的一方面就是拥有大量的第三方库可以做任何事。基本上每一个 API 或者工具包都有一个 Java SDK,可以很方便的用 Maven 引入。 334 | 335 | 并且这些第三方 Java 库本身依赖特定版本的其他的库。如果你引入足够多的库,你会发现有些库的版本是冲突的,像这样: 336 | 337 | Foo library depends on Bar library v1.0 338 | Widget library depends on Bar library v0.9 339 | 340 | 你的项目到底要引入哪一个版本呢? 341 | 342 | 如果你的项目依赖于不同版本的同一个库,使用 [Maven 依赖趋同插件][depconverge]构建时将会报错。然后你有两个方案来解决这个冲突: 343 | 344 | 1. 在你的 *dependencyManagement* 部分明确地支出你所使用的 Bar 的版本号 345 | 2. 在 FOO 或者 Widget 中排除对 Bar 的依赖。 346 | 347 | 这两个方案到底选哪一个要看你面对的是什么情况:如果你想跟踪一个项目的版本,那么选择排除的方案是不错的。另一方面,如果你想明确地指出它,你可以选择一个版本,尽管你在需要更新其他依赖的时候也需要更新它。 348 | 349 | ### Continuous Integration 350 | 351 | 很明显,你需要某种形式的持续集成服务器来帮你不断构建你的快照版本和基于 git 标签构建。 352 | 353 | [Jenkins][jenkins] 和 [Travis-CI][travis] 就成了很自然的选择. 354 | 355 | 代码覆盖率非常有用,[Cobertura][cobertura] 就有 [一个很好的 Maven 插件][coberturamaven] 356 | [a good Maven plugin][coberturamaven] 并且支持 CI。还有一些其他的支持 Java 的代码覆盖率工具,但是我只用过 Cobertura。 357 | 358 | ### Maven repository 359 | 360 | 你需要一个地方存储你生成的 JAR 包,WAR 包或者 EAR 包,因此,你需要一个仓库。 361 | 362 | 一般选择有 [Artifactory][artifactory] 和 [Nexus][nexus] 这两个。它们都可以用,但是它们都有着各自的优缺点。 363 | 364 | 你应该有自己的 Artifactory/Nexus 设备和[镜像][artifactorymirror] 使你的依赖基于此。这样就不会由于上游的 Maven 库宕机而使你的构建崩溃了。 365 | 366 | ### Configuration management 367 | 368 | 现在,你的代码已经编译完了,你的仓库也跑起来了,最终你需要把你的代码从开发环境部署到生产环境了。到了这里,千万不要吝啬,因为将来很长一段时间,你会从这些自动化方式中尝到很多的甜头。 369 | 370 | [Chef][chef],[Puppet][puppet],和 [Ansible][ansible] 是很典型的选择。我曾经也写了一个叫 [Squadron][squadron] 的也可供选择,当然,我认为你应该仔细看看这个,因为它使用起来比其他的更为简单方便。 371 | 372 | 无论你选择了什么工具,不要忘了使你的部署实现自动化。 373 | 374 | ## Libraries 375 | 376 | 对 Java 来说,拥有大量的扩展库也许是最大的特点了。下面这些一小部分的扩展库对大部分人来说很适用的。 377 | 378 | ### Missing Features 379 | 380 | Java 标准库曾经作出过惊人的改进,但是现在来看,它仍然缺少一些关键的特性。 381 | 382 | #### Apache Commons 383 | 384 | [Apache Commons 项目][apachecommons] 拥有大量的有用的扩展库。 385 | 386 | **Commons Codec** 对 Base64 和 16 进制字符串来说有很多有用的编/解码方法。不要再浪费时间重写这些东西了。 387 | 388 | **Commons Lang** 有许多关于字符串的操作和创建,字符集和许多各种各样的实用的方法。 389 | 390 | **Commons IO** 拥有所有你能想到的关于文件操作的方法。有 391 | [FileUtils.copyDirectory][copydir],[FileUtils.writeStringToFile][writestring],[IOUtils.readLines][readlines] 和更多实用的方法。 392 | 393 | #### Guava 394 | 395 | [Guava][guava] 是谷歌优秀的对 Java 标准库缺少的特性进行补充的扩展库。虽然这很难提炼总结出我有多喜欢这个库,但是我会尽力的。 396 | 397 | **Cache** 让你可以用很简单的方法,实现把网络访问,磁盘访问,缓存函数或者其他任何你想要缓存的内容,缓存到内存当中。你仅仅只需要实现 [CacheBuilder][cachebuilder] 类并且告诉 Guava 怎么样构建你的缓存,一切就搞定了! 398 | 399 | **Immutable** 集合。它有许多如:[ImmutableMap][immutablemap],[ImmutableList][immutablelist],或者甚至 [ImmutableSortedMultiSet][immutablesorted] 等不可变集合可以使用,如果你喜欢用这种风格的话。 400 | 401 | 我也喜欢用 Guava 的方式来写一些可变的集合: 402 | 403 | ```java 404 | // Instead of 405 | final Map map = new HashMap<>(); 406 | 407 | // You can use 408 | final Map map = Maps.newHashMap(); 409 | ``` 410 | 411 | 它还有一些静态类如 [Lists][lists],[Maps][maps]和[Sets][sets] 等。使用起来它们显得更整洁,并且可读性更强。 412 | 413 | 如果你坚持使用 Java 6 或者 7 的话,你可以使用 [Collections2][collections2] 这个类,它有一些像 filter 和 transform 这样的方法。能够让你没有 Java 8 的 Stream 的支持也能写出流畅的代码。 414 | 415 | Guava 也可以做一些很简单的事情,比如 **Joiner** 类可以用来用分隔符把字符串拼接起来,并且可以用忽略的方式[来处理打断程序][uninterrupt]的数据。 416 | 417 | #### Gson 418 | 419 | 谷歌的 [Gson][gson] 库是一个简单快速的 JSON 解析库。可以这样用: 420 | 421 | ```java 422 | final Gson gson = new Gson(); 423 | final String json = gson.toJson(fooWidget); 424 | 425 | final FooWidget newFooWidget = gson.fromJson(json, FooWidget.class); 426 | ``` 427 | 428 | 这用起来真的很简单,很愉悦。[Gson 用户手册][gsonguide] 有很多的使用示例。 429 | 430 | #### Java Tuples 431 | 432 | Java 令我比较烦恼的问题之一 Java 标准库中没有内置对元组的支持。幸运的是,[Java tuples][javatuples] 项目解决了这个问题。 433 | 434 | 它使用用起来很简单,很棒: 435 | 436 | ```java 437 | Pair func(String input) { 438 | // something... 439 | return Pair.with(stringResult, intResult); 440 | } 441 | ``` 442 | 443 | #### Javaslang 444 | 445 | [Javaslang][javaslang] 是一个函数式编程库,它被设计用来弥补本应该出现在 Java 8 中但缺失的一些特性。它有这样的一些特点: 446 | 447 | * 一个全新函数式集合库 448 | * 紧密集成的元组功能 449 | * 模式匹配 450 | * 通过不可变性保证线程安全 451 | * 饥汉式和懒汉式的数据类型 452 | * 通过 Option 实现了 null 的安全性 453 | * 通过 Try 更好的实现异常处理 454 | 455 | 有一些 Java 库依赖于原始的 Java 集合类。它们通过以面向对象和被设计为可变的方式来保证和其他的类的兼容性。而 Javaslang 的集合的设计灵感来源于 Haskell, Clojure 和 Scala,是一个全新的飞跃。它们被设计为函数式风格并且遵循不可变性的设计风格。 456 | 457 | 像下面这样的代码就可以自动实现线程安全,并且不用 try-catch 语句处理异常: 458 | 459 | ```java 460 | // Success/Failure containing the result/exception 461 | public static Try getUser(int userId) { 462 | return Try.of(() -> DB.findUser(userId)) 463 | .recover(x -> Match.of(x) 464 | .whenType(RemoteException.class).then(e -> ...) 465 | .whenType(SQLException.class).then(e -> ...)); 466 | } 467 | 468 | // Thread-safe, reusable collections 469 | public static List sayByeBye() { 470 | return List.of("bye, "bye", "collect", "mania") 471 | .map(String::toUpperCase) 472 | .intersperse(" "); 473 | } 474 | ``` 475 | 476 | #### Joda-Time 477 | 478 | [Joda-Time][joda] 是我用过的最简单的时间处理库。简单,直接,并且很容易测试。夫复何求? 479 | 480 | 因为 Java 8 已经有了自己的新的 [时间处理][java8datetime]库, 所以如果你还没有用 Java 8,你需要这一个库足矣。 481 | 482 | #### Lombok 483 | 484 | [Lombok][lombok] 是一个很有意思的库。它可以让你以注解的方式减少 Java 中糟糕的样板代码。 485 | 486 | 想为你的类的变量添加 setter 和 getter 方法吗?像这样: 487 | 488 | ```java 489 | public class Foo { 490 | @Getter @Setter private int var; 491 | } 492 | ``` 493 | 494 | 现在你就可以这么用了: 495 | 496 | ```java 497 | final Foo foo = new Foo(); 498 | foo.setVar(5); 499 | ``` 500 | 501 | 这还有[很多][lombokguide]例子。我在之前的产品中还没有用过 Lombok,但是现在我等不急了。 502 | 503 | #### Play framework 504 | 505 | **好的替代品**: [Jersey][jersey] 或者 [Spark][spark] 506 | 507 | 在 Java 实现 RESTful web services 有两大主要阵营:[JAX-RS][jaxrs] 和其他。 508 | 509 | JAX-RS 是传统的实现方式。你可以用像 [Jersey][jersey] 这样的框架,以注解的方式来实现接口及其实现的结合。这样你就可以很容易的根据接口类来开发客户端。 510 | 511 | [Play 框架][play] 基于 JVM 的 web services 实现和其他根本框架不同:它有一个路由文件,你写的类要和路由文件中的路由信息关联起来。Play 框架其实是一个[完整的 MVC 框架][playdoc],但是你可以很简单地仅仅使用它的 REST web services 部分的功能。 512 | 513 | 它同时支持 Java 和 Scala。虽然对重点支持的 Scala 稍有不足,但是对 Java 的支持还是很好用的。 514 | 515 | 如果你在 Python 中用过像 Flask 这样的微框架,你对 [Spark][spark] 肯定会很熟悉。它对 Java 8 的支持尤其的好。 516 | 517 | #### SLF4J 518 | 519 | 有很多 Java 日志解决方案。我最喜欢的是 [SLF4J][slf4j],因为它拥有非常棒的可插拔性,同时能够和很多的日志框架想结合。有没有做过同时使用 java.util.logging,JCL,和 log4j 的奇葩项目?SLF4J 就是为你而生。 520 | 521 | 这[两页手册][slf4jmanual]足够你可以开始入门使用 SLF4J 了。 522 | 523 | #### jOOQ 524 | 525 | 我不喜欢重量级的 ORM 框架,因为我喜欢 SQL。所以我写了很多 [JDBC 模板][jdbc],但是很难去维护它。[jOOQ][jooq] 是一个更好的解决方案。 526 | 527 | 它让你在 Java 中用类型安全的方式编写 SQL: 528 | 529 | ```java 530 | // Typesafely execute the SQL statement directly with jOOQ 531 | Result> result = 532 | create.select(BOOK.TITLE, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME) 533 | .from(BOOK) 534 | .join(AUTHOR) 535 | .on(BOOK.AUTHOR_ID.equal(AUTHOR.ID)) 536 | .where(BOOK.PUBLISHED_IN.equal(1948)) 537 | .fetch(); 538 | ``` 539 | 540 | 使用 jOOQ 和 [DAO][dao] 的模式让你的数据库访问变得轻而易举。 541 | 542 | ### Testing 543 | 544 | 测试是软件的关键环节。下面这些软件包能够让你更容易地测试。 545 | 546 | #### jUnit 4 547 | 548 | **好的替代品**:[TestNG][testng]. 549 | 550 | [jUnit][junit] 就无需多言了。它是 Java 单元测试中的标准工具。 551 | 552 | 但是很可能你使用的 jUnit 并没有发挥它的全部潜力。jUnit 支持[参数化测试][junitparam],[规则化][junitrules]测试,[theories][junittheories] 可以随机测试特定代码,还有 [assumptions][junitassume],可以让你少写很多样板代码。 553 | 554 | #### jMock 555 | 556 | 如果你完成了依赖注入,这是它的回报:可以 mock 出有副作用(比如和 REST 服务器交互)的代码,并且可以断言调用这段代码的行为。 557 | 558 | [jMock][jmock] 是标准的 Java mock 工具。像这样使用: 559 | 560 | ```java 561 | public class FooWidgetTest { 562 | private Mockery context = new Mockery(); 563 | 564 | @Test 565 | public void basicTest() { 566 | final FooWidgetDependency dep = context.mock(FooWidgetDependency.class); 567 | 568 | context.checking(new Expectations() {{ 569 | oneOf(dep).call(with(any(String.class))); 570 | atLeast(0).of(dep).optionalCall(); 571 | }}); 572 | 573 | final FooWidget foo = new FooWidget(dep); 574 | 575 | Assert.assertTrue(foo.doThing()); 576 | context.assertIsSatisfied(); 577 | } 578 | } 579 | ``` 580 | 581 | 这段代码通过 jMock 建立了一个 *FooWidgetDependency*,然后添加你所期望结果的条件。我们期望 *dep* 的 *call* 方法会被以一个字符串为参数的形式调用,并且会被调用 0 次或者多次。 582 | 583 | 如果你想一遍又一遍地设置相同的依赖,你应该把它放到 [test fixture][junitfixture] 中,并且把*assertIsSatisfied* 放在以 *@After* 注解的 fixture 中。 584 | 585 | #### AssertJ 586 | 587 | 你曾经用 jUnit 干过这个吗? 588 | 589 | ```java 590 | final List result = some.testMethod(); 591 | assertEquals(4, result.size()); 592 | assertTrue(result.contains("some result")); 593 | assertTrue(result.contains("some other result")); 594 | assertFalse(result.contains("shouldn't be here")); 595 | ``` 596 | 597 | 这是很恶心的样板代码。[AssertJ][assertj] 可以解决这个问题。你可以把相同的代码转换成这个样子: 598 | 599 | ```java 600 | assertThat(some.testMethod()).hasSize(4) 601 | .contains("some result", "some other result") 602 | .doesNotContain("shouldn't be here"); 603 | ``` 604 | 605 | 这样的流畅接口让你的测试更具有可读性。你还想咋地? 606 | 607 | ## Tools 608 | 609 | ### IntelliJ IDEA 610 | 611 | **好的替代品**: [Eclipse][eclipse] 和 [Netbeans][netbeans] 612 | 613 | Java 最好的 IDE 是 [IntelliJ IDEA][intellij]。它有大量的牛逼的特性,它是真正的能让 Java 用来像不戴套做爱那么爽的工具。自动完成功能超棒,[代码检查功能也是顶尖的][intellijexample],重构工具那是相当有帮助。 614 | 615 | 免费的社区版对我来说已经足够好了,但是它的旗舰版加载了更多的牛逼的特性,如数据库工具,Spring 框架的支持和对 Chronon 的支持。 616 | 617 | #### Chronon 618 | 619 | 我最喜欢 GDB 7 的特性之一就是调试的时候能够按照时间跟踪回来。当你拥有了旗舰版的 IntelliJ,你可以通过安装 [Chronon IntelliJ 插件][chronon]实现。 620 | 621 | 你可以获取到变量的变化历史,后退,方法的历史以及更多的信息。如果你是第一次用会觉得有点怪,但是它真的能够帮你解决很复杂的 bug,诸如海森堡类的 bug。 622 | 623 | ### JRebel 624 | 625 | **好的替代品**: [DCEVM](https://github.com/dcevm/dcevm) 626 | 627 | 持续集成往往以软件即服务为产品目标。想象一下如果你不用等待代码构建完成而能实时看到代码的变化会是怎样? 628 | 629 | 这就是 [JRebel][jrebel] 所做的。一旦你将你的服务器和你的 JReble 以 hook 方式连接,你就可以从服务器看到实时变化。当你想快速试验的时候它能为你节省大量的时间。 630 | 631 | ### The Checker Framework 632 | 633 | Java 的类型系统很差劲。它不能够区分正常的字符串和正则表达式字符串,更不用说[坏点检查][taint]了。不过 [Checker Framework][checker] 可以完成这个功能并且能够实现更多的东西。 634 | 635 | 它使用像 *@Nullable* 这样的注解来检查类型。你甚至可以使用[自定义注解][customchecker]来实现静态分析,甚至更强大的功能。 636 | 637 | ### Code Quality 638 | 639 | 即使遵循着最佳实践的原则,即使是最好的开发者,也都会犯错误。这有很多工具,你可以使用它们验证你的代码从而检查代码是否有问题。下面是选出的最流行的一部分工具。很多这些工具都可以和流行的 IDE 如 Eclipse 或者 IntelliJ 集成,可以让你更快地发现代码中的错误。 640 | 641 | * **[Checkstyle](http://checkstyle.sourceforge.net/ "Checkstyle")**:一个静态代码分析工具,它主要着力于保证你的代码符合代码标准。检查规则在一个 XML 文件中定义,你可以把它检入你的版本控制工具,和你的代码放在一起。 642 | * **[FindBugs](http://findbugs.sourceforge.net/ "FindBugs")**:主要集中于发现你的代码中可能导致产生 bug 或者错误的部分。虽然作为独立的进程运行,但是对流行的 IDE 和构建工具的支持也很好。 643 | * **[PMD](https://pmd.github.io/ "PMD")**:和 FindBugs 很相似,PMD 着力于发现你代码中的错误和整理的你的代码。你可以把针对你的代码的检查规则控制在 XML 文件中,和你的代码放在一块儿提交。 644 | * **[SonarQube](http://www.sonarqube.org/ "SonarQube")**:和前面所述的工具不同,它是在本地运行的,SonarQube 启动一个服务器,你把你代码提交到这个服务器来进行分析。它提供了 web 界面,你可以看到你的代码的健康状况信息,如不好的做法,潜在的 bug,测试覆盖率百分比,和你写代码的[技术水平](https://en.wikipedia.org/wiki/Technical_debt "Technical Debt on Wikipedia") 645 | 646 | 除了在开发工程中使用这些工具,把它们用在你的构建阶段往往也是一个不错的想法。它可以和想 Maven 或者 Gradle 这样的构建工具绑定到一起,也可以和持续集成工具绑定使用。 647 | 648 | ### Eclipse Memory Analyzer 649 | 650 | 即使在 Java 中内存泄露也时有发生。幸运的是,我们有一些工具就是为此而生。[Eclipse Memory Analyzer][mat] 是我用过的最好用的解决内存泄露问题的工具。它能够获取到堆栈信息让你查阅,去发现问题所在。 651 | 652 | 有几种方法可以获取到 JVM 进程的堆栈信息,但是我用 [jmap][jmap] 工具实现: 653 | 654 | ```bash 655 | $ jmap -dump:live,format=b,file=heapdump.hprof -F 8152 656 | Attaching to process ID 8152, please wait... 657 | Debugger attached successfully. 658 | Server compiler detected. 659 | JVM version is 23.25-b01 660 | Dumping heap to heapdump.hprof ... 661 | ... snip ... 662 | Heap dump file created 663 | ``` 664 | 665 | 然后你可以用内存分析器打开 *heapdump.hprof* 文件,快看看到底是怎么回事。 666 | 667 | ## Resources 668 | 669 | 这些资源能够帮你成为 Java 大牛。 670 | 671 | ### Books 672 | 673 | * [Effective Java](http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp/0321356683) 674 | * [Java Concurrency in Practice](http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601) 675 | * [Clean Code](http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/) 676 | 677 | ### Podcasts 678 | 679 | * [The Java Posse](http://www.javaposse.com/) (*discontinued*) 680 | * [vJUG](http://virtualjug.com/) 681 | * [Les Cast Codeurs](https://lescastcodeurs.com/) (*French*) 682 | * [Java Pub House](http://www.javapubhouse.com/) 683 | * [Java Off Heap](http://www.javaoffheap.com/) 684 | * [Enterprise Java Newscast](http://www.enterprisejavanews.com) 685 | 686 | ### Videos 687 | 688 | * [Effective Java - Still Effective After All These Years](https://www.youtube.com/watch?v=V1vQf4qyMXg) 689 | * [InfoQ](http://www.infoq.com/) - see especially [presentations](http://www.infoq.com/java/presentations/) and [interviews](http://www.infoq.com/java/interviews/) 690 | * [Parleys](https://www.parleys.com/) 691 | 692 | [immutablemap]: http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/ImmutableMap.html 693 | [immutablelist]: http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/ImmutableList.html 694 | [immutableset]: http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/ImmutableSet.html 695 | [depconverge]: https://maven.apache.org/enforcer/enforcer-rules/dependencyConvergence.html 696 | [copydir]: http://commons.apache.org/proper/commons-io/javadocs/api-release/org/apache/commons/io/FileUtils.html#copyDirectory(java.io.File,%20java.io.File) 697 | [writestring]: http://commons.apache.org/proper/commons-io/javadocs/api-release/org/apache/commons/io/FileUtils.html#writeStringToFile(java.io.File,%20java.lang.String) 698 | [readlines]: http://commons.apache.org/proper/commons-io/javadocs/api-release/org/apache/commons/io/IOUtils.html#readLines(java.io.InputStream) 699 | [guava]: https://github.com/google/guava 700 | [cachebuilder]: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/cache/CacheBuilder.html 701 | [immutablesorted]: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/ImmutableSortedMultiset.html 702 | [uninterrupt]: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/Uninterruptibles.html 703 | [lists]: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Lists.html 704 | [maps]: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html 705 | [sets]: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html 706 | [collections2]: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Collections2.html 707 | [rootpom]: https://gist.github.com/cxxr/10787344 708 | [mavenexample]: http://books.sonatype.com/mvnex-book/reference/index.html 709 | [jenkins]: http://jenkins-ci.org/ 710 | [travis]: https://travis-ci.org/ 711 | [cobertura]: http://cobertura.github.io/cobertura/ 712 | [coberturamaven]: http://mojo.codehaus.org/cobertura-maven-plugin/usage.html 713 | [nexus]: http://www.sonatype.com/nexus 714 | [artifactory]: http://www.jfrog.com/ 715 | [mavenrepo]: http://stackoverflow.com/questions/364775/should-we-use-nexus-or-artifactory-for-a-maven-repo 716 | [artifactorymirror]: http://www.jfrog.com/confluence/display/RTF/Configuring+Artifacts+Resolution 717 | [gson]: https://github.com/google/gson 718 | [gsonguide]: https://sites.google.com/site/gson/gson-user-guide 719 | [joda]: http://www.joda.org/joda-time/ 720 | [lombokguide]: http://jnb.ociweb.com/jnb/jnbJan2010.html 721 | [play]: https://www.playframework.com/ 722 | [chef]: https://www.chef.io/chef/ 723 | [puppet]: https://puppetlabs.com/ 724 | [ansible]: http://www.ansible.com/home 725 | [squadron]: http://www.gosquadron.com 726 | [googlestyle]: http://google.github.io/styleguide/javaguide.html 727 | [googlepractices]: http://google.github.io/styleguide/javaguide.html#s6-programming-practices 728 | [di]: https://en.wikipedia.org/wiki/Dependency_injection 729 | [spring]: http://projects.spring.io/spring-framework/ 730 | [springso]: http://programmers.stackexchange.com/questions/92393/what-does-the-spring-framework-do-should-i-use-it-why-or-why-not 731 | [java8]: http://www.java8.org/ 732 | [javaslang]: http://javaslang.com/ 733 | [javastream]: http://blog.hartveld.com/2013/03/jdk-8-33-stream-api.html 734 | [slf4j]: http://www.slf4j.org/ 735 | [slf4jmanual]: http://www.slf4j.org/manual.html 736 | [junit]: http://junit.org/ 737 | [testng]: http://testng.org 738 | [junitparam]: https://github.com/junit-team/junit/wiki/Parameterized-tests 739 | [junitrules]: https://github.com/junit-team/junit/wiki/Rules 740 | [junittheories]: https://github.com/junit-team/junit/wiki/Theories 741 | [junitassume]: https://github.com/junit-team/junit/wiki/Assumptions-with-assume 742 | [jmock]: http://www.jmock.org/ 743 | [junitfixture]: https://github.com/junit-team/junit/wiki/Test-fixtures 744 | [initializingbean]: http://docs.spring.io/spring/docs/3.2.6.RELEASE/javadoc-api/org/springframework/beans/factory/InitializingBean.html 745 | [apachecommons]: http://commons.apache.org/ 746 | [lombok]: https://projectlombok.org/ 747 | [javatuples]: http://www.javatuples.org/ 748 | [dontbean]: http://www.javapractices.com/topic/TopicAction.do?Id=84 749 | [nullable]: https://github.com/google/guice/wiki/UseNullable 750 | [optional]: http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html 751 | [jdbc]: http://docs.spring.io/spring/docs/4.0.3.RELEASE/javadoc-api/org/springframework/jdbc/core/JdbcTemplate.html 752 | [jooq]: http://www.jooq.org/ 753 | [dao]: http://www.javapractices.com/topic/TopicAction.do?Id=66 754 | [gradle]: http://gradle.org/ 755 | [intellij]: http://www.jetbrains.com/idea/ 756 | [intellijexample]: http://i.imgur.com/92ztcCd.png 757 | [chronon]: http://blog.jetbrains.com/idea/2014/03/try-chronon-debugger-with-intellij-idea-13-1-eap/ 758 | [eclipse]: https://www.eclipse.org/ 759 | [dagger]: http://square.github.io/dagger/ 760 | [guice]: https://github.com/google/guice 761 | [netbeans]: https://netbeans.org/ 762 | [mat]: http://www.eclipse.org/mat/ 763 | [jmap]: http://docs.oracle.com/javase/7/docs/technotes/tools/share/jmap.html 764 | [jrebel]: http://zeroturnaround.com/software/jrebel/ 765 | [taint]: https://en.wikipedia.org/wiki/Taint_checking 766 | [checker]: http://types.cs.washington.edu/checker-framework/ 767 | [customchecker]: http://types.cs.washington.edu/checker-framework/tutorial/webpages/encryption-checker-cmd.html 768 | [builderex]: http://jlordiales.me/2012/12/13/the-builder-pattern-in-practice/ 769 | [javadocex]: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/ImmutableMap.Builder.html 770 | [dropwizard]: https://dropwizard.github.io/dropwizard/ 771 | [jersey]: https://jersey.java.net/ 772 | [springboot]: http://projects.spring.io/spring-boot/ 773 | [spark]: http://sparkjava.com/ 774 | [assertj]: http://joel-costigliola.github.io/assertj/index.html 775 | [jaxrs]: https://en.wikipedia.org/wiki/Java_API_for_RESTful_Web_Services 776 | [playdoc]: https://www.playframework.com/documentation/2.3.x/Anatomy 777 | [java8datetime]: http://www.oracle.com/technetwork/articles/java/jf14-date-time-2125367.html 778 | [checkedex]: http://docs.oracle.com/javase/7/docs/api/java/lang/Exception.html 779 | [the-worst-mistake-of-computer-science]: https://www.lucidchart.com/techblog/2015/08/31/the-worst-mistake-of-computer-science/ 780 | 781 | 782 | --------------------------------------------------------------------------------