5.9 包
包package是一个唯一命名的类的集合; 将类集合到包中, 是为了在应用程序中使用预先编写的类时, 避免与类自身引起命名冲突.
通过包名加以限定, 包中的类不会和程序中或者其他包中的类命名冲突;
String类的全名是java.lang.String, 可以使用非限定名称是因为java.lang包中的类在程序代码中已经隐式的import了(default package).
如果自定义了String类, 需要使用java.lang.String来调用库的类.
5.9.1 对类打包
包语句package statement由关键字package加上包名组成.
Note 必须是第一条语句, 包语句之前只允许空行或注释; public的类可以在包的外部被访问; 否则只能被包内部的类访问;
1
2
3
4
|
package
Geometry;
public
class
Line {
// Details of the class definition
}
|
>Geometry中的类必须在开头包括同样的包语句, 必须在同一个目录Geometry下.
1) 包与目录结构
名为ClassName的类定义必须存储在名为ClassName.java的文件中[可以在文件中再定义其他类, 但主类名必须与文件名统一];
所有在包PackageName中的类文件必须包含在名为PackageName的目录下.
Note 可以将编译生成的ClassName.class文件存储在另一个目录下, 但目录名必须与包名一样;
包名可以由多个简单名字组合, 点号分隔.
1
2
|
package
Geometry.Shapes3D;
package
Geometry.Shapes2D;
|
目录结构是Geometry/Shapes3D和Geometry/Shapes2D;
2) 编译包
Note 指向包目录的路径必须显式地设置到CLASSPATH环境变量中, 让编译器知道. 设定CLASSPATH的方法可以是在调用编译器时使用-classpath选项;
Note 指向包目录的路径是包含包目录的路径; e.g. Geometry类存储在C:\Test\Geometry目录下的Geometry包中, 那Geomerty目录的路径是C:\Test而不是C:\Test\Geometry.
1
|
javac -classpath
"C:\Test"
Line.java
|
>编译会影响所有Line类引用的所有类;
>如果路径中包含空格, 必须使用双引号;
如果包内的类互相不相关, 可以使用通配符编译相应的文件;
1
|
javac -classpath
"C:\Test 1"
*.java
|
3) 访问包
编译使用的包的程序时, 1)可以将.class文件存放在包命名的同名目录;
2) 如果包目录包含.class文件, 包路径必须出现在CLASSPATH环境变量中, 或者使用编译器命令行参数 -classpath (会覆盖CLASSPATH).
Note 使用CLASSPATH, 只需要包含对应包的路径, 不必考虑Java的标准包, 编译器解释器能自动找到;
CLASSPATH, e.g. Windows: Control Panel->System->Advanced System Setting->Create the variable CLASSPATH; 可以添加任意路径, 分号隔开; Linux: CLASSPATH取决于所使用的shell程序;
使用JDK时, 使用-classpath的优点是只对当前的编译或执行起作用.
1
|
javac –classpath
"C:\MySource;C:\MyPackages"
MyProgram.java
|
3)将编译的包变成标准包集合的扩展;
4) 使用扩展
扩展是存储在ext目录中的.jar文件, 默认目录结构 >
>创建归档文件: jar cvf Geometry.jar Geometry\*.class, 将Geometry.java放在ext目录下.
Note 当创建.jar文件时, 要确保在包名对应的目录结构中添加了.class文件.
[More] 工具和实用程序 Tools and Utilities;
5.9.2 将类从包添加到程序中
1
|
import
Geometry.Shapes3D.*;
// Include all classes from this package
|
>导入规范中可以使用通配符; e.g.导入包中所有的类, 而不是目录中所有的文件;
Note 这时程序中的类名称必须与包中的类名不同, 否则会冲突;
1
|
import
Geometry.Shapes3D.Sphere;
// Include the class Sphere
|
>只导入要使用的类, 避免冲突;
Note *只能用来选择包中所有类, 不能用来选择所有的包; e.g. Geometry.*;
5.9.3 程序中的包和名称
包名可以看作类名的前缀; 类的全名其实是Folder.SubFolder.ClassName;
使用全名可以不需要import, 也可以在特定情况下防止名字冲突;
1
|
Geometry.Shapes3D.Sphere ball =
new
Geometry.Shapes3D.Sphere(
10.0
,
1.0
,
1.0
,
1.0
);
|
5.9.4 导入静态成员
1
2
3
|
Math.PI
//普通使用
import
static
java.lang.Math.PI;
//可以直接使用PI;
|
可以使用*号导入类的静态成员: 常量(变量)和方法;
1
|
import
static
java.lang.Math.*;
// Import all static members of the Math class
|
5.9.5 标准包
标准类在标准包中:
java.lang 包含一些对于Java 基本的类(比如Math 类), 这些类都在程序中自动可用. 不需要使用import 语句来导入它们.
java.nio.file 包含的类与java.io 包中的类一起支持文件I/O, 是在JDK 7 中新加入的;
java.awt 包含的类支持Java 的图形用户界面(Graphical User Interface-GUI). 通常使用Swing 类, 更加容易而且效果更好.
javax.swing 提供的类支持“Swing”GUI 组件. 不仅仅比同类的java.awt 更灵活, 更容易使用, 也主要由Java 实现并且对本地代码依赖很少;
java.awt.event 包含支持事件处理的类;
java.awt.geom 包含绘制和操作平面几何实体的类;
javax.swing.border 其中的类支持生成围绕Swing 组件的边界;
javax.swing.event 其中的类支持Swing 组件的事件处理;
java.applet 包含的类允许编写Java applet-嵌套在网页中的程序;
java.util 包含的类支持对管理数据集合, 访问日期和时间信息以及分析字符串进行一部分标准操作;
封装了基本数据类型的标准类
Boolean, Character, Byte, Short, Integer, Long, Float, Double;
基本类型与字符串之间的转换
每个类的静态toString()方法; 非静态toString()方法返回类对象的字符串表示形式;
将String对象转换成基本类型: Integer-parseInt(), NumberFormatException异常; parseShort(), parseByte(), parseLong()...
parseFloat(), parseDouble(), parseBoolean()...
基本数据类型和Boolean类定义了静态方法valueOf(), 将字符串转换成类类型的对象; 对Boolean类型来说, 忽略大小写, 如果字符串不等于true则valueOf()返回false;
将对象转换成值
每个封装基本数据值的类定义了classValue()方法, class代表对应的基本类型名, 该方法将对象封装的值以对应的基本类型返回;
e.g. 一个Double类型对象, 封装了3.1415, number.doubleValue返回double类型的值3.1415;
基本常量
static final的MAX_VALUE和MIN_VALUE;
浮点型有POSITIVE_IFINITY, NEGATIVE_INFINITY和NaN(Not a Number, 非数字, 0/0), 可以用进行比较; 方法有isInfinte(), isNaN();
Note 及时不是被零除也可能出现无限值; 对于计算结果太大无法表示的情况会产生POS和NEGATIVE_INFINITY;
基本类型数值的自动装箱
装箱转换 boxing conversion, 自动装箱 autoboxing;
将基本类型的值传递给一个方法, 但是参数是一个指向对象的引用, 当情况允许时, 编译器会提供基本类型数值到对于类类型的自动转换; e.g. int -> Integer;
对于封装了基本类型的类的对象, 当需要将指向对象的引用转换成对象封装的值, 编译器会插入拆箱转换 unboxing conversion;
5.10 类成员的访问控制
静态类方法可以引用静态成员, 非静态方法可以指向类的成员; 可访问度还取决于类成员设定的访问属性, 包, 类是否public.
5.10.1 使用访问属性
同一个包中的类能直接访问其他的类名: 声明变量或设定参数类型; 成员变量和方法的可访问性由访问属性access attribute控制; 只有类为public时, 类名才可以被其他包的类访问;
类成员的访问属性:
无访问属性: 允许同一个包中任意类的方法访问;
public: 只要类声明为public, 允许任意位置的类方法访问;
private: 只允许类内部的方法访问, 完全不能在类的外部访问;
protected: 允许同一个包中任意类的方法访问并且允许任意位置的子类访问;
包内:
包外:
>Class1在另一个包中的子类无法访问没有访问属性的成员;
5.10.2 设定访问属性
public, protected, private, 按顺序排列有助于代码阅读;
当成员变量作为private时,
获取成员的值: 可以使用取值accessor方法: public type getMember() { return member; }
修改成员的值: 可以使用赋值mutator方法: public oid setMember(type value) { member = value; }, 赋值方法可以进行新值有效性检测;
5.10.3 选择访问属性
对于面向对象来说, public类中的成员变量一般为private; 使用access方法获取成员变量;
特殊情况:
-对于包中非public的类, 无法从包外访问, 这时最好允许包中的其他类能直接访问其中的数据成员;
-对于final的数据成员, constant值可能在包外也有用, 可以设置成public;
-类中的方法可能只想被同一类的其他方法使用, 这种情况应该设为private;
-对于标准类比如Math, 作为数据值的容器, 应该将所有内容设置为pubic;
使用包和访问属性
Note 包中每个类的源文件和.class文件必须放在名称一样的包下[路径可以不一样].
在编译时要确保源文件和class文件的目录路径在CLASSPATH变量中, 或者-classpath设置目录;
源文件开头的包语句定义了类所属的包;
e.g. TryPackage目录下的TryPackage.java, 对于Windows系统如果路径是C:\Packages\Geometry; 引用和导入的类在Geometry下;
编译: TryPackage.java在当前目录下;
1
|
javac –classpath
"C:\Packages"
TryPackage.java
|
执行: .代表当前目录
1
|
java –classpath
".;C:\Packages"
TryPackage
|
二维数组定义:
1
2
|
double[][] coords = { {1.0, 0.0}, {6.0, 0.0}, {6.0, 10.0},
{10.0,10.0}, {10.0, -14.0}, {8.0, -14.0}};
|
数组长度coords.length为6;
Note 可以创建一个二维数组, 每行的元素数目不同. e.g. double[][] coords = { {1.0, 2, 0.0}, {6.0, 0.0}};
编译包下的类, 将.class生成在包下:
1
|
javac -classpath
"C:\Packages"
Geometry/*.java
|
5.11 嵌套类
将一个类的定义放到另一个类中, 这个内部类被称为嵌套类nested class; 嵌套类也可以在自身内部嵌套另一个类;
1
2
3
4
5
6
7
|
public
class
Outside {
// Nested class
public
class
Inside {
// Details of Inside class...
}
// More members of Outside class...
}
|
>Inside被定义成public, 所以在Outside外部也能访问Inside类;
包围类也称为顶级类top level class. 顶级类自身不是嵌套类; 想要创建嵌套类的对象, 必须使用包围类的名称作为限定名来引用嵌套类;
1
|
Outside.Inside inner = outer.
new
Inside();
// Define a nested class object
|
在Outside的非静态成员方法中, 可以直接使用Inside类名, 编译器自动使用了this作为限定名.
在Outside内部: Inside inner = new Inside(); 等于this.Inside inner = this.new Inside();
Note 包围类的静态方法不可以创建非静态嵌套类对象, 直接创建的非静态嵌套类的对象, 会在包围类的有效范围之外创建出嵌套类对象, 这是非法的策略; [失去控制]
5.1.11.1 静态嵌套类
嵌套类对象独立于包围类对象;
1
2
3
4
5
6
7
8
9
10
|
public
class
Outside {
public
static
class
Skinside {
// Details of Skinside
}
// Nested class
public
class
Inside {
// Details of Inside class...
}
// More members of Outside class...
}
|
>Skinside类是静态的, 可以独立于任何Outside类对象声明嵌套类对象;
1
|
Outside.Skinside example =
new
Outside.Skinside();
|
>静态嵌套类的名称存在于外部类的上下文中, 嵌套类名由包围类类名限定;
Note 静态嵌套类能拥有静态成员, 非静态嵌套类不能拥有;
如果查看编译器产生的.class文件, 嵌套类有自己的名为Outside$Inside的class文件; 嵌套类的类名由包括它的外部类的类名限定;
5.11.2 使用非静态嵌套类
将原本静态嵌套类中的静态数据成员搬到包围类中处理;
5.11.3 使用非顶级类的嵌套类
可以在包含内部类的顶级类的外部创建内部类的对象;
static的Rabbit
1
2
|
MagicHat oldHat =
new
MagicHat(
"Old hat"
);
// New hat object
MagicHat.Rabbit rabbit =
new
MagicHat.Rabbit();
// Create rabbit object
|
>使用顶级类的类名作为限定符;
非statici的Rabbit
1
2
|
MagicHat oldHat =
new
MagicHat(
"Old hat"
);
// New hat object
MagicHat.Rabbit rabbit = oldHat.
new
Rabbit();
// Create rabbit object
|
Note 构造函数调用时顶级类的使用: oldHat.new Rabbit(); 对象名限定符在new之前, 表示在对象oldHat的上下文中创建rabbit, 在rabbit中使用的顶级类成员和oldHat绑定;
5.11.4 本地嵌套
在方法中定义类: 本地嵌套类 local nested class, 或称为本地内部类 local inner class; 非静态嵌套类通常被称为内部类 inner class;
当方法内部的计算需要使用特殊的, 在其他地方不需要的类时, 使用本地内部类; e.g. 监听用户与应用程序之间交互事件的监听器;
本地内部类能引用在类定义出现的方法中声明的变量, 但是这些变量必须是final的;
5.12 小结
类定义: 类定义设定了作为类成员的变量和方法;
类文件: 每个类都必须存储到一个与类同名的文件中, 必须使用扩展名.java;
类变量: 使用关键字static 声明类变量, 每个类变量的实例都会被所有类对象共享;
实例变量: 类的每个对象都有自己的实例变量—它们在类中声明, 没有使用关键字static;
静态方法: 对于设定为static 的方法, 即使不存在类对象也可以调用, 但是static 方法不能引用实例变量;
非静态方法: 没有使用static 进行设定的方法可以直接访问类中的所有变量;
递归方法: 自己调用自己的方法;
类成员访问: 类成员的访问性取决于对它们各自设置的访问属性. 访问属性分为public private protected 或者无属性;
包: 类可以被聚集到一个包中; 如果要从包的外部访问包中的类, 那么这些类必须使用关键字public 进行声明;
包成员: 为了将类设计成包的成员, 需要在包含类定义的文件的开头使用一条包语句;
使用包成员: 为了将包中的类添加到文件中, 需要在文件中的任何包语句之后立即使用import语句;
嵌套类: 嵌套类是指在另一个类定义的内部定义的类. 嵌套类对象只有在外部类对象的上下文中才能存在;
创建静态嵌套类对象: 可以独立地创建静态嵌套类对象, 但是静态嵌套类的类名必须由外部类的类名进行限定;
---5 End---