评论删除后,数据将无法恢复
Java is one of the most popular programming languages around, but no one seems to enjoy using it. Well, Java is actually an alright programming language, and since Java 8 came out recently, I decided to compile a list of libraries, practices, and tools to make using Java better.
This article is on Github. Feel free to contribute and add your own Java tips and best practices.
Style
Javadoc
The Builder Pattern
Structs
Dependency injection
Avoid Nulls
Immutable-by-default
Avoid lots of Util classes
Formatting
Streams
Deploying
Dependency Convergence
Frameworks
Maven
Continuous Integration
Maven repository
Configuration management
Libraries
jUnit 4
jMock
AssertJ
Apache Commons
Guava
Gson
Java Tuples
Joda-Time
Lombok
Play framework
SLF4J
jOOQ
Missing Features
Testing
Tools
Chronon
IntelliJ IDEA
JRebel
The Checker Framework
Eclipse Memory Analyzer
Resources
Books
Podcasts
Traditionally, Java was programmed in a very verbose enterprise JavaBean style. The new style is much cleaner, more correct, and easier on the eyes.
One of the simplest things we as programmers do is pass around data. The traditional way to do this is to define a JavaBean:
public class DataHolder { private String data; public DataHolder() { } public void setData(String data) { this.data = data; } public String getData() { return this.data; }}
This is verbose and wasteful. Even if your IDE automatically generated this code, it's a waste. So, don't do this.
Instead, I prefer the C struct style of writing classes that merely hold data:
public class DataHolder { public final String data; public DataHolder(String data) { this.data = data; }}
This is a reduction in number of lines of code by a half. Further, this class is immutable unless you extend it, so we can reason about it easier as we know that it can't be changed.
If you're storing objects like Map or List that can be modified easily, you should instead use ImmutableMap or ImmutableList, which is discussed in the section about immutability.
If you have a rather complicated object that you want to build a struct for, consider the Builder pattern.
You make a subclass in your object which will construct your object. It uses mutable state, but as soon as you call build, it will emit an immutable object.
Imagine we had a more complicated DataHolder. The builder for it might look like:
public class ComplicatedDataHolder { public final String data; public final int num; // lots more fields and a constructor public static class Builder { private String data; private int num; public Builder data(String data) { this.data = data; return this; } public Builder num(int num) { this.num = num; return this; } public ComplicatedDataHolder build() { return new ComplicatedDataHolder(data, num); // etc } }}
Then to use it:
final ComplicatedDataHolder cdh = new ComplicatedDataHolder.Builder() .data("set this") .num(523) .build();
There are better examples of Builders elsewhere but this should give you a taste for what it's like. This ends up with a lot of the boilerplate we were trying to avoid, but it gets you immutable objects and a very fluent interface.
This is more of a software engineering section than a Java section, but one of the best ways to write testable software is to use dependency injection(DI). Because Java strongly encourages OO design, to make testable software, you need to use DI.
In Java, this is typically done with the Spring Framework. It has a either code-based wiring or XML configuration-based wiring. If you use the XML configuration, it's important that you don't overuse Spring because of its XML-based configuration format. There should be absolutely no logic or control structures in XML. It should only inject dependencies.
Good alternatives to using Spring is Google and Square's Daggerlibrary or Google's Guice. They don't use Spring's XML configuration file format, and instead they put the injection logic in annotations and in code.
Try to avoid using nulls when you can. Do not return null collections when you should have instead returned an empty collection. If you're going to use null, consider the @Nullable annotation. IntelliJ IDEA has built-in support for the @Nullable annotation.
If you're using Java 8, you can use the excellent new Optional type. If a value may or may not be present, wrap it in an Optional class like this:
public class FooWidget { private final String data; private final Optional<Bar> bar; public FooWidget(String data) { this(data, Optional.empty()); } public FooWidget(String data, Optional<Bar> bar) { this.data = data; this.bar = bar; } public Optional<Bar> getBar() { return bar; }}
So now it's clear that data will never be null, but bar may or may not be present. Optional has methods like isPresent, which may make it feel like not a lot is different from just checking null. But it allows you to write statements like:
final Optional<FooWidget> fooWidget = maybeGetFooWidget(); final Baz baz = fooWidget.flatMap(FooWidget::getBar) .flatMap(BarWidget::getBaz) .orElse(defaultBaz);
Which is much better than chained if null checks. The only downside of using Optional is that the standard library doesn't have good Optional support, so dealing with nulls is still required there.
Unless you have a good reason to make them otherwise, variables, classes, and collections should be immutable.
Variable references can be made immutable with final:
final FooWidget fooWidget;if (condition()) { fooWidget = getWidget();} else { try { fooWidget = cachedFooWidget.get(); } catch (CachingException e) { log.error("Couldn't get cached value", e); throw e; }}// fooWidget is guaranteed to be set here
Now you can be sure that fooWidget won't be accidentally reassigned. The finalkeyword works with if/else blocks and with try/catch blocks. Of course, if thefooWidget itself isn't immutable you could easily mutate it.
Collections should, whenever possible, use the Guava ImmutableMap,ImmutableList, or ImmutableSet classes. These have builders so that you can build them up dynamically and then mark them immutable by calling the build method.
Classes should be made immutable by declaring fields immutable (via final) and by using immutable collections. Optionally, you can make the class itself final so that it can't be extended and made mutable.
Be careful if you find yourself adding a lot of methods to a Util class.
public class MiscUtil { public static String frobnicateString(String base, int times) { // ... etc } public static void throwIfCondition(boolean condition, String msg) { // ... etc }}
These classes, at first, seem attractive because the methods that go in them don't really belong in any one place. So you throw them all in here in the name of code reuse.
The cure is worse than the disease. Put these classes where they belong, or if you must have common methods like this, consider Java 8's default methods on interfaces. Then you could lump common actions into interfaces. And, since they're interfaces, you can implement multiple of them.
public interface Thrower { default void throwIfCondition(boolean condition, String msg) { // ... } default void throwAorB(Throwable a, Throwable b, boolean throwA) { // ... }}
Then every class which needs it can simply implement this interface.
Formatting is so much less important than most programmers make it out to be. Does consistency show that you care about your craft and does it help others read? Absolutely. But let's not waste a day adding spaces to if blocks so that it "matches".
If you absolutely need a code formatting guide, I highly recommendGoogle's Java Style guide. The best part of that guide is theProgramming Practices section. Definitely worth a read.
Documenting your user facing code is important. And this means using examples and using sensible descriptions of variables, methods, and classes.
The corollary of this is to not document what doesn't need documenting. If you
don't have anything to say about what an argument is, or if it's obvious,
don't document it. Boilerplate documentation is worse than no documentation at
all, as it tricks your users into thinking that there is documentation.
Java 8 has a nice stream and lambda syntax. You could write code like this:
final List<String> filtered = list.stream() .filter(s -> s.startsWith("s")) .map(s -> s.toUpperCase());
Instead of this:
final List<String> filtered = Lists.newArrayList();for (String str : list) { if (str.startsWith("s") { filtered.add(str.toUpperCase()); }}
This allows you to write more fluent code, which is more readable.
Deploying Java properly can be a bit tricky. There are two main ways to deploy Java nowadays: use a framework or use a home grown solution that is more flexible.
Because deploying Java isn't easy, frameworks have been made which can help. Two of the best are Dropwizard and Spring Boot. The Play framework can also be considered one of these deployment frameworks as well.
All of them try to lower the barrier to getting your code out the door. They're especially helpful if you're new to Java or if you need to get things done fast. Single JAR deployments are just easier than complicated WAR or EAR deployments.
However, they can be somewhat inflexible and are rather opinionated, so if your project doesn't fit with the choices the developers of your framework made, you'll have to migrate to a more hand-rolled configuration.
评论删除后,数据将无法恢复
评论(68)
引用来自“肖滔”的评论
Java中概念太多,做起事来啰嗦,像php多简洁引用来自“Yattin_Vong”的评论
^_^不是一个重量级的嘛引用来自“肖滔”的评论
Java在web开发中,已经失败。引用来自“virusyang”的评论
一味喷语言优越性的人要么是不懂 要么就是最底层的引用来自“南湖船老大”的评论
恰恰相反,Java在web开发中相当成功。当然如果PHPER的眼光只是局限在wordpress,dedecms这类网站就算了引用来自“肖滔”的评论
Java中概念太多,做起事来啰嗦,像php多简洁引用来自“TuWei”的评论
Php 太随意…代码可读性远没Java 好,再说你凭啥说Java web失败,就因为不能快速建垃圾站吗引用来自“肖滔”的评论
Java一坨坨的类,做一个功能,一陀代码,错误一坨坨看不清楚,真不知道好在哪引用来自“TuWei”的评论
我还真不知道php 比java更容易debug,再说了,你不知道java可以直接用jsp写程序吗引用来自“肖滔”的评论
Java中概念太多,做起事来啰嗦,像php多简洁引用来自“TuWei”的评论
Php 太随意…代码可读性远没Java 好,再说你凭啥说Java web失败,就因为不能快速建垃圾站吗引用来自“肖滔”的评论
Java一坨坨的类,做一个功能,一陀代码,错误一坨坨看不清楚,真不知道好在哪引用来自“肖滔”的评论
Java中概念太多,做起事来啰嗦,像php多简洁引用来自“TuWei”的评论
Php 太随意…代码可读性远没Java 好,再说你凭啥说Java web失败,就因为不能快速建垃圾站吗