为新手解释Java项目结构?

时间:2009-12-23 14:16:35

标签: java development-environment

我来自.NET背景,对Java来说是全新的,我正试图了解Java项目结构。

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

MyCompany.SomeApplication.ProjectName

项目名称通常等于项目的根名称空间。如果它是一个大型项目,我可能会进一步打破命名空间,但更多时候我认为不需要进一步命名空间。

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

谢谢!

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

  • .JAR文件是否包含已编译的代码?或者只是压缩的源代码文件?
  • 包装名称是否全部小写有充分的理由吗?
  • 包裹是否具有'循环依赖关系'?换句话说,Package.A可以使用Package.B,反之亦然?
  • 任何人都可以只显示将类声明为包中的典型语法,并声明您希望引用类中的另一个包(可能是using语句吗?)

10 个答案:

答案 0 :(得分:34)

“简单”J2SE项目

正如cletus解释的那样,源目录结构直接等同于包结构,而且它本质上是内置于Java中的。其他一切都不那么明确了。

许多简单的项目都是手工组织的,所以人们可以选择一个他们认为合适的结构。经常做的事情(这也反映在Eclipse中的项目结构,一个非常主要的Java工具)是让你的源树开始于一个名为src的目录中。您的无包源文件将直接位于src中,而您的包层次结构(通常以com目录开头)同样也包含在src中。如果在启动CD编译器之前srcjavac目录,则编译的.class文件将以相同的目录结构结束,每个.class文件都位于同一目录及其.java文件旁边。

如果你有很多源文件和类文件,你会希望将它们彼此分开以减少混乱。手动和Eclipse组织通常将binclasses目录与src平行放置,因此.class文件最终位于与src的层次结构相同的层次结构中。

如果您的项目有一组.jar个文件可以从第三方库提供功能,那么第三个目录(通常为lib)与src和{{1}平行放置}}。 bin中的所有内容都需要放在类路径上进行编译和执行。

最后,有一堆这个和或多或少是可选的:

  • lib
  • 中的文档
  • doc
  • 中的资源
  • resources
  • 中的数据
  • data ...
  • 中的配置

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

J2EE项目

J2EE大致相当于ASP.NET,它是用于组织Web应用程序的大型(标准)框架。虽然您可以按照自己喜欢的方式开发J2EE项目的代码,但Web容器希望您的应用程序交付的结构有一个坚定的标准。而且这种结构也往往会反映出源代码布局。 这是一个详细介绍Java项目的项目结构的页面(它们与我上面写的内容不太一致),特别是J2EE项目:

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

Maven项目

conf是一个非常通用的项目构建工具。就个人而言,Maven很好地满足了我的构建需求,与ant大致相比。另一方面,Maven是完整的生命周期构建管理,依赖于管理依赖性管理。 Java世界中大多数代码的库和源代码都是免费提供给'net,maven,如果问得好的话,它会为你抓取它并把你项目所需的一切带回家,而你甚至不需要告诉它。它也为您管理一个小存储库。

这个高度勤奋的生物的缺点是它对项目结构非常具有法西斯主义的事实。你按照Maven的方式做到或不做。通过强迫其标准,Maven设法使全球项目在结构上更加相似,更易于管理,并且更容易以最少的输入自动构建。

如果您选择Maven,您可以不再担心项目结构,因为只能有一个。就是这样:http://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html

答案 1 :(得分:13)

Java中的包与.Net中的命名空间非常相似。包的名称实际上创建了一个生活在其中的类的路径。此路径可以被视为类的命名空间(在.Net术语中),因为它是您要使用的特定类的唯一标识符。例如,如果您有一个名为:

的包
org.myapp.myProject

在里面你有很多课程:

MyClass1
MyClass2

要具体参考您将使用的那些课程:

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

这和.Net(我知道)之间唯一真正的区别是Java在结构上组织其“名称空间”(每个包是一个不同的文件夹),而.Net允许您使用namespace来定义类的范围关键字并忽略文档实际存在的位置。

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

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

答案 2 :(得分:7)

包很像.Net命名空间。 Java中的一般约定是使用您的反向域名作为包前缀,因此如果您的公司是example.com,您的包可能是:

com.example.projectname.etc...

它可以分解为多个级别,而不仅仅是一个(项目名称),但通常一个就足够了。

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

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

Ant基本上是一种特定于域的脚本语言并且非常灵活,但您最终自己编写了很多样板文件(构建,部署,测试等任务)。它虽然快捷方便。

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

你可以通过这种方式免费获得很多东西。 Maven的真正好处是它可以为你管理你的项目依赖关系,所以使用pom.xml(Maven项目文件)和正确配置的Maven,你可以把它交给其他人(用你的源代码),他们可以构建,部署,测试并自动下载库来运行您的项目。

Ant与Ivy有类似的东西。

答案 3 :(得分:7)

以下是有关Java软件包的一些注意事项,可以帮助您入门:

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

下一级别通常是应用程序或库级别,所以如果它是您的电子商务库,它可能类似于“com.bobswidgets.ecommerce”。

进一步低于通常代表应用程序的体系结构。作为项目核心的类和接口驻留在“根”中,例如com.bobswidgets.ecommerce.InvalidRequestException。

使用包进一步细分功能很常见。通常,模式是将接口和异常放入细分的根,并将实现放入子包中,例如。

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 the intranet project。

Jar文件将容纳将.java代码编译为字节码的结果的类文件。它们只是扩展名为.jar的zip文件。 Jar文件的根是命名空间层次结构的根,例如com.bobswidgets.ecommerce将是/ com / bobswidgets / ecommerce /在Jar文件中。 Jar文件也可以是容器资源,例如财产档案等。

答案 4 :(得分:4)

一个包是一组源文件,它们可以让他们看到彼此的包私有方法和变量,这样这组类就可以访问其他类不能访问的东西了。

期望所有java类都有一个用于消除它们歧义的包。因此,如果您在项目中打开jar文件(如spring),则每个包都以org.springframework开头。类加载器不知道jarfile名称,它们只使用包。

有一种常见的做法是按对象或功能的类型分解,不是每个人都同意这一点。与此处发布的Cletus一样,有将Web控制器,域对象,服务和数据访问对象分组到自己的包中的趋势。我认为一些领域驱动设计人员认为这不是一件好事。它确实具有以下优点:通常包中的所有内容都共享相同类型的依赖项(控制器可能依赖于服务和域对象,服务依赖于域对象和数据访问对象等),因此这样可以很方便。

答案 5 :(得分:3)

好的,在java中你有三种不同类型的access到类成员函数和变量

公共 保护 包私人的 和私人

同一个包中的所有类都可以看到彼此的public,protected和package-private元素。

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

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

他们计划在JDK7中添加一个模块系统到Java(称为Project Jigsaw),并且有一个名为OSGi的现有模块系统。这些模块系统将为您提供比简单封装系统更多的灵活性和功能。

此外,包名称通常都是小写。 :)

答案 6 :(得分:2)

来自维基百科:

  

Java包是一种机制   将Java类组织成   命名空间

  

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

因此,对于包a.b.c,您可以在a,a.b和a.b.c包中使用Java类。通常,在表示相关功能时,将类分组在同一个包中。从功能上讲,同一个包中的类和不同包中的类之间的唯一区别是,Java中成员的默认访问级别是“受包受保护的”,这意味着同一个包中的其他类可以访问。

对于abcMyClass类,如果你想在你的项目中使用MyClass,你会import a.b.c.MyClass或者更少推荐,import a.b.c.*另外,为了让MyClass首先驻留在包abc中,你会在MyClass.java的第一行声明它:package a.b.c;

要做到这一点,你可以JAR整个包(包括包b和c以及类MyClass)并将这个JAR放入你的$CLASSPATH;这将使您可以访问其他源代码(通过上述import语句)。

答案 7 :(得分:2)

回答示例子问题:

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");
   }

}

答案 8 :(得分:2)

  

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

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

  

有没有一个很好的理由为什么包名称都是小写的?

是的,有一个很好的理由可以用小写字母写出包名:有一条指南说明只有类名是用大写字母写的。

  

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

包不相互使用。只有课程。是的,这可能是可能的,但不好的做法。

  

任何人都可以只显示将类声明为包中的典型语法,并声明您希望引用类中的另一个包(可能使用using语句吗?)

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

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

或使用without import(比如你使用两个不同的名为ArrayList的类)

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

答案 9 :(得分:0)

虽然使循环依赖类工作并不容易,但这可能并非不可能。我确实让它在一个案例中工作。 A类和B类相互依赖,不会从头开始编译。但是我意识到A类的一部分不需要B类,而那部分是B类需要完全编译的,我把A类的一部分,B类不需要,以及类的其余部分重新考虑一下A能够编译,然后我能够编译类B.然后我能够解除那个需要类B的A类的部分,并且能够编译完整的类A.然后两个类都正常运行。虽然这不典型,但如果这些类是这样绑在一起的,那么它就是犹太教,有时可能是必要的。只需确保为自己留下特殊的编译说明以便将来更新。