如何写好Java程序呢

Maven仍然是编译,打包,运行测试的标准化工具。还有其它一些选择,比如Gradle,不过它们的采用程度远不Maven。如果你之前没用过Maven,你可以看下这个Maven的使用示例。

我喜欢用一个根POM文件来包含所有的外部依赖。它看起来就像是这样。这个根POM文件只有一个外部依赖,不过如果你的产品很大的话,你可能会有很多依赖。你的根POM文件自己就应该是一个项目:它有版本控制,并且和其它的Java项目一样进行发布。

你的所有Maven工程都应该包含你的主POM文件以及所有的版本信息。这样的话,你能知道公司项目的每个外部依赖所选择的版本,以及所有正确的Maven插件。如果你需要引入一个外部依赖的话,大概是这样的:

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

如果你需要进行内部依赖的话,应该在项目的段中单独进行维护。不然的话,主POM文件的版本号就要疯涨了。

依赖收敛

Java的一个最好的地方就是有大量的第三方库,它们无所不能。几乎每个API或者工具都有相应的Java SDK,并且可以很容易地引入到Maven中来。

所有的这些Java库自身可能又会依赖一些特定的版本的其它类库。如果你引入了大量的库,可能会出现版本冲突 ,比如说像这样:

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

你的工程应该引入哪个版本?

有了Maven的依赖收敛的插件后,如果你的依赖版本不一致的话,编译的时候就会报错。那么你有两种解决冲突的方案:

在dependencyManagement区中显式地选择某个版本的bar。Foo或者Widget都不要依赖Bar。

到底选择哪种方案取决你的具体情况: 如果你想要跟踪某个工程的版本,不依赖它是最好的。另一方面,如果你想要明确一点,你可以自己选择一个版本,不过这样的话,如果更新了其它的依赖,也得同步地修改它。

持续集成

很明显你需要某种持续集成的服务器来不断地编译你的SNAPSHOT版本,或者对Git分支进行构建。

Jenkins和Travis-CI是你的不二选择。

代码覆盖率也很重要,Cobertura有一个不错的Maven插件,并且对CI支持的也不错。当然还有其它的代码覆盖的工具,不过我用的是Cobertura。

Maven库

你需要一个地方来存储你编译好的jar包,war包,以及EAR包,因此你需要一个代码仓库。

常见的选择是Artifactory或者Nexus。两个都能用,并且各有利弊。

你应该自己进行Artifactory/Nexus的安装并且将你的依赖做一份镜像。这样不会由于下载Maven 库的时候出错了导到编译中断。

配置管理

那现在你的代码可以编译了,仓库也搭建起来了,你需要把你的代码带出开发环境,走向最终的发布了。别马虎了,因为自动化执行从长远来看,好处是大大的。

Chef,Puppet,和Ansible都是常见的选择。我自己也写了一个可选方案,Squadron。这个嘛,当然了,我自然是希望你们能下载下它的,因为它比其它那些要好用多了。

不管你用的是哪个工具,别忘了自动化部署就好。

可能Java最好的特性就是它拥有的这些库了。下面列出了一些库,应该绝大多数人都会用得上。

Java的标准库,曾经还是很不错的,但在现在看来它也遗漏掉了很多关键的特性。

Apache Commons

Apache Commons项目有许多有用的功能。

Commons Codec有许多有用的Base64或者16进制字符串的编解码的方法。别浪费时间自己又写一遍了。Commons Lang是一个字符串操作,创建,字符集,以及许多工具方法的类库。Commons IO,你想要的文件相关的方法都在这里了。它有FileUtils.copyDirectory,FileUtils.writeStringToFile, IOUtils.readLines,等等。Guava

Guava是一个非常棒的库,它就是Java标准库”所缺失的那部分”。它有很多我喜欢的地方,很难一一赘述,不过我还是想试一下。

Cache,这是一个最简单的获取内存缓存的方式了,你可以用它来缓存网络访问,磁盘访问,或者几乎所有东西。你只需实现一个CacheBuilder,告诉Guava如何创建缓存就好了。不可变集合。这里有许多类:ImmutableMap, ImmutableList,甚至还有ImmutableSortedMultiSet,如果这就是你想要的话。

我还喜欢用Guava的方式来新建可变集合:

1234567// Instead offinalMap<String,Widget>map=newHashMap<String,Widget>();// You can usefinalMap<String,Widget>map=Maps.newHashMap();

有许多像Lists, Maps, Sets的静态类,他们都更简洁易懂一些。

如果你还在坚持使用Java 6或者7的话,你可以用下Collections2,它有一些诸如filter和transform的方法。没有Jvaa 8的stream的支持,你也可以用它们来写出连贯的代码。

Guava也有一些很简单的东西,比如Joiner,你可以用它来拼接字符串,还有一个类可以用来处理中断。

Gson

Google的Gson是一个简单高效的JSON解析库。它是这样工作的:

123456finalGson gson=newGson();finalStringjson=gson.toJson(fooWidget);finalFooWidget newFooWidget=gson.fromJson(json,FooWidget.class);

真的很简单,使用它会感觉非常愉快。Gson的用户指南中有更多的示例。

Java Tuples

我对Java一个不爽的地方就是它的标准库中居然没有元组。幸运的是,Java tuples工程解决了这一问题。

它也很容易使用,并且真的很赞:

123456Pair<String,Integer>func(Stringinput){// something…returnPair.with(stringResult,intResult);}

Joda-Time

Joda-Time是我用过的最好的时间库了。简直,直接,容易测试。你还想要什么?

这个库里我最喜欢的一个类就是Duration,因为我用它来告诉说我要等待多长时间,或者过多久我才进行重试。

Lombok

Lombok是一个非常有趣的库。它通过注释来减少了Java中的饱受诟病的样板代码(注:setter,getter之类的)。

想给你类中的变量增加setter, getter方法?太简单了:

12345publicclassFoo{@Getter@Setter privateintvar;}

现在你可以这么写了:

1234finalFoofoo=newFoo();foo.setVar(5);

这里还有更多的示例。我还没在生产代码中用过Lombok,不过我有点等不及了。

Play框架

备选方案:Jersey或者Spark

在Java中实现REST风格的WEB服务有两大阵营:JAX-RS和其它。

JAX-RS是传统的方式。你使用像Jersey这样的东西来将注解和接口,实现组合到一起来实现WEB服务。这样做的好处就是,你可以通过一个接口就能很容易创建出一个调用的客户端来。

Play框架是在JVM上实现WEB服务的截然不同的一种方式:你有一个routes文件,然后你去实现routes中那些规则所引用到的类。它其实就是个完整的MVC框架,不过你可以只用它来实现REST服务。

它同时支持Java和Scala。它优先使用Scala这点可能有点令人沮丧,但是用Java进行开发的话也非常不错。

如果你习惯了Python里的Flask这类的微框架,那么你应该会对Spark感到很熟悉。有了Java 8它简直如虎添翼。

SLF4J

Java打印日志有许多不错的解决方案。我个人最喜欢的是SLF4J,因为它是可挺插拔的,并且可以同时混合不同的日志框架中输出的日志。有个奇怪的工程同时使用了java.util.logging, JCL, 和log4j?没问题,SLF4J就是为它而生的。

想入门的话,看下它的这个两页的手册就足够的了。

jOOQ

我不喜欢很重的ORM框架,因为我喜欢SQL。因此我写了许多的JDBC模板,但它们很难维护。jOOQ是个更不错的解决方案。

你可以在Java中以一种类型安全的方式来书写SQL语句:

12345678910// Typesafely execute the SQL statement directly with jOOQResult<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();

将它以及DAO模式结合起来,你可以让数据库访问变得更简单。

测试

测试对软件来说至关重要。下面这些库能让测试变得更加容易。

jUnit 4

jUnit就不用介绍了。它是Java中单元测试的标准工具。

不过可能你还没有完全发挥jUnit的威力。jUnit还支持参数化测试,以及能让你少写很多样板代码的测试规则,还有能随机测试代码的Theory,以及Assumptions。

jMock

如果你已经完成了依赖注入,那么它回报你的时候来了:你可以mock出带副作用的代码(就像和REST服务器通信那样),并且仍然能对调用它的代码执行断言操作。

jMock是Java中标准的mock工具。它的使用方式是这样的:

1234567891011121314151617181920publicclassFooWidgetTest{privateMockerycontext=newMockery();@TestpublicvoidbasicTest(){finalFooWidgetDependencydep=context.mock(FooWidgetDependency.class);context.checking(newExpectations(){oneOf(dep).call(with(any(String.class)));atLeast(0).of(dep).optionalCall();});finalFooWidgetfoo=newFooWidget(dep);Assert.assertTrue(foo.doThing());context.assertIsSatisfied();}}

这段代码通过jMock设置了一个FooWidgetDependency ,然后添加了一些期望的操作。我们希望dep的call方法被调用一次而dep的optionalCall 方法会被调用0或更多次。

如果你反复的构造同样的FooWidgetDependency,你应该把它放到一个测试设备(Test Fixture)里,然后把assertIsSatisfied放到一个@After方法中。

AssertJ

你是不是用jUnit写过这些?

1234567finalList<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"));

这些样板代码有点太聒噪了。AssertJ解决了这个问题。同样的代码可以变成这样:

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

连贯接口让你的测试代码可读性更强了。代码如此,夫复何求?

工具IntelliJ IDEA

备选方案: Eclipse and Netbeans

最好的Java IDE当然是 IntelliJ IDEA。它有许多很棒的特性,我们之所以还能忍受Java这些冗长的代码,它起了很大的作用。自动补全很棒,< a href=”http://i.imgur.com/92ztcCd.png” target=”_blank”>代码检查也超赞,重构工具也非常实用。

免费的社区版对我来说已经足够了,不过在旗舰版中有许多不错的特性比如数据库工具,Srping框架的支持以及Chronon等。

Chronon

GDB 7中我最喜欢的特性就是调试的时候可以按时间进行遍历了。有了IntelliJ IDEA的Chronon插件后,这个也成为现实了。当然你得是旗舰版的。

你可以获取到变量的历史值,跳回前面执行的地方,获取方法的调用历史等等。第一次使用的话会感觉有点怪,但它能帮忙你调试一些很棘手的BUG。

JRebel

持续集成通常都是SaaS产品的一个目标。你想想如果你甚至都不需要等到编译完成就可以看到代码的更新?

这就是JRebel在做的事情。只要你把你的服务器挂到某个JRebel客户端上,代码一旦有改动你马上就能看到效果。当你想快速体验一个功能的话,这个的确能节省不少时间。

验证框架

Java的类型系统是相当弱的。它不能区分出普通字符串以及实际上是正则的字符串,也不能进行

12345678910<ahref="http://en.wikipedia.org/wiki/Taint_checking"target="_blank”&gt;污点检查&lt;/a&gt;。而验证框架替它做了。&lt;/p&gt; &lt;p&gt;它使用像@Nullable这样的注解来检查类型,你可以定义自己的注解来让静态代码分析的威力变得更加强大。&lt;/p&gt; &lt;h4&gt;Eclipse Memory Analyzer&lt;/h4&gt; &lt;p&gt;在Java中也会发生内存泄漏。幸运的是,有这类的专门的工具。解决这类问题的最佳工具我觉得就是Eclipse Memory Analyzer了。它能dump出堆的内存,然后帮助你找出泄露的原因:&lt;/p&gt; &lt;p&gt;从JVM进程中dump出堆内存有许多方法,我个人用的是jmap:&lt;/p&gt; &lt;div="><code>$jmap-dump:live,format=b,file=heapdump.hprof-F8152Attaching toprocess ID8152,please wait…Debugger attached successfully.Server compiler detected.JVM version is23.25-b01Dumping heap toheapdump.hprof……snip…Heap dump file created</code></a>

那风再温柔。太深的流连便成了一种羁绊,

如何写好Java程序呢

相关文章:

你感兴趣的文章:

标签云: