使用的教材是java核心技术卷1,我将跟着这本书的章节同时配合视频资源来进行学习基础java知识。
day022 包(类的导入、静态导入、将类放入包中、包作用域)
Java允许使用包(package)将类组织起来。借助于包可以方便地组织自己的代码,并将自己的代码与别人提供的代码库分开管理。
标准的Java类库分布在多个包中,包括java.lang、java.util和java.net等。标准的Java包具有一个层次结构。如同硬盘的目录嵌套一样,也可以使用嵌套层次组织包。所有标准的Java包都处于java和javax包层次中。
使用包的主要原因是确保类名的唯一性。假如两个程序员不约而同地建立了Employee类。只要将这些类放置在不同的包中,就不会产生冲突。事实上,为了保证包名的绝对唯一性,Sun公司建议将公司的因特网域名(这显然是独一无二的)以逆序的形式作为包名,并且对于不同的项目使用不同的子包。例如,horstmann.com的域名。逆序形式为com.horstmann。这个包还可以被进一步地划分成子包,如com.horstmann.corejava。
从编译器的角度来看,嵌套的包之间没有任何关系。例如,java.util包与java.util.jar包毫无关系。每一个都拥有独立的类集合。
1.类的导入
一个类可以使用所属包中的所有类,以及其他包中的公有类(public class)。我们可以采用两种方式访问另一个包中的公有类。第一种方式是在每个类名之前添加完整的包名。例如:
java.time.LocalDate today = java.time.LocalDate.now();
这显然很繁琐。更简单且更常用的方式是使用import语句。import语句是一种引用包含在包中的类的简明描述。一旦使用了import语句,在使用类时,就不必写出包的全名了。
可以使用import语句导人一个特定的类或者整个包。import语句应该位于源文件的顶部(但位于package语句的后面)。例如,可以使用下面这条语句导人java.util包中所有的类。
import java.util.*;
然后,就可以使用
LocalDate today = LocalDate.now();
而无须在前面加上包前缀。还可以导人一个包中的特定类:
import java.time.LocalDate;
java.time.*的语法比较简单,对代码的大小也没有任何负面影响。当然,如果能够明确地指出所导人的类,将会使代码的读者更加准确地知道加载了哪些类。
在Eclipse中,可以使用菜单选项Source—OrganizeImports。Package语句,如import java.util.*;将会自动地扩展指定的导入列表,如:
import java.util.ArrayList;
import java.util.Date;
这是一个十分便捷的特性。
但是,需要注意的是,只能使用星号(*)导入一个包,而不能使用import java.*或import java.*.*导入以java为前缀的所有包。
在大多数情况下,只导入所需的包,并不必过多地理睬它们。但在发生命名冲突的时候,就不能不注意包的名字了。例如,java.util和java.sql包都有日期(Date)类。如果在程序中导入了这两个包:
import java.util.*;
import java.sql.*;
在程序使用Date类的时候,就会出现一个编译错误:
Date today;//Error java.util.Date or java.sql.Date?
此时编译器无法确定程序使用的是哪一个Date类。可以采用增加一个特定的import语句来解决这个问题:
import java.util.*;
import java.sql.*;
import java.util.Date;
如果这两个Date类都需要使用,又该怎么办呢?答案是,在每个类名的前面加上完整的包名。
java.util.Date deadline = new java.util.Date();
java.sql.Date today = new java.sql.Date(...);
在包中定位类是编译器(compiler)的工作。类文件中的字节码肯定使用完整的包名来引用其他类。
2.静态导入
import语句不仅可以导人类,还增加了导人静态方法和静态域的功能。
例如,如果在源文件的顶部,添加一条指令:
import static java.lang.System.*;
就可以使用System类的静态方法和静态域,而不必加类名前缀:
out.println("Goodbye, World!"); //i.e., System.out
exit(0);//i.e., System.exit
另外,还可以导入特定的方法或域:
import static java.lang.System.out;
实际上,是否有更多的程序员采用System.out或System.exit的简写形式,似乎是一件值得怀疑的事情。这种编写形式不利于代码的清晰度。不过,
sqrt(pow(x,2)+pow(y,2));
看起来比
Math.sqrt(Math.pow(x, 2)+Math.pow(y, 2));
清晰得多。
3.将类放入包中
要想将一个类放人包中,就必须将包的名字放在源文件的开头,包中定义类的代码之前。
package com.horstiann.corejava;
public class Employee
{
...
}
如果没有在源文件中放置package语句,这个源文件中的类就被放置在一个默认包(defaulfpackage)中。默认包是一个没有名字的包。在此之前,我们定义的所有类都在默认包中。
将包中的文件放到与完整的包名匹配的子目录中。例如,com.horstmann.corejava包中的所有源文件应该被放置在子目录com/horstmann/corejava(Windows中com\horstmann\corejava)中。编译器将类文件也放在相同的目录结构中。
之前的两个程序分放在两个包中:PackageTest类放置在默认包中;Employee类放置在com.horstmann.corejava包中。因此,Employee.java文件必须包含在子目录com/horstmann/corejava中。换句话说,目录结构如下所示:

要想编译这个程序,只需改变基目录,并运行命令
javac PackageTest.java
编译器就会自动地查找文件com/horstmann/corejava/Employee.java并进行编译。
下面看一个更加实际的例子。在这里不使用默认包,而是将类分别放在不同的包中(com.horstmann.corejava和com.mycompany)。

在这种情况下,仍然要从基目录编译和运行类,即包含com目录:
javaccom/myconipany/PayrollApp.java
javacom.mycompany.PayrollApp
需要注意,编译器对文件(带有文件分隔符和扩展名.java的文件)进行操作。而Java解释器加载类(带有.分隔符)。
4.包作用域
前面已经接触过访问修饰符public和private。标记为public的部分可以被任意的类使用;标记为private的部分只能被定义它们的类使用。如果没有指定public或private,这个部分(类、方法或变量)可以被同一个包中的所有方法访问。
在之前的程序中,没有将Employee类定义为公有类,因此只有在同一个包(在此是默认包)中的其他类可以访问,例如EmployeeTest。对于类来说,这种默认是合乎情理的。但是,对于变量来说就有些不适宜了,因此变量必须显式地标记为private,不然的话将默认为包可见。显然,这样做会破坏封装性。问题主要出于人们经常忘记键人关键字private。在java.awt包中的Window类就是一个典型的示例。java.awt包是JDK提供的部分源代码:
public class Window extends Container
{
String warningString;
...
}
请注意,这里的wamingString变量不是private!这意味着java.awt包中的所有类的方法都可以访问该变量,并将它设置为任意值(例如,“Trustme!”)。实际上,只有Window类的方法访问它,因此应该将它设置为私有变量。
287



被折叠的 条评论
为什么被折叠?



