建设唯一无二的包名
各人或者已留意到这样一个事实:由于一个包永远不会真的“封装”到单唯一个文件内里,它可由多个.class文件组成,所以排场大概稍微有些杂乱。为制止这个问题,最公道的一种做法就是将某个特定包利用的所有.class文件都置入单个目次里。也就是说,我们要操作操纵系统的分级文件布局制止呈现杂乱排场。这正是Java所采纳的要领。
它同时也办理了另两个问题:建设唯一无二的包名以及找出那些大概深藏于目次布局某处的类。正如我们在第2章报告的那样,为到达这个目标,需要将.class文件的位置路径编码到package的名字里。但按照约定,编译器强迫package名的第一部门是类建设者的因特网域名。由于因特网域名必定是唯一无二的(由InterNIC担保——注释②,它节制着域名的分派),所以如果按这一约定行事,package的名称就必定不会反复,所以永远不会碰着名称斗嘴的问题。换句话说,除非将本身的域名转让给其他人,并且对方也凭据沟通的路径名编写Java代码,不然名字的斗嘴是永远不会呈现的。虽然,假如你没有本身的域名,那么必需缔造一个很是生僻的包名(譬喻本身的英文姓名),以便尽最大大概建设一个唯一无二的包名。如抉择刊行本身的Java代码,那么强烈推荐去申请本身的域名,它所需的用度长短常低廉的。
②:ftp://ftp.internic.net
这个能力的另一部门是将package名理会本钱身呆板上的一个目次。这样一来,Java措施运行并需要装载.class文件的时候(这是动态举办的,在措施需要建设属于谁人类的一个工具,可能首次会见谁人类的一个static成员时),它就可以找到.class文件驻留的谁人目次。
Java表明器的事情措施如下:首先,它找到情况变量CLASSPATH(将Java可能具有Java表明本领的东西——如欣赏器——安装到呆板中时,通过操纵系统举办设定)。CLASSPATH包括了一个或多个目次,它们作为一种非凡的“根”利用,从这里展开对.class文件的搜索。从谁人根开始,表明器会寻找包名,并将每个点号(句点)替换成一个斜杠,从而生成从CLASSPATH根开始的一个路径名(所以package foo.bar.baz会酿成foo\bar\baz可能foo/bar/baz;详细是正斜杠照旧反斜杠由操纵系统抉择)。随后将它们毗连到一起,成为CLASSPATH内的各个条目(进口)。今后搜索.class文件时,就可从这些处所开始查找与筹备建设的类名对应的名字。另外,它也会搜索一些尺度目次——这些目次与Java表明器驻留的处所有关。
为进一步领略这个问题,下面以我本身的域名为例,它是bruceeckel.com。将其反转过来后,com.bruceeckel就为我的类建设了唯一无二的全局名称(com,edu,org,net等扩展名以前在Java包中都是大写的,但自Java 1.2以来,这种环境已产生了变革。此刻整个包名都是小写的)。由于抉择建设一个名为util的库,我可以进一步地支解它,所以最后获得的包名如下:
package com.bruceeckel.util;
此刻,可将这个包名作为下述两个文件的“定名空间”利用:
//: Vector.java // Creating a package package com.bruceeckel.util; public class Vector { public Vector() { System.out.println( "com.bruceeckel.util.Vector"); } } ///:~
建设本身的包时,要求package语句必需是文件中的第一个“非注释”代码。第二个文件外貌看起来是雷同的:
//: List.java // Creating a package package com.bruceeckel.util; public class List { public List() { System.out.println( "com.bruceeckel.util.List"); } } ///:~
这两个文件都置于我本身系统的一个子目次中:
C:\DOC\JavaT\com\bruceeckel\util
若通过它往回走,就会发明包名com.bruceeckel.util,但路径的第一部门又是什么呢?这是由CLASSPATH情况变量抉择的。在我的呆板上,它是:
CLASSPATH=.;D:\JAVA\LIB;C:\DOC\JavaT
可以看出,CLASSPATH里能包括大量备用的搜索路径。然而,利用JAR文件时要留意一个问题:必需将JAR文件的名字置于类路径里,而不只仅是它地址的路径。所以对一个名为grape.jar的JAR文件来说,我们的类路径需要包罗:
CLASSPATH=.;D:\JAVA\LIB;C:\flavors\grape.jar
正确配置好类路径后,可将下面这个文件置于任何目次里(若在执行该措施时碰着贫苦,请拜见第3章的3.1.2小节“赋值”):
//: LibTest.java // Uses the library package c05; import com.bruceeckel.util.*; public class LibTest { public static void main(String[] args) { Vector v = new Vector(); List l = new List(); } } ///:~
#p#分页标题#e#
编译器碰着import语句后,它会搜索由CLASSPATH指定的目次,查找子目次com\bruceeckel\util,然后查找名称适当的已编译文件(对付Vector是Vector.class,对付List则是List.class)。留意Vector和List内无论类照旧需要的要领都必需设为public。
1. 自动编译
为导入的类首次建设一个工具时(可能会见一个类的static成员时),编译器会在适当的目次里寻找同名的.class文件(所以假如建设类X的一个工具,就应该是X.class)。若只发明X.class,它就是必需利用的那一个类。然而,假如它在沟通的目次中还发明白一个X.java,编译器就会较量两个文件的日期标志。假如X.java比X.class新,就会自动编译X.java,生成一个最新的X.class。
对付一个特定的类,或在与它同名的.java文件中没有找到它,就会对谁人类采纳上述的处理惩罚。
2. 斗嘴
若通过*导入了两个库,并且它们包罗沟通的名字,这时会呈现什么环境呢?譬喻,假定一个措施利用了下述导入语句:
import com.bruceeckel.util.*;
import java.util.*;
由于java.util.*也包括了一个Vector类,所以这会造成潜在的斗嘴。然而,只要斗嘴并不真的产生,那么就不会发生任何问题——这虽然是最抱负的环境,因为不然的话,就需要举办大量编程事情,防御那些大概大概永远也不会产生的斗嘴。
如此刻试着生成一个Vector,就必定会产生斗嘴。如下所示:
Vector v = new Vector();
它引用的到底是哪个Vector类呢?编译器对这个问题没有谜底,读者也不行能知道。所以编译器会陈诉一个错误,强迫我们举办明晰的说明。譬喻,假设我想利用尺度的Java Vector,那么必需象下面这样编程:
java.util.Vector v = new java.util.Vector();
由于它(与CLASSPATH一起)完整指定了谁人Vector的位置,所以不再需要import java.util.*语句,除非还想利用来自java.util的其他对象。