Java package的概念及类加载与运行

在了解任何其它的语法之前,我们首先要理解,如何组织你的JAVA源文件。我们以后将编写越来越多的JAVA源文件,难道把它们全部放到一个目录下面吗?当然不是这样的。在源代码变得混乱不可维护之前,我们首先要了解代码的组织方法。我们可以利用JAVA中“包(package)”的概念来组织你的代码。Package这种概念实际上跟JAVA中的安全模型有关,但安全模型不是我们现在要了解的,这些东西以后在适当的时候,我会跟大家讲清楚的。现在,我们理解“包”这种概念,它就是一个目录树结构。

 

观察操作系统里面的目录结构,你就能理解“包”这个概念的作用。

 

创建一个类(Test01.java), 先不声明包,编译和运行,然后声明一个包:mypkgs.examples,并放到一个目录树下面:

然后编译和运行,观察它的错误!

java.lang.NoClassDefFoundError: Test01 (wrong name: mypkgs/examples/Test01)

 

为什么呢?当我们在一个类中声明了一个包,那么在运行的时候,必须指定这个类的全路径类名!java mypkgs.examples.Test01这样的全路径类名来运行它!观察错误:

java.lang.NoClassDefFoundError: mypkgs/examples/Test01

 

这种异常,大家以后肯定会经常碰到,它的出现跟很多因素有关。以后,我们会逐步了解所有的机制!

 

如果我们要运行一个声明了包的类,必须为这个包创建一个目录结构,并把这个类(.class文件)放到那个目录下面,并到包的根目录下用全路径类名来运行它!

 

如果自行创建一个目录结构太麻烦的话,那么我们可以在编译的时候,指定编译器自动帮我们根据包结构创建一个目录结构如何?【用javac –d 命令编译并运行】

-d选项是指定类的输出目录的根

 

如果不指定-d选项,那么编译器会把.class文件放在跟.java文件一个目录下!

 

扩展思考:

 

l           包的命名

那么,我们通常是怎么创建这样的一个目录结构的呢?虽然你可以随意创建一些目录来存放你的代码,但为了不让其它人把你当成“菜鸟”,我们需要遵守一定的编程规范,这些编程规范是由SUN公司制定的,并被业界众多JAVA程序员遵守的写程序通用原则。这些原则不是强制性的规则,而只是参考意见,你可以不遵守这些原则,它不会导致你的程序不能运行,只不过如果你遵守这些原则的话,会显得更加专业,而且容易阅读和理解。这些通用原则包括:如何给一个类起名字;如何给一个变量起名字;如何创建包结构;如何写代码注释……等等,大家以后写程序的时候,应该强迫自己遵守这些约定,并让它成为你的一种习惯。就是说,到你不需要在心里告诉自己如何去定义这些类//变量等,而是非常自然的就按照习惯去定义的时候,你才算“出师”了! J

 

请参考:http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html 阅读有关的编码原则。

 

包的命名通常是小写字母,一般以com/org等开头,然后是公司/组织名称,然后是项目名称等。

 

SayHello.java放到一个包中。

 

我们把SayHello放到了某个目录下面,我们以source目录为根,以后的代码都放在这个根目录下面,现在,我们编译SayHello.java

 

这个source目录,我们称之为“源代码目录”,在这个目录下面,将按照包的规则,创建子目录,然后把JAVA源文件放到某个目录下面。这样,我们必须更改SayHello.java源文件,在它的第一行,加上包的声明:package cn.com.leadfar.learning;

 

演示编译运行

 

l           在哪里执行javac.exe以编译代码?

n         可以在任何目录下执行javac.exe

n         只要在编译的时候,指定了正确的.java文件路径即可,比如在source下面编译可以用:javac cn/com/leadfar/learning/ SayHello.java 命令来编译;或者可以直接进入learning目录,运行:javac SayHello.java来编译

n         编译之后,.class文件被放置到和.java文件一样的目录中

n         因为没有指定.class文件输出到别的目录,所以,现在.java源代码文件和.class文件都在一起,这种情况下,“源代码目录”同时也是“类路径目录

n         所谓“类路径目录”,即类存放的根目录(在根目录下,按照类的包结构创建子目录来存放!

u       进入source的父目录,创建classes目录

u       运行:

u       JavaSE-02>javac -d classes source/cn/com/leadfar/learning/SayHello.java

u       表示将.class文件输出到classes目录(请观察classes目录中的生成内容!)

n         这个classes,即类路径目录,类路径(CLASSPATH)的概念稍后解释

l           如何以及在哪里执行java.exe以运行SayHello类?

n         如果一个类名被放置在一个包里面,那么我们运行的时候必需指定类的全路径类名

n         java cn.com.leadfar.learning.SayHello

n         那么这个命令应该在哪里运行呢,我们应该在source目录中运行

n         在别的目录中是否可以运行?

u       尝试一下,转到上一级目录运行java命令(演示)

u       Exception in thread "main" java.lang.NoClassDefFoundError: cn/com/leadfar/learning/SayHello

u       Caused by: java.lang.ClassNotFoundException: cn.com.leadfar.learning.SayHello

u               at java.net.URLClassLoader$1.run(URLClassLoader.java:202)

u               at java.security.AccessController.doPrivileged(Native Method)

u               at java.net.URLClassLoader.findClass(URLClassLoader.java:190)

u               at java.lang.ClassLoader.loadClass(ClassLoader.java:307)

u               at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)

u               at java.lang.ClassLoader.loadClass(ClassLoader.java:248)

u       Could not find the main class: cn.com.leadfar.learning.SayHello.  Program will exit.

u       为什么会出现类找不到的异常呢?那是因为java虚拟机在当前运行目录中找不到cn/com/leadfar/learning这样的目录结构,自然也找不到类了。

u       如果不指定CLASSPATH环境变量,java虚拟机会在“工作目录”下寻找相应的类,所谓工作目录,缺省情况下,就是在哪个目录运行java命令,哪个目录就是“工作目录”。

u       现在,“工作目录”就是source目录的父目录

u       所以,现在我们需要指定CLASSPATH环境变量

l           在别的目录中执行其它目录中的类?CLASSPATH的概念

n         CLASSPATH的概念

u       虚拟机使用类加载器将类加载到内存中运行

u       虚拟机根据CLASSPATH环境变量所指定的路径,来按顺序搜索及加载类

u       CLASSPATH是一个目录或.jar文件的列表,用“;”分号分隔(操作系统不同,分隔符也不同,比如在Linux下面用“:”冒号来作为分隔符)

n         如何指定CLASSPATH

u       java –classpath source cn.com.leadfar.learning.SayHello,将正确运行

l           更进一步的思考:可不可以不指定CLASSPATH,而运行SayHello - 工作目录的概念

u       回答:指定“工作目录”

l         缺省的工作目录是当前运行java程序所在的目录

l         可以用-Duser.dir=xxx来指定工作目录

l         java -Duser.dir=source cn.com.leadfar.learning.SayHello,即可

u       理解工作目录的概念,有利于理解CLASSPATH

u       经常有人习惯将CLASSPATH定义为包含“.”,即一个英文句点,表示当前目录的意思,这个所谓的“当前目录”,就是“工作目录”,而不一定是运行java.exe所在的目录

u       进入source目录,运行:

u       java cn.com.leadfar.learning.SayHelloOK

u       java –Duser.dir=. cn.com.leadfar.learning.SayHelloOK

u       java –Duser.dir=com cn.com.leadfar.learning.SayHelloERROR

l         出现类没找到的错误(原因是没有指定CLASSPATH,缺省以工作目录进行搜索)

u       java –Duser.dir=com –classpath . cn.com.leadfar.learning.SayHelloERROR

l         出现类没找到的错误(原因是虽然指定了CLASSPATH,但它是一个相对路径,这相对的概念是相对于工作目录而言,而不是当前运行java.exe这个目录)

u       java –Duser.dir=com –classpath .. cn.com.leadfar.learning.SayHelloOK

u       java –Duser.dir=com –classpath D:/xx/xx/source cn.com.leadfar.learning.SayHelloOK

l         这回指定了绝对路径作为CLASSPATH,它就与工作目录无关了!

u       java –Duser.dir=com/a/b/c/d –classpath D:/xx/xx/source cn.com.leadfar.learning.SayHelloOK

l         工作目录,实际上可以是任何值(一个实际不存在的目录也可以,但最好不要这样干)

n         要注意,类路径的定义方法是-classpath加空格,然后加路径列表,而不是-classpath=路径列表,路径列表,用“;”分号分隔!

n         我们SayHello类中引入的另外一个类System在哪里?

u       JAVA中一个一个类,就像一个一个动态链接库一样,在需要的时候,JVM才会去加载

u       System这个类,在这里:C:/Program Files/Java/jdk1.6.0_18/jre/lib/rt.jar

l         一个jar扩展名的文件,它是一系列.class文件(也可以包含其它想包含的文件)压缩之后形成的一个包

l         一个jar扩展名的文件,可以作为CLASSPATH的一部分

l         rt.jar这个包,包含了JavaSE标准API的所有类,我们在JAVA API文档中能够查找到的类,都在这个包里。

u       为什么没有把rt.jar这个包加到我们的CLASSPATH中?

l         因为它由JVM自动加载,而且它的位置是固定的,无需指定

l           更加深层次的再次思考:可不可以既不指定CLASSPATH,也不指定工作目录,而仍然能够运行SayHello

n         Java虚拟机运行一个类的时候,是否能够加载一个类,取决于类加载器是否能够找到这个类!类加载器的概念,后面会深入探讨,现在只从字面上去理解它。类加载器就是加载一个类用的!缺省情况下,JAVA虚拟机会依次使用以下三个类加载器来搜索和加载一个类:启动类加载器、扩展类加载器、类路径类加载器!

u       启动类加载器就是搜索rt.jar包(实际上就是一个压缩文件,里面包含很多类文件),尝试从其中加载一个类,如果没有找到,则

u       通过扩展类加载器,搜索ext目录(扩展目录,注意这个目录在JDK下面的JRE下)中所有的jar包,搜索这些jar包中是否有要加载的类,如果没有找到,则

u       通过类路径类加载器,搜索类路径中所有的类,如果还是没有找到,则抛出异常!

u       所以答案就是:利用JAVA中的“扩展目录”。

u       JAVA中的扩展目录是:C:/Program Files/Java/jdk1.6.0_18/jre/lib/ext

u       在这个扩展目录jar包中包含的类,也无需使用CLASSPATH指定,即可自动加载

u       所以,我们只要把SayHello类打成jar包,并把它拷贝到这个目录下即可

l         我们进入source目录,执行以下命令:

l         jar cvf sayhello.jar .

l         表示把当前目录下的所有内容打包成sayhello.jar文件

l         jar.exe这个命令位于JDK开发工具包下的bin目录

l         或我们进入source目录的父目录,执行以下命令:

l         jar cvf sayhello.jar –C source . 也可以打包成sayhello.jar

l          

u       打包,拷贝到扩展目录中,我们进入任何一个目录,都可以运行:

l         java cn.com.leadfar.learning.SayHello

 

总结,我们从“包”的概念出发,描述了:

l           如何将源代码按照一定的包结构来进行组织

l           如果源代码被放到一个包中,则必须在代码中增加package声明

l           理解源代码目录(即存放源代码的目录)

l           如果源代码被放到一个包中,编译时(javac),需指定路径

l           如果源代码被放到一个包中,编译后,.class文件将与源文件在同一个目录结构中存放

l           对于被放到某个包中的类,运行的时候,需要使用全路径类名,理解全路径类名的概念

l           理解“工作目录”、“类路径”、“扩展目录”的概念,及其联系

l           使用jar命令,打包.class文件

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值