为新手解释Java项目结构?

我来自.NET背景,对于Java来说是全新的,并且正试图让我的头脑围绕Java项目结构。

我典型的.NET解决scheme结构包含的项目表示逻辑上不同的组件,通常使用以下格式命名:

MyCompany.SomeApplication.ProjectName

项目名称通常等于项目的根名称空间。 如果它是一个大项目,我可能会进一步分解命名空间,但是更多的时候我看不到任何进一步的命名空间。

现在在Java中,你有应用程序组成的项目,然后你有一个新的逻辑层次 – 包。 什么是包裹? 它应该包含什么? 你在这个App.Project.Package结构中如何命名空间? JAR在哪里适合这一切? 基本上,有人可以提供一个Java应用程序结构的新手介绍?

谢谢!

编辑:一些真正的破解答案谢谢你们。 随后几个后续问题:

  • .JAR文件是否包含编译好的代码? 或只是压缩的源代码文件?
  • 包名是否都是小写字母有充分的理由吗?
  • 包可以有“循环依赖”? 换句话说,Package.A可以使用Package.B,反之亦然?
  • 任何人都可以显示声明一个类的典型语法是在一个包中,并声明你希望引用另一个包在一个类中(一个using语句也许?)

“简单”的J2SE项目

正如Cletus所解释的那样,源目录结构直接等同于包结构,并且基本上内置于Java中。 其他一切都不太清楚。

很多简单的项目都是通过手工组织的,所以人们可以select一个他们感觉良好的结构。 通常做的事情(这也反映在Eclipse中一个非常占优势的Java工具的项目结构上)是让源码树在一个名为src的目录中开始。 你的无包文件源文件将直接放在src中,你的包层次结构(通常以com目录开头)同样包含在src 。 如果在启动javac编译器之前将CD放入src目录中,则编译后的.class文件将以相同的目录结构结束,每个.class文件位于相同的目录中,并位于.java文件旁边。

如果你有很多的源文件和类文件,你需要把它们分开,以减less混乱。 手动和Eclipse组织通常将一个bin或者classes目录放到src这样.class文件就会以一个反映src的层次结构。

如果您的项目有一组.jar文件来提供第三方库的function,那么第三个目录(通常是lib )将与srcbin并行放置。 lib所有东西都需要放在classpath中进行编译和执行。

最后,还有一些这个或多或less是可选的:

  • doc中的doc
  • resources
  • 数据中的data
  • configuration在conf

你明白了。 编译器不关心这些目录,它们只是你自己组织(或混淆)的方法。

J2EE项目

J2EE大致相当于ASP.NET,这是一个组织Web应用程序的巨大(标准)框架。 虽然你可以用任何你喜欢的方式开发你的J2EE项目代码,但是Web容器将会期望你的应用程序提交的结构有一个坚实的标准。而且这个结构也会反映到源代码布局中。 下面是一个详细介绍Java项目的项目结构的页面(他们与我上面写的不一致),特别是J2EE项目:

http://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html

Maven项目

Maven是一个非常灵活的项目构build工具。 就我个人而言,我的构build需求很好地被ant满足,这大致与nmake 。 另一方面,Maven是一个完整的生命周期pipe理系统,依赖pipe理已经被大量使用。 Java世界中的大部分代码的库和源代码在“net”中是免费的,如果问得好的话,maven将会为你抓取它,并且把你的项目需要的东西带回家,甚至不需要你去告诉它。 它也为你pipe理一个小库。

这个高度勤劳的小动物的缺点是,这是一个项目结构高度法西斯的事实。 你做Maven的方式,或根本没有。 通过强迫自己的标准落到实处,Maven设法使全球的项目在结构上更加相似,更易于pipe理,并且以最less的投入自动构build。

如果你selectMaven,你可以停止担心项目结构,因为只能有一个。 这是它: http : //maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html

Java中的包与.NET中的名称空间非常相似。 包的名字本质上是创build一个到它里面的类的path。 这个path可以被认为是类的名字空间(用.Net术语来说),因为它是你要使用的特定类的唯一标识符。 例如,如果您有一个名为:

 org.myapp.myProject 

在里面你有一堆课:

 MyClass1 MyClass2 

要特别指出您将使用的那些类:

 org.myapp.myProject.MyClass1 org.myapp.myProject.MyClass2 

这和.Net(我知道)之间唯一真正的区别是,Java在结构上组织它的“命名空间”(每个包是一个独立的文件夹),而.Net允许你使用namespace关键字来限定类,并忽略文档的实际位置住。

在大多数情况下,JAR文件大致类似于DLL。 这是一个压缩文件(可以用7zip打开)包含其他项目的源代码,这些项目可以在应用程序中作为依赖项添加。 库通常包含在JAR中。

关于Java的事情要记住的是非常结构化的; WHERE文件的存在很重要。 当然,还有更多的故事,然后我发布,但我认为这应该让你开始。

一个包很像.Net命名空间。 在Java中的一般惯例是使用反向域名作为包前缀,所以如果你的公司是example.com,你的包可能是:

 com.example.projectname.etc... 

它可以分解成许多层次,而不仅仅是一个(项目名称),但通常是足够的。

在你的项目结构中,类通常被分成逻辑区域:控制器,模型,视图等等。这取决于项目的types。

Java中有两个主要的构build系统:Ant和Maven。

Ant基本上是一个特定于领域的脚本语言,并且非常灵活,但是最终你自己写了很多样板文件(构build,部署,testing等等)。 这虽然快捷方便。

Maven更现代,更完整,值得使用(imho)。 Maven与Ant的不同之处在于Maven声明这个项目是一个“Web应用程序项目”(称为原型 )。 一旦声明了,一旦你指定了groupId(com.example)和artifactId(项目名称),目录结构就被强制执行了。

这样可以免费得到很多东西。 Maven的真正好处在于它可以通过一个pom.xml(Maven项目文件)来pipe理你的项目依赖关系,并且正确configurationMaven,你可以把它交给别人(用你的源代码),并且可以构build,部署,testing并自动下载库来运行你的项目。

ant常春藤得到这样的东西。

下面是关于Java包的一些注意事项,应该让你开始:

Java软件包名称的最佳做法是使用组织的域名作为软件包的开始,反之,例如,如果您的公司拥有域名“bobswidgets.com”,则可以使用“com”来启动软件包。 bobswidgets”。

下一个级别通常是应用程序或库级别,所以如果是电子商务库,则可能是“com.bobswidgets.ecommerce”。

进一步比这往往代表了你的应用程序的体系结构。 作为项目核心的类和接口驻留在“root”中,例如com.bobswidgets.ecommerce.InvalidRequestException。

使用包进一步细分function是很常见的。 通常这种模式是将接口和exception放到细分的根部,并将其实现到子包中

 com.bobswidgets.ecommerce.payment.PaymentAuthoriser (interface) com.bobswidgets.ecommerce.payment.PaymentException com.bobswidgets.ecommerce.payment.paypal.PaypalPaymentAuthoriser (implementation) 

这使得将“付款”类和包放入他们自己的项目中变得非常容易。

其他一些说明:

Java包与目录结构紧密耦合。 因此,在一个项目中,一个包含com.example.MyClass的类将永远在com / example / MyClass.java中。 这是因为当它被打包成一个Jar时,类文件肯定会在com / example / MyClass.class中。

Java包与项目松散耦合。 项目将拥有自己独特的软件包名称是很常见的,例如com.bobswidgets.ecommerce for ecommerce,com.bobswidgets.intranet for intranet project。

Jar文件将容器编译你的.java代码成字节码的结果的类文件。 他们只是扩展名为.jar的zip文件。 Jar文件的根目录是命名空间层次结构的根目录,例如com.bobswidgets.ecommerce将在Jar文件中为/ com / bobswidgets / ecommerce /。 Jar文件也可以容器资源,例如属性文件等

一个包是一组源文件,可以让他们看到对方的包 – 私有方法和variables,以便这组类可以访问其他类不能访问的东西。

期望的是所有的java类都有一个用来消除歧义的包。 所以如果你在你的项目中打开一个jar文件,比如spring,那么每个包都以org.springframework开始。 类加载器不知道jarfile名称,他们只使用包。

按照对象或function的types来分解东西是一种普遍的做法,并不是每个人都同意这一点。 就像在这里发布的Cletus一样,将Web控制器,域对象,服务和数据访问对象分组到他们自己的包中是一种趋势。 我认为一些领域驱动devise人员不认为这是一件好事。 它的优势在于,通常包中的所有内容都具有相同的依赖关系(控制器可能依赖于服务和域对象,服务依赖于域对象和数据访问对象等),因此可以很方便。

好的,所以在Java中,你有三种不同types的类成员函数和variables的访问

公共保护包 – 私人和私人

同一个包中的所有类都可以看到其他公共,受保护和包私有的元素。

包在系统中不是分层的。 通常它们是以分层的方式组织的,但就运行时而言,com.example.widgets是一个与com.example.widgets.cogs完全不同的包

包被安排为目录,这有助于保持组织结构:文件结构总是与包结构相似。

他们正在计划在JDK7中为Java添加一个模块系统(称为Project Jigsaw ),并且存在一个名为OSGi的现有模块系统。 这些模块系统将为您提供更多的灵活性和function,然后是简单的包系统。

而且,软件包名称通常都是小写字母。 🙂

维基百科:

Java包是一种将Java类组织到名称空间中的机制

Java包可以存储在称为JAR文件的压缩文件中

所以对于包abc,你可以在a,ab和abc包中包含Java类。 通常,当它们表示相关的function时,它们将同一个包中的类分组。 在function上,同一个包中的类和不同包中的类之间的唯一区别在于,Java中成员的默认访问级别是“包保护”的,这意味着同一包中的其他类可以访问。

对于类abcMyClass,如果你想在你的项目中使用MyClass,你可以import abcMyClass或者不推荐使用import abc*另外,MyClass首先驻留在包abc中,你可以在MyClass的第一行声明它.java: package abc;

要做到这一点,你可以JAR整个包(包括包B和C和类MyClass),并把这个JAR到你的$CLASSPATH ; 这将使您的其他源代码可以使用(通过上述导入语句)。

回答这个例子子问题:

 package com.smotricz.goodfornaught; import java.util.HashMap; import javax.swing.*; public class MyFrame extends JFrame { private HashMap myMap = new HashMap(); public MyFrame() { setTitle("My very own frame"); } } 

.JAR文件是否包含编译好的代码? 或只是压缩的源代码文件?

它们可能包含两种,甚至完全不同的文件,如图片。 首先是一个zip文件。 大多数情况下,您会看到包含类文件的JAR以及包含源文件(如果您使用第三方代码时在IDE中进行debugging便利)或包含javadoc(源代码documentatin)的JAR,如果IDE支持对文档进行工具提示当你访问lib的函数。

包名是否都是小写字母有充分的理由吗?

是的,软件包名称用小写字母写有一个很好的理由:有一个指导说,只有类名是用大写字母写在前面。

包可以有“循环依赖”? 换句话说,Package.A可以使用Package.B,反之亦然?

包不互相使用。 只有class才能做到。 是的,这可能是可能的,但不好的做法。

任何人都可以显示声明一个类的典型语法是在一个包中,并声明你希望引用另一个包在一个类中(一个using语句也许?)

假设您想要使用java.util包中的ArrayList类,请使用

  import java.util.ArrayList; ArrayList myList = new ArrayList(); 

或者在没有导入的情况下使用(比如说你使用了两个不同的包,

  java.util.ArrayList myList = new java.util.ArrayList(); your.package.ArrayList mySecondList = new your.package.ArrayList(); 

虽然循环依赖类并不是一件容易的事情,但也不是不可能的。 我确实得到它在一个案件的工作。 A类和B类彼此依赖,不会从头开始编译。 但是认识到A类的一部分不需要B类,那部分是B类需要完全编译的,我把A类的那部分,B类不需要的部分,剩下的部分类A能够编译,然后我能够编译B类。然后,我可以解除需要B类的A类部分,并且能够编译完整的类A.这两个类都可以正常工作。 虽然这不是典型的,但是如果这些class级像这样连在一起的话,那就是犹太教,有时可能是必要的。 只要确保你留下自己特殊的编译指示,以便将来的更新。