为什么要写这个指南
当你开始接触一个新的平台时,都会从做同一件事开始,通常你会根据你已学的概念或者框架来尝试快速搭建它,但是你无从下手,因为它们通常以全新的名字和方法展现在你面前。
走完这个过程非常耗时,有时甚至让人一筹莫展。这篇指南正是用来帮助那些新手避免此类问题的。
这篇指南我也可以受益, 因为我确定我已经有了错误并且会发现更多的错误,所以你发现任何错误请及时反馈给我。
最好的方式是给我发送pull request。
持续更新
虽然这只是一篇博客, 但是当我碰到新东西的时候我也会即时更新这个页面。文章最顶部有最新的更新日期.
目标人群
这篇指南主要是针对.NET开发者,因为在这里你会发现诸多可以拿来和.NET进行比较的地方。其实从本文的URL你就可以看出些端倪了。话虽如此,我同样也希望这些能对那些初次接触Java平台的非.NET发开者有所帮助。
基础
Java语言,Java环境,Java虚拟机
这三者全完不是一回事。一个是编程语言(想想C#),一个是编程环境(想想.NET开发环境), 另一个则是开发平台(想想CLR)。
不幸的是似乎Java通常被用来指代以上所有。
不要一条路走到黑。虽然我不喜欢Java这样的语言但是整个Java的生态系统却充满了活力同时有许多创新不断发生。作为一名.NET开发者,你应该对NHibernate,NUnit,NLog,NAnt等等非常了解,其实
所有这些都来自于Java的生态系统(把N去掉就是了)。
多语言平台
把JVM想象是CLR。它们是对多语言编程提供跨平台的虚拟机。虽然它们都支持多语言,但是它们之间还是有差别的。
在CLR上,我们主要使用C#,VB.NET(“濒危物种”)和F#,在JVM上就是Java,Scala,Clojure,
Ceylon,Groovy,JRuby和Kotlin,这里只是举几个例子。
JVM字节码
JVM字节码是基于JVM的语言的编译从而在JVM上运行。这和.NET上的IL相似。
跨平台
JVM是100%跨平台的。除了Windows,OSX和Linux,它也可以在许多其它设备上运行。
JVM部署,版本类型和升级版本
JVM有多种实现。最常见的就是在Oracle和OpenJDK中的实现。甚至有一种.NET实现叫做IKVM.NET。
版本类型和升级版本
这可能是目前为止在本指南中最复杂的部分。你根本无法想象连像命名或者版本控制这样简单的事情都能搞砸。这种命名方式甚至使微软的产品都显得合理不少。
让我们开始吧:
版本类型
JRE – Java运行环境。这是用来运行Java应用程序的。但你不能仅仅依靠它来开发运行在JVM上的应用程序。
Java SE (JDK) – Java标准版。它也称为JDK。这是在JVM上开发应用程序最起码的条件。
Java EE – Java企业版。好了,看名字你就懂了。通过它,你可以获得所有有关企业级分布式的东西大规模应用。明确一点的是它包含了Java
SE。
Java ME – Java移动版。这是针对移动电话和便携设备的,它很像.NET微框架。
JavaFX – 代替了Swing,它是Java的主要GUI开发包。同时它(尽管有些争议的话题)也面向RIA的开发。(难道是HTML/JS/CSS不够好吗?)。
可以推断,所有形如Java XY的版本都可以写成JDK的形式。
这里你可以找到更多相关的历史和命名。
升级版本
当前的Java版本是7,Java 8 将于2014年发布。(译者注:Java 8将于2014年3月18日正式发布)
查询你所安装的Java版本和类型
你将会得到如下信息:
java version "1.7.0_40" Java(TM) SE Runtime Environment (build 1.7.0_40-b43) Java HotSpot(TM) 64-Bit Server VM (build 24.0-b56, mixed mode) |
这就是Java 7。为什么这么称呼?非常简单,将1.7.0_40中的1去掉就是7.0_40。其中0_40表示的是更新包。这是Java
7的所有版本发布。
以此类推1.5是Java 5,1.6是Java 6,1.7就是Java 7,所以你猜测一下就知道,Java
8将会是 1.8。
是的,反正我是知道的。
安装Java
一旦你找到了你想要学习的版本,你可以去Oracle的安装指南页面进行下载安装。
如果你想要知道为什么你需要安装Ask Toolbar,不要怪Oracle。显然这是在它和Sun之前达成的协议。我听说一旦期满他们就不会继续履行协议。
应用程序输出又叫Artifacts
无论在.NET上还是在本地应用程序上,编译通常以得到一个可执行文件或者一些动态链接库文件来标志着结束。
而通过Java,你将在输出文件夹中得到若干.class文件。
通常每个类对应一个Java类(当编译Java语言或者其它语言时按照约定转化为字节码)。
这些类是JVM字节码,这非常类似于CLR中的IL。
JAR文件
类文件不会万年不变,你完全可以创建一个JAR文件,只不过就是一个.class文件的压缩包而已。你可以通过你最喜欢的工具来创建JAR文件,或者更简单的方式就是运行
jar cf jar-file input-file(s) |
WAR文件
WAR文件就是一个由Sun公司创造的面向网络应用的JAR文件。它包含了许多类文件和一些附加的元数据以及描述网络服务器(如TomCat)的文件夹。
运行Java应用程序
任何Java程序只能有一个主类在命令提示符中运行。例如:
java <class_containing_main_method> |
你必须在拥有.class的文件夹下运行这段代码。
Classpath
在运行应用程序时,JVM在当前文件夹下寻找所有必要的依赖文件,然后再查找CLASSPATH环境变量所指向的一个或多个文件夹下的.class文件或者JAR文件和ZIP文件。
你可以设置CLASSPATH全局环境变量,当运行一个应用程序时通过使用Java命令并添加一个命令行参数:
java <class_containing_main_method> -cp <class_path> |
每个条目都是用分号隔开的。
构建工具
.NET中有许多构建工具包括MS Build,NAnt,Albacore,Fake等等。JVM也没示弱。虽然许多语言都有它们自己的构建工具例如Clojure中的或者Scala中的SBT,许多语言(包括前面所提到的)可以使用更加标准的构建工具。
Ant
它是XML,NAnt也基于它。这就像MS-Build,尽管它只是XML我仍要强调它。
Maven
Maven非常流行。当你发现项目中有一个pom.xml文件,那这个项目就是Maven了。Maven也会损坏。Maven也是XML。
然而,Maven不仅仅是一个构建工具。它是一个封装系统。这就像.Net中的NuGet,也像Node.js中的NPM。像nuget.org,其实也有maven.org。这类似于“如果不用nuget.org就无法运行”,这种情况在Java系统中也同样存在。
NuGet,你也可以创建你自己的Maven版本库。Artifactory就能让你这么做。
Gradle
Gradle是一个更好的Maven。它基于Groovy所以你可以摆脱复杂的XML并且创建一个更好的依赖关系管理的方法。
我正在尝试更多的使用Gradle。
IntelliJ IDEA的构建
虽然IDE更多的是属于工具部分,IntelliJ IDEA也提供了它自己的构建系统。然而你仅仅能够在合适的环境下使用它,也就是IntelliJ
IDEA和TeamCity的环境。
框架和库
这里有大量的框架和库,我只会对我已经知道的或者我需要的部分进行介绍。如果你有更好的推荐,请给我发一个pull
request。
JSON序列化
Jackson – 我用过它,感觉很好。
单元测试
相当多的单元测试框架:
JUnit – 作为事实上的标准,它相当不错,并且被众多工具支持。
Spek – 放弃。这是我自己的框架,但我仍然在用,值得一提。它能更好地支持DSL,至少我这么认为。
JBehave – Dan North的原始JBehave框架。
TestNG – JUnit的替代品。我用的不多,所以不过多评论。
模拟框架
最近我也没模拟什么框架,但是我我曾经用过一个:
Mockito
日志
SLF4J – 这是在JVM平台上用于记录日志最常见的方式。使用它可以让你(理论上)变更日志并且允许选择你所需要的库。
IoC容器
Guice – 来自Google,我用过它,相当不错。
Spring – 来自Spring框架,我不确定你是否可以不用配置XML就使用它,因为我也没用过。
HTTP客户端
我正使用标准的Apache Commons,也接受更好的选择。
Apache的HTTP客户端 – 我正在使用它。非常需要一个封装器(Wrapper)。
Web框架
许多的Web框架都基于Java Servlet API的通用接口。这类似于OWIN.
应用可以托管在GlassFish,Jetty,Apache TomCat上。
顺便说一下,Oracle宣布它将停止对GlassFish商业版的支持,它的主要传播者,Arun Gupta,最近离开Oracle去了RedHat。现在它让WildFly来作为替代。
对于Web开发而言,一个非常时髦而且轻量级的选择是Vert.x。它基于Netty的基础上构建的,你甚至可以使用不同的语言,如Java,
JavaScript, Ruby。
网络
Netty – 高效的异步事件驱动框架来编写高性能的web应用程序。它是从通信层抽象出来的,所以你可以使用HTTP,Sockets等等。
其它库和工具
JodaTime – Java中时间和日期的管理比.NET还要脆弱。使用JodaTime可以有效解决这个问题。这起源于Jon
Skeet的NodaTime。
Reflections – 使反射更加完美。
Apache Commons – 许多经常用到的小型库。
约定
虽然约定在很大程度上是根据你选择的编程语言来决定,但是或多或少都有一些共同的约定。
命名空间
命名空间是逆序的,换言之,它以顶级域名开始,然后才是公司/组织域名等。
org.hadihariri.spek.runners |
不幸的是,某些时候,它取决于每段是否成为了一个文件夹。那意味着最终你会在GitHub得到:
幸运的是如果你使用了一个不错的IDE,这些都不是问题而且易于管理。
属性和方法名称
如果你原来是学C#的,而现在正在使用像Java,Scala或者Kotlin,约定将以前命名的字段,属性以及方法进行颠覆。它们都以小驼峰(lowerCamelCase)的形式命名。
如果你开始使用Java,可以浏览来进行比较。
工具
当你安装JDK时你可以得到一个编译器(javac)和一个jar创建器(jar),javadoc(用于创建Java文档)以及一些有用的工具。再加上一个文本编辑器你就可以创建并运行一个应用程序了。
关于IDE
Java领域中有三个主要的IDE:
NetBeans
Eclipse
IntelliJ IDEA
这三个都是自由运营的。如果你想要有一些额外的框架支持,IntellIJ
IDEA就有一个最终的商业运行版,换句话说,这有鲜明的企业特点。完整比较
虽然Eclipse可能是最常用的集成开发环境,但我还是选择用IntelliJ
IDEA。如果你想用ReSharper,你一定会喜欢IntelliJ IDEA。当然,你可以说我偏袒它。
持续集成
在持续集成工具的方面,.NET和JVM有诸多相似之处。事实上,它们中的许多都是基于JVM的。
TeamCity – 提供一个免费版。
Jenkins – 是从Hudson中拆分出来的。
随机工具
我会将我发现有用的工具集加入进来。
JRebel – Awesome plugin to IntelliJ
IDEA的一个相当棒的插件,其它IDE的代码可以支持热插拔,换句话说就是可以不经过编译就运行代码。
YourKit – Java分析器
站在Visual Studio用户角度看IntelliJ IDEA
这已经变为自己的指南
在JVM上工作
这部分描述了JVM上的常见情况。大多数例子是基于Kotlin的但是也可以比较容易地适应其它语言。
类加载器
JVM上的类加载器对于这篇指南而言过于庞大,所以我只想简单介绍一下。
在.NET中有一个集合类去加载其它类。在JVM上拥有类加载器。它不会是单个存在,换句话说,类加载器往往多于一个。
用于启动你的应用程序的默认类加载器可以通过使用类加载器中的方法getSystemClassLoader():
val classLoader = ClassLoader.getSystemClassLoader() |
不要尝试去创建一个类加载器的实例,因为它是一个抽象类。有一些类加载器的实现经常使用,其中之一就是URL类加载器。
关于类加载器一个非常重要的观点是,每个类加载器有一个指向父类加载器的属性。
这是如何影响类加载器的呢?当你尝试加载一个类时,JVM首先将会尝试加载父类。如果父类不能加载这个类,那么你自己的类加载器就会进行加载。
类加载器,包括URL类加载器允许你在创建实例时指定一个可供选择的父类。
如果没有被子类没有将类加载成功,那么系统默认的类加载器会根据给定的classpath来进行加载操作。
同时,JVM中的类识别与.NET中很相似,那就意味着从不同的类加载器中加载相同的类将出现不兼容的情况,即使是在相同的链中也是如此。
创建你自己的类加载器
你不仅可以从ClassLoader类中创建你自己的类加载器,而且你也可以改变默认的类加载器,而这些在.NET几乎不可能。
关于类加载器的更多信息
关于类加载器的文章非常多。这里有一些我找到的:
Extensive tutorial on Class Loaders
by Zeroturnaround
The Basics of Java Class Loaders
Ted Newards’s Papers on Finding, Loading
Classes and more
Oracle’s Papers on Java Class Loading
仅使用Google就够了,因为通过它可以找到成千上万关于该主题的博客,文章和论文。
从当前动态类加载模块
在.NET中,想要加载从当前配置中加载一个类,你可以这样做:
var assembly = Assembly.GetExecutingAssembly();
var loadedClass = assembly.GetType("Loader.Customer");
|
加载器是当前用户所在命名空间的集合。这些工作的生命周期和当前集合的类一致。
在JVM中就是:
val classLoader = ClassLoader.getSystemClassLoader()
val loadedClass = classLoader?.loadClass("org.loader.Customer")
|
这是Kotlin的用法,它表示,如果类加载器不为null,就执行这个操作。它是下面写法的简化版:
if (classLoader != null) {
val loadedClass = classLoader.loadClass("org.loader.Customer")
}
|
从另一个模块中加载类
在.NET中,你会这么做:
var assembly = Assembly.LoadFrom(@"C:\Folder\SampleModule.dll");
var loadedClass = assembly.GetType("SampleModule.Customer");
|
在JVM上,你通常使用URL类加载器,将URL序列传递进去,而不是传递文件夹。
使用URL类加载器的好处就是你可以从磁盘和网络等地方加载。
如果你的类作为一个JAR打包,那么你可以这么定义JAR文件名:
val url = URL("file:///path-to-folder/sampleModule.jar")
val urls = array(url)
val classLoader = URLClassLoader.newInstance(urls)
val loadedClass = classLoader?.loadClass("org.sampleModule.Customer")
|
你完全可以使用文件而不是URL,同时指向一个文件,这更加普遍,但之后你需要将文件转为URL:
val file = File("/path-to-folder/sampleModule.jar")
val url = file.toURI().toURL()
|
如果你的类在文件夹中以单独的.class文件形式存在,你要指定文件夹名:
val url = URL("file:///path-to-root-folder/")
val urls = array(url)
val classLoader = URLClassLoader.newInstance(urls)
val loadedClass = classLoader?.loadClass("org.sampleModule.Customer")
|
为什么我没有初始化URL类加载器,而使用newInstance方法?显然它的好处就是如果装有Security
Manager的话就能调用securityManager.checkPackageAccess方法。
在使用目录时以下两点对于解决你的问题非常重要:(不是JAR文件):
确保你传递的URL或文件的后缀正确。
确保你在根文件夹下,那意味着什么呢?当你编译org.sampleModule.Customer这个类时,它产生了一个输出:output-root-folder/org/sampleModule/Customer.class
“.”替换成了“/”。这意味着URL加载器期望你指向根文件夹,而不是命名空间开始的地方。loadClass方法才需要提供完整的命名空间。
感谢@orangy帮助我解决这个麻烦。
会议
近年来即使我不进行更多的Java开发,但是我一直在参加有关Java的会议。 这里有一些我参加会议后认为比较不错的建议和感受:
Devoxx – 最大的版本在安特卫普,但现在在法国和英国。
JAX – 德国的一个长期大型会议。一群水平相当的人的会议!
JavaZone – 这像是没有Java的开发者会。但是主要集中在挪威。
JavaOne – 颇具规模。它是Java领域的技术大会。
这里也有许多关于“跨平台”的会议NDC,QCon,YOW!,GOTO,等等。
变更日志
指南变更记录。
日期 变更
2013年12月30日 更新了JavaFX描述。追加“更改日志”部分
2014年01月06日 更新了标题并且固定添加了IntelliJ IDEA指南的链接
2014年01月09日 追加了类加载部分
|