Java package从实践到理论【转】

我常用:现在在aaa上层目录   package  aaa;     javac -d . *.java       将*.java编译到aaa内

Java package从实践到理论【转】

1,什么都别说,先跟着我来做一把
我们先找一个目录,比如C:/myjob
然后我们建立两个目录,一个叫做src,一个叫做bin
C:/myjob>md src
C:/myjob>md bin
C:/myjob>dir
驱动器 C 中的卷是 LIGHTNING
卷的序列号是 3DD1-83D9
C:/myjob 的目录
2005-12-25 14:33 <DIR> .
2005-12-25 14:33 <DIR> ..
2005-12-25 14:34 <DIR> src
2005-12-25 14:34 <DIR> bin
0 个文件 0 字节
4 个目录 305,123,328 可用字节
C:/myjob>
然后我们在src目录中去写程序
C:/myjob>cd src
C:/myjob/src>
我们写这么4个java文件
A.java
package com.lightning;
public class A{
{System.out.println(”com.lightning.A”);}
}
B.java
package com.lightning;
public class B{
{System.out.println(”com.lightning.B”);}
}
C.java
package com;
public class C{
{System.out.println(”com.C”);}
}

Test.java
package net.test;
import com.lightning.*;
import com.*;
public class Test{
public static void main(String[] args)
{
new A();new B();new C();
System.out.println(”net.test.Test”);
}
}
写好之后就是这样

C:/myjob/src>dir
驱动器 C 中的卷是 LIGHTNING
卷的序列号是 3DD1-83D9

C:/myjob/src 的目录

2005-12-25 14:34 <DIR> .
2005-12-25 14:34 <DIR> ..
2005-12-25 14:39 86 A.java
2005-12-25 14:40 86 B.java
2005-12-25 14:42 194 Test.java
2005-12-25 14:43 68 C.java
4 个文件 434 字节
2 个目录 305,106,944 可用字节

然后我们建立com目录,com/lightning/目录,net/test/目录
C:/myjob/src>md com
C:/myjob/src>md com/lightning
C:/myjob/src>md net/test/
我们将Test.java放入net/test/中去
将A.java,B.java放入com/lightning/中去
将C.java放入com/中去
C:/myjob/src>move Test.java net/test/
C:/myjob/src>move A.java com/lightning/
C:/myjob/src>move B.java com/lightning/
C:/myjob/src>move C.java com/

然后我们在c:/myjobs中发令:
C:/myjob/src>cd ..
C:/myjob>javac -sourcepath src -d bin src/net/test/Test.java
然后我们看看bin目录中多了什么
C:/myjob>dir bin /s
驱动器 C 中的卷是 LIGHTNING
卷的序列号是 3DD1-83D9

C:/myjob/bin 的目录

2005-12-25 14:34 <DIR> .
2005-12-25 14:34 <DIR> ..
2005-12-25 14:46 <DIR> net
2005-12-25 14:46 <DIR> com
0 个文件 0 字节

C:/myjob/bin/net 的目录

2005-12-25 14:46 <DIR> .
2005-12-25 14:46 <DIR> ..
2005-12-25 14:46 <DIR> test
0 个文件 0 字节

C:/myjob/bin/net/test 的目录

2005-12-25 14:46 <DIR> .
2005-12-25 14:46 <DIR> ..
2005-12-25 14:46 520 Test.class
1 个文件 520 字节

C:/myjob/bin/com 的目录

2005-12-25 14:46 <DIR> .
2005-12-25 14:46 <DIR> ..
2005-12-25 14:46 <DIR> lightning
2005-12-25 14:46 338 C.class
1 个文件 338 字节

C:/myjob/bin/com/lightning 的目录

2005-12-25 14:46 <DIR> .
2005-12-25 14:46 <DIR> ..
2005-12-25 14:46 354 A.class
2005-12-25 14:46 354 B.class
2 个文件 708 字节

所列文件总数:
4 个文件 1,566 字节
14 个目录 305,057,792 可用字节

然后我们执行,同样在c:/myjobs/下发令
C:/myjob>java -cp bin net.test.Test
com.lightning.A
com.lightning.B
com.C
net.test.Test

2,从实践到理论

刚才我用一个非常简单但是非常完整的例子给大家演示了java的package机制。
为什么以前脑海里面那么简单的javac会搞得这么复杂呢?

实际上它本就这么复杂,
并不是一个.java,一行javac一个当前目录中的class这么简单。

首先我要打破一些东西,然后才好建立一些东西。
javac并非只是给一个.java编译一个class的。javac自带有make机制,也就是说,如果在
javac的参数中java文件使用到的任何类,javac首先会去找寻这个类的class文件存在与否
,如果不存在,就会在sourcepath中找寻源代码文件来编译。

什么是sourcepath呢?sourcepath是javac的一个参数,如果你不加指定,那么sourcepath
就是classpath。

比如我们装好jdk之后,我说过不要设定classpath环境变量,因为大部分人一旦设定了
classpath,不是多此一举把什么dt.jar放进去。(我可以好好打击你一下,告诉你一个可
悲的事实–jre永远不会从你这个classpath中去寻找dt.jar。你完全是徒劳的!)就是
把”.”搞不见了,搞得是冷水一盆盆的往自己身上泼,脑袋一点点的涨大。

不要设!classpath没有你想象中那么普适和强大,它只是命令行的简化替代品。
你不设的话它就是”.”。

回到sourcepath,sourcepath指的是你源代码树的存放地点。
为什么是源代码树?而不是一个目录的平板源代码呢?
请大家将原本脑海中C的编译过程完全砸掉!
java完全不同,java没有头文件,每个.java都是要放在源代码树中的。
那么这颗树是怎么组织的呢?
对了,就是package语句。
比如写了package com.lightning;
那么这个.java就必须放在源代码树根/的com/lighting/之下才行。

很多浮躁的初学者被default打包方式宠坏了。自我为中心,以为java就是一套库,自己写
的时候最多import进来就行了,代码从不打包,直接javac,直接java,多么方便。
孰不知自己写的这个.java也不过是java大平台的其中一个小单元而已。如果不打包,
我写一个Point,你写一个Point,甚至更有甚者敢于给自己的类起名叫String等等。
全部都在平板式的目录中,那jre该选哪一个?

一旦要使用package语句,就要使用代码树结构,当然,你要直接javac,也行。
不过javac出来的这个class要放在符合package结构的目录中才能发挥正常作用,否则就是
废物一坨。
而且,如果你这个.java还用到其它自己写的有package语句的.java,那这个方法就回天乏
术了。

按照sun的想象,应该是写好的.java放在符合package结构的目录中,package语句保证了
正确放置,如果目录位置和package语句中指示的不同,则会出错。

所以按照刚才我们的那种package写法,我们必然要将那几个.java文件放入相应的目录中
才能让javac顺利找到他们来make。

有人说javac可不可以像java那样 java aaa.bbb.c…java?
不可以
javac中的那个.java文件参数必须是一个文件系统的路径文件名形式。
然后如果用到其它的.java,javac会根据目前的sourcepath出发寻找目录结构中的那些
java文件。

当然了,既然打了包,在使用的时候。
要么写全名–包名.类名
或者使用import
不得不提的是,import就好比c++的using,它不负责做文件操作,它只是方便你写代码。

另外import语句中的*代表的是类名,不代表包名片断。
你import com.*;
编译器仍然找不到com.lightning中的任何类。
反之亦然。
就好象你告诉编译器,我这里面要用到姓诸葛的人。
那么姓诸的人当然编译器不会认为也包含在内。

所以,如果程序一旦写到一定规模。
就不得不使用ant来管理这些。
或者使用IDE,否则jdk就真的变成了java developer killer。

但是对于初学者,在了解为什么会有ant之类的之前,还是要体会一下使用
jdk的艰辛。

这个和以前在unix上开发的人用gcc命令行到后来使用make之后使用ide
之类的时候的体会是类似的。

最后,javac的-d参数是指示编译出来的class文件放在哪里的,如果你不指定的话,它们
和.java混在一起。当然也符合目录结构。

 

 

///

Java中的package真让人头疼啊,挺简单的例子也会错,郁闷啊!今天有点时间,干脆把能想到的几种情况都试试,还好成功了,赶快记下来,免得忘了。

  所有.java文件都在C:/Java/myjava里。我用的是UltraEdit+命令行。比较原始哦!

例1.

 

文件HelloWorld.java

package hello;

public class HelloWorld

{

  public static void main(String[] args)

  {

    System.out.println("Hello World!");

  }

}

C:/Java/myjava/hello>javac HelloWorld.java

C:/Java/myjava>java HelloWorld 或 C:/Java/myjava>java hello.HelloWorld

 

例2.

文件MyClass.java

package mypackage;

public class MyClass

{

  public MyClass()

  {

    System.out.println("Create an object of MyClass.");

  }

}

文件UsePkg.java

import mypackage.*;

public class UsePkg

{

  public static void main(String[] args)

  {

    MyClass obj = new MyClass();

  }

}

C:/Java/myjava/mypackage>javac MyClass.java

C:/Java/myjava>javac UsePkg.java

C:/Java/myjava>java UsePkg

 

例3.

1) 其实两个文件在一个文件夹里,默认就是可以访问的。

C:/Java/myjava/mypackage>javac MyClass.java

C:/Java/myjava/mypackage>javac UsePkg.java

C:/Java/myjava/mypackage>java UsePkg

2) 不过为了熟悉package的用法,还是用吧,这样比较有成就感嘛。

文件MyClass.java同上

文件UsePkg.java

package mypackage;

public class UsePkg

{

  public static void main(String[] args)

  {

    MyClass obj = new MyClass();

  }

}

C:/Java/myjava/mypackage>javac MyClass.java

C:/Java/myjava/mypackage>javac -classpath C:/Java/myjava UsePkg.java

C:/Java/myjava>java mypackage.UsePkg

C:/Java/myjava>java UsePkg  //这是不行的

 

例4.

文件MyClass.java与文件UsePkg.java同例2。

C:/Java/myjava/mypackage>javac MyClass.java

C:/Java/myjava/user>javac -classpath C:/Java/myjava UsePkg.java

C:/Java/myjava/user>java -classpath .;C:/Java/myjava UsePkg

.代表当前目录,如果是在其它目录下,在classpath中要写全,即:

D:/>java -classpath C:/Java/myjava/user;C:/Java/myjava UsePkg

 

  当然,上面的classpath参数也可以在环境变量classpath中设置,只是修改完后需要重启。

 








 

以下内容的测试条件是你的机器上,设置了path命令PATH= D:/JDK1.4/BIN;D:/JDK1.4/LIB;,可以正常执行Java和javac命令,不用设置classpath路径的情况下。

 


 


    从一个简单的例子谈谈package与import机制

 

基本原则:为什么需要将Java文件和类文件切实安置到其所归属之Package所对应的相对路径下。

 

为什么要这样做呢?假如你在程序中,用到打包命令package,并且直接编译和执行该程序。例如:以下面程序为例:

package a.b.c;

public class hello

{

  public static void main(String args[])

  {   

   System.out.println("Hello the world!");

  }

}

此程序可以编译通过,但是执行时,却提示以下错误!

D:/my/xdj>javac hello.java


 


D:/my/xdj>java hello

Exception in thread "main" java.lang.NoClassDefFoundError: hello (wrong name: a/

b/c/hello)

        at java.lang.ClassLoader.defineClass0(Native Method)

        at java.lang.ClassLoader.defineClass(ClassLoader.java:537)

        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:12

3)

        at java.net.URLClassLoader.defineClass(URLClassLoader.java:251)

        at java.net.URLClassLoader.Access$100(URLClassLoader.java:55)

        at java.net.URLClassLoader$1.run(URLClassLoader.java:194)

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

        at java.net.URLClassLoader.findClass(URLClassLoader.java:187)

        at java.lang.ClassLoader.loadClass(ClassLoader.java:289)

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

        at java.lang.ClassLoader.loadClass(ClassLoader.java:235)

        at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:302)


 


D:/my/xdj>


 


在xdj目录下建立一个/a/b/c子目录把hello.java放在它下面,用以下命令进行编译和执行时,可正常通过!


 


D:/my/xdj>javac d:/my/xdj/a/b/c/hello.java


 


D:/my/xdj>java a.b.c.hello

Hello the world!


 


D:/my/xdj>


 


再看下面另外一种情况,先让我们在xdj目录下建立两个文件a.java和b.java文件,其内容如下。

a.java文件内容:

import a.b.c.*;

public class a

{

   public static void main(String[] args)

     {

       b b1=new b();

       b1.print();

       }


 


}


 

 

 

 

 


a.java文件内容:

package a.b.c;

public class b

{

   public  void print()

     {

      System.out.println("我是被调用子类的程序输出呀!");

       }

}


 


直接编译a.java文件时,会提示以下错误!


 


D:/my/xdj>javac a.java

a.java:1: package a.b.c does not exist

import a.b.c.*;

^

a.java:6: cannot access b

bad class file: ./b.java

file does not contain class b

Please remove or make sure it appears in the correct subDirectory of the classpa

th.

       b b1=new b();

       ^

2 errors


 


D:/my/xdj>

接下来,我们把b.java移到xdj/a/b/c/下,并把/xdj目录下的b.java删除掉呀!重新执行编译指令,这次肯定可以编译成功!你可以发现b.java也同时被编译过了,这就是所谓的make编译方式。


 


D:/my/xdj>javac a.java


 


D:/my/xdj>


 


提示1:假如你在/xdj目录下仍保留一个b.java文件的话,执行对主程序的编译命令时仍会报错!你自己可以试试呀!

提示2:假如你删除/xdj/a/b/c/b.java文件的话,保留b.class文件,执行对主程序的编译命令时是可以通过,此地可以不需要子程序的源代码。


 


提出一个问题:假如把目录/a/b/c全部剪切到其它目录,如D盘根目录下,在/xdj目录假如执行编译和执行命令呢?

 

很明显,会报以下错误!当然了,前提条件是你没有设置classpath路径,其实只要没把类搜索路径设置到我这个位置就会出错的!你试试吧!


 


D:/my/xdj>javac a.java

a.java:1: package a.b.c does not exist

import a.b.c.*;

^

a.java:6: cannot resolve symbol

symbol  : class b

location: class a

       b b1=new b();

       ^

a.java:6: cannot resolve symbol

symbol  : class b

location: class a

       b b1=new b();

                ^

3 errors


 


D:/my/xdj>java a

Exception in thread "main" java.lang.NoClassDefFoundError: a/b/c/b

        at a.main(a.java:6)


 


D:/my/xdj>

解决的办法可以用以下命令即可正常编译和执行:


 


D:/my/xdj>javac -classpath  d:/   a.java


 


D:/my/xdj>java -classpath  d:/;a

我是被调用子类的程序输出呀!


 


D:/my/xdj>


 


提示3:-classpath参数,缺省是以当前目录为根基目录的,即不带-classpath参数的情况下。

提示4:使用java.exe还是javac.exe,最好明确指定-classpath选项,可设置环境变量CLASSPATH即可,同时设置了-classpath参数和环境变量classpath时,会以-classpath参数为主的。假如在它们所指定的路径或JAR文件中存有package名称和类名称相同的类,会引起混淆的!


 


假如你在D盘的根目录生成一个打包文件a.zip,其内容目录a/b/c/下的所有文件的话,你也可以用下面命令进行编译和执行。

D:/my/xdj>javac -classpath d:/a.zip a.java


 


D:/my/xdj>java -classpath d:/a.zip;. a

我是被调用子类的程序输出呀!


 


D:/my/xdj>


 


以上讨论就暂告一段落吧!假如你还想进一步了解package与import机制话,哪你可以继续往下看下去的。


 


深入分析package与import机制部分

 

不管你有没有使用import指令,存在目前目录下的类都会被编译器优先采用,只要它不属于任何package。这是因为编译器总是先假设您所输入的类名就是该类的全名(不属于任何package),然后-classpath所指定的路径中搜索属于该类的.java文件或.class文件,在这里可以知道default package的角色非常非凡。

必须明确告诉编译器我们用到哪个package下的类,导入时或在包名称.类名称中进行引用。导入某个包时,一定要进行-classpath路径指定某个包的位置。你假如指定了多个路径话,假如在一个路径下已经找到了该包话,就优先引用该包的类。

当java编译器开始编译某个类的源代码时,首先它会做一件事情,这就是建立“类路径引用表”,它是根据参数-classpath或classpath环境变量来建立的。假如没有指定选项-classpath或环境变量CLASSPATH时,缺省情况下类路径引用表只有一笔记录,即当前的目录(“.”)。环境变量CLASSPATH的内容会被选项-classpath所覆盖,没有累加效果。

   当编译器将类路径引用表建立好之后,接着编译
文章来源: 动态网站制作(www.knowsky.com) 出处:http://www.knowsky.com/363549.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值