5.1 包:库单元
5.1 包:库单元
我们用
import java.util.*;
它的作用是导入完整的实用工具(Utility)库,该库属于标准
若想导入单独一个类,可在
import java.util.Vector;
现在,我们可以自由地使用
之所以要进行这样的导入,是为了提供一种特殊的机制,以便管理“命名空间”(Name Space
正是由于存在名字潜在的冲突,所以特别有必要对
迄今为止,本书的大多数例子都仅存在于单个文件中,而且设计成局部(本地)使用,没有同包名发生冲突(在这种情况下,类名置于“默认包”内
编译一个
①:
“库”也由一系列类文件构成。每个文件都有一个
若在一个文件的开头使用下述代码:
package mypackage;
那么
例如,假定文件名是
package mypackage;
public class MyClass {
// . . .
现在,如果有人想使用
mypackage.MyClass m = new mypackage.MyClass();
import mypackage.*;
// . . .
MyClass m = new MyClass();
作为一名库设计者,一定要记住
大家或许已注意到这样一个事实:由于一个包永远不会真的“封装”到单独一个文件里面,它可由多个
②:ftp://ftp.internic.net
这个技巧的另一部分是将
为进一步理解这个问题,下面以我自己的域名为例,它是
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");
}
} ///:~
创建自己的包时,要求
//: 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
若通过它往回走,就会发现包名
CLASSPATH=.;D:\JAVA\LIB;C:\DOC\JavaT
可以看出,
CLASSPATH=.;D:\JAVA\LIB;C:\flavors\grape.jar
正确设置好类路径后,可将下面这个文件置于任何目录里(若在执行该程序时遇到麻烦,请参见第
//: 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();
}
} ///:~
编译器遇到
- 自动编译
为导入的类首次创建一个对象时(或者访问一个类的
- 冲突
若通过 *
导入了两个库,而且它们包括相同的名字,这时会出现什么情况呢?例如,假定一个程序使用了下述导入语句:
import com.bruceeckel.util.*;
import java.util.*;
由于 java.util.*
也包含了一个
如现在试着生成一个
Vector v = new Vector();
它引用的到底是哪个
java.util.Vector v = new java.util.Vector();
由于它(与import java.util.*
语句,除非还想使用来自
掌握前述的知识后,接下来就可以开始创建自己的工具库,以便减少或者完全消除重复的代码。例如,可为
//: P.java
// The P.rint & P.rintln shorthand
package com.bruceeckel.tools;
public class P {
public static void rint(Object obj) {
System.out.print(obj);
}
public static void rint(String s) {
System.out.print(s);
}
public static void rint(char[] s) {
System.out.print(s);
}
public static void rint(char c) {
System.out.print(c);
}
public static void rint(int i) {
System.out.print(i);
}
public static void rint(long l) {
System.out.print(l);
}
public static void rint(float f) {
System.out.print(f);
}
public static void rint(double d) {
System.out.print(d);
}
public static void rint(boolean b) {
System.out.print(b);
}
public static void rintln() {
System.out.println();
}
public static void rintln(Object obj) {
System.out.println(obj);
}
public static void rintln(String s) {
System.out.println(s);
}
public static void rintln(char[] s) {
System.out.println(s);
}
public static void rintln(char c) {
System.out.println(c);
}
public static void rintln(int i) {
System.out.println(i);
}
public static void rintln(long l) {
System.out.println(l);
}
public static void rintln(float f) {
System.out.println(f);
}
public static void rintln(double d) {
System.out.println(d);
}
public static void rintln(boolean b) {
System.out.println(b);
}
} ///:~
所有不同的数据类型现在都可以在一个新行输出(P.rintln()
ToolTest.java
所以从现在开始,无论什么时候只要做出了一个有用的新工具,就可将其加入
CLASSPATH 的陷阱
乍一看来,这似乎是编译器的一个错误,但假若考察
如果您遇到象这样的情况,请务必保证对于类路径的每个地方,每个名字都仅存在一个类。
然而,条件编译还有另一些非常有价值的用途。一种很常见的用途就是调试代码。调试特性可在开发过程中使用,但在发行的产品中却无此功能。Alen Holub(www.holub.com)提出了利用包(package)来模仿条件编译的概念。根据这一概念,它创建了
可用下面这个类进行程序调试:
//: Assert.java
// Assertion tool for debugging
package com.bruceeckel.tools.debug;
public class Assert {
private static void perr(String msg) {
System.err.println(msg);
}
public final static void is_true(boolean exp) {
if(!exp) perr("Assertion failed");
}
public final static void is_false(boolean exp){
if(exp) perr("Assertion failed");
}
public final static void
is_true(boolean exp, String msg) {
if(!exp) perr("Assertion failed: " + msg);
}
public final static void
is_false(boolean exp, String msg) {
if(exp) perr("Assertion failed: " + msg);
}
} ///:~
这个类只是简单地封装了布尔测试。如果失败,就显示出出错消息。在第
import com.bruceeckel.tools.debug.*;
如欲清除断定机制,以便自己能发行最终的代码,我们创建了第二个
//: Assert.java
// Turning off the assertion output
// so you can ship the program.
package com.bruceeckel.tools;
public class Assert {
public final static void is_true(boolean exp){}
public final static void is_false(boolean exp){}
public final static void
is_true(boolean exp, String msg) {}
public final static void
is_false(boolean exp, String msg) {}
} ///:~
现在,假如将前一个
import com.bruceeckel.tools.*;
程序便不再显示出断言。下面是个例子:
//: TestAssert.java
// Demonstrating the assertion tool
package c05;
// Comment the following, and uncomment the
// subsequent line to change assertion behavior:
import com.bruceeckel.tools.debug.*;
// import com.bruceeckel.tools.*;
public class TestAssert {
public static void main(String[] args) {
Assert.is_true((2 + 2) == 5);
Assert.is_false((1 + 1) == 2);
Assert.is_true((2 + 2) == 5, "2 + 2 == 5");
Assert.is_false((1 + 1) == 2, "1 +1 != 2");
}
} ///:~
通过改变导入的
大家应注意这样一个问题:每次创建一个包后,都在为包取名时间接地指定了一个目录结构。这个包必须存在(驻留)于由它的名字规定的目录内。而且这个目录必须能从