package
对于package的理解,我目前有两个想法,第一配合Access Modifier(protected和默认的两种访问方式),第二为了便于解决命名冲突并实现方便的组织文件(这和Python的模块组织方式的目的是一样的)。第二种,解决命名冲突的同时,将文件(夹)路径与import语句导入的包/文件所在的路径进行映射,便于梳理清楚项目的结构,便于开发和管理。
Python,C++都有环境变量可以设置,Java也有,如CLASSPATH, JAVA_HOME。但一定要设置这些环境变量吗?对于CLASSPATH,可以选择不设置,毕竟可以用参数-classpath, -cp来补充;至于JAVA_HOME,目前还没有了解过。不过这应该是好习惯吧。在Ubuntu下:
JAVA_HOME=/usr/lib/jvm/java-6-openjdk
CLASSPATH=.:/usr/lib/jvm/java-6-openjdk/lib
我尝试了不设置环境变量的情况。如果在当前工作目录下有:
ClassB.java pkg/ClassA.java
在pkg/ClassA.java中的第一行有 package pkg; 即说明ClassA.java文件是含在pkg包中的。
在ClassB.java 中第一行有 import pkg.*; 即导入了pkg这个包中的所有内容(当前只有ClassA这个文件)。
这时可以编译并运行:javac pkg/ClassA.java ; javac ClassB.java;java ClassB。一切OK。
需要明确的点:
1) ClassA.java中的package语句所能体现的路径应该与ClassA.java文件所在的路径一致,并且这与ClassB.java的import语句要导入的包的路径是一致的。
2) 还是要说说配置环境变量的益处,如果不配置,那么当所有的工作内容都在当前目录下时,因为当前目录是默认在classpath中的,所以仍然可以工作;但是如果需要引入一些自己编写的,或者第三方的包时,将面临问题,如果不将它们拷贝到同一个工作目录,那么将无法有效的导入。
3) 如果有一个目录./myWork在CLASSPATH中,并且如果有一个包mypkg在myWork下,而mypkg下面的文件都是该包的文件,即文件开头都有package语句:package mypkg; 那么我们在其他目录下导入mypkg下面的内容时,之需要导入myWork下面的路径,即import mypkg.Pkg.*,也即导入语句不包含已经在CLASSPATH中的myWork。
4) 导入较深的目录的包。对于路径比较深的包,假如在我们已经加入CLASSPATH的目录./myWork下,有路径 ./myjava/mypkg/pkg,而pkg是一个package;那么在其他工作目录下,当我们需要导入这个比较深的包时,结合1)和3),我们需要注意的有,a) pkg包中的文件使用的package语句为 package myjava.mypkg.pkg; b) 导入文件中使用的import语句为 import myjava.mypkg.pkg; 。
5) 包中文件的相互导入。目前的测试看来不存在“相对路径”的方法,即使是某一层次的父目录下的.java文件中import 当前子目录所对应的包,也要使用子目录所对应的全路径(从CLASSPATH中包含的那个“根”目录下面开始算起),而不是某种相对路径。稍微搜索了一下,但是没有发现有效的信息,待进一步的学习,或许可以知道。
应该还是要了解一下CLASSPATH的,但这里偷个懒,留作以后在学习吧。
import
直接引一个链接上的原文吧,链接地址为http://blog.chinaunix.net/uid-25756163-id-276244.html。
import只是一种让你偷点懒少打字的方法,绝对不会影响你的classpath,没有非用import不可的理由,用了import也不会起到类似c里面嵌入某文件内容的效果,它只是一种省事的办法。不在classpath中的class,任你再import也无济于事。
如果你不用import,你用ArrayList这个类,就需要写 java.util.ArrayList。而用了import java.util.ArrayList;的话以后代码中写ArrayList就可以了,省事。
import可以使用通配符*,*代表某package下所有的class,不包括子目录。import java.awt.* 不等于 import java.awt.*;import java.awt.event.*;如果你要简写java.awt.event下和java.awt下的类,你不能偷懒,两个都要import。
接下来是另外一篇中的内容,来自 http://www.knowsky.com/363549.html 的最后几段:
不管你有没有使用import指令,存在目前目录下的类都会被编译器优先采用,只要它不属于任何package。这是因为编译器总是先假设您所输入的类名就是该类的全名(不属于任何package),然后-classpath所指定的路径中搜索属于该类的.java文件或.class文件,在这里可以知道default package的角色非常非凡。
必须明确告诉编译器我们用到哪个package下的类,导入时或在包名称.类名称中进行引用。导入某个包时,一定要进行-classpath路径指定某个包的位置。你假如指定了多个路径话,假如在一个路径下已经找到了该包话,就优先引用该包的类。
当java编译器开始编译某个类的源代码时,首先它会做一件事情,这就是建立“类路径引用表”,它是根据参数-classpath或classpath环境变量来建立的。假如没有指定选项-classpath或环境变量CLASSPATH时,缺省情况下类路径引用表只有一条记录,即当前的目录(“.”)。环境变量CLASSPATH的内容会被选项-classpath所覆盖,没有累加效果。当编译器将类路径引用表建立好之后,接着编译。
OK,这里犯懒,只做了一个简单的测试:
如果myjava目录所在的路径已经在CLASSPATH中了,然后我们有package pkg在../myjava/mypkg/pkg的路径下,然后有ClassA.java和ClassA.class在pkg下。这时,如果在另一个工作目录下,我们有ClassE.java,这个文件中没有import 语句,但是有使用ClassA的语句,如 mypkg.pkg.ClassA a = new mypkg.pkg.ClassA(); 是同样可以正常工作的。
所以总结下来应该有:
1) java编译器在编译时,建立“类路径引用表”,来自CLASSPATH环境变量或者 -classpath参数;默认包含当前目录,即' . '。
2) 不一定要用import语句,使用 path1.path2.path3.ClassA 的方式也可以达到使用指定类的目的,path1.path2.path3.ClassA的方式,其实也是告诉编译器按照 path1/path2/path3/ 的目录路径去找 ClassA.class 文件。当然,你可以把这个路径所在的“根目录”加到CLASSPATH或者-classpath中,然后import,就不用每次引用一大长串了。这就是import的作用吧。
3) 很遗憾,通配符*竟然只能匹配当前目录下的文件,而不包含子目录,也就是说' * ' 其实指代的是 ' *.class '。