Java核心技术第4章(7)

4.7 包

    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包毫无关系.每一个都拥有独立的类集合.

4.7.1   类的导入

一个类可以使用所属包中的所有类,以及其他包中的公有类.可以采用两种方式访问另一个包中的公有类. 第一种方式是在每一个类名之前添加完整的包名.例如:
java.util.Date tody = new java.util.Date();
    这显然令人讨厌, 第二种方法是使用 import 语句. import 语句是一种引用包含在包中的类的简明描述.一旦使用了 import 语句,在使用类时,就不必写出包的全名了.
    可以使用 import 语句导入一个特定的类或者整个包. import 语句应该位于源文件的顶部(但位于 package 语句的后面).例如,可以使用下列语句导入java.util包中所有的类.
import java.util.*;
    然后,就可以使用
Date today = new Date();
    而无须在前面加上包前缀.还可以导入一个包中的特定类:
import java.util.Date;
    java.util.*的语法比较简单,对代码的大小也没有任何负面影响.当然,如果能够明确地指出所导入的类,将会使代码的读者更准确地知道了加载了哪些类.
    提示:在Eclipse中,可以使用菜单选项Source-Organize Import.Package语句,如 import java.util.*,将会自动地扩展指定的导入列表,如:
import java.util.ArrayList;
import java.util.Date;
    这是一个十分便捷的特性.
    但是,需要注意的是,只能使用星号(*)导入一个包,而不能使用 import java.*或 import java.*.*导入以java为前缀的所有包.
    在大多数情况下,只导入所需的包,并不必过多地理睬它们.但 在发生命名冲突的时候,就不能不注意包的名字了.例如,java.util和java.sql包都有日期类.如果在程序中导入了这两个包:
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(...);
    在包中定位类是编译器的工作.类文件中的字节码肯定使用完整的包名来引用其他类.
    注释:C++程序员经常将 import 和 #include 弄混.实际上,这两者之间没有共同之处.在C++中,必须使用#include将外部特性的声明加载进来,这是因为C++编译器无法查看任何文件的内部,除了正在编译的文件以及在头文件中明确包含的文件.Java编译器可以查看其它文件的内部,只要告诉它到哪里去查看就可以了.
    在Java中,通过显式地给出包名,如java.util.Date,就可以不使用 import;而在C++中,无法避免使用#include .
    import 语句的唯一好处是简捷.可以使用简短的名字而不是完整的包名来引用一个类.例如,在 import java.util.*(或 import java.util.Date)语句之后,可以仅仅用Date引用java.util.Date类.
    在C++中,与包机制类似的是命名空间(namespace),在Java中,package 与 import 语句类似于C++中的 namespace 和 using 指令.

4.7.2   静态导入

    import 语句不仅可以导入类,还添加了导入静态方法和静态域的功能.
    例如,如果在源文件的顶部,添加一条指令:
import static java.lang.System.*;
    就可以使用System类的静态方法和静态域,而不必加类名的前缀.
out.println("Goodbye, world");      // 等价于 System.out
exit(0);                            // 等价于 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))
    清晰得多.

4.7.3   将类放入包中

    要想将一个类放入包中,就必须将包的名字放入源文件的开头,包中定义类的代码之前.例如,程序4-7的文件Employee.java开头是这样的:
package com.horstmann.corejava;
public class Employee
{
    ...
}
    如果没有在源文件中放置 package 语句,这个源文件中的类就放置在一个默认包(default package)中.默认包是一个没有名字的包.在此之前,我们定义的所有类都在默认包中.
    将包中的文件放到完整的报名匹配的子目录中.例如,com.horstmann.corejava包中所有源文件都应该被放置在子目录com/horstmann/corejava中.编译器将类文件也放在相同的目录结构中.
    程序4-6和程序4-7分别放在两个包中:PackageTest类放置在默认包中;Employee类放置在com.horstmann.corejava包中.因此,Employee.java文件必须包含在子目录com/horstmann/corejava中.
    要想编译这个程序,只需改变基目录(当前目录),并运行命令
javac PackageTest.java
    编译器就会自动地查找文件com/horstmann/corejava/Employee.java(在当前目录下,即PackageTest.java所在目录)并进行编译.
    下面看一个更加实际的例子.在这里不使用默认包,而是将类分别放在不同的包中(com.horstmann.corejava和com.mycompany).
    在这种情况下,仍然要从基目录编译和运行类,即包含com目录:
javac com/mycompany/PayrollApp.java
java com.mycompany.PayrollApp
    需要注意,编译器对文件(带有文件分隔符和扩展名.java的文件)进行操作.而Java解释器加载类(带有.分隔符).
    提示:从下一章开始,将对源代码使用包,这样一来,就可以为各章建立一个IDE工程,而不是各小节分别建立工程.
    警告:编译器在编译源文件的时候不检查目录结构.例如,假定有一个源文件开头有下列语句:
package com.mycompany;
    即使这个源文件没有在子目录com/mycompany下,也可以进行编译.如果它不依赖其他包,就不会出现编译错误.但是,最终的程序将无法运行,因为虚拟机找不到类文件.
    程序4-6 PackageTest.java如下所示:
import com.horstmann.corejava.*;
// the Employee class is defined in that package
import static java.lang.System.*;

public class PackageTest
{
    public static void main(String[] args)
    {
        // because of the import statement, we don't have to use com.horstmann.corejava.Employee here
        Employee harry = new Employee("Harry Hacker", 5000, 1999, 10, 1);
        harry.raiseSalary(5);
        // because of the static import statement, we don't have to use System.out here
        out.println("name = " + harry.getName() + ", salary = " + harry.getSalary());
    }
}
    程序4-7 Employee.java如下所示:
package com.horstmann.corejava;
// the classes in this file are part of this package

import java.util.*;
// import statements come after the package statement

public class Employee
{
   private String name;
   private double salary;
   private Date hireDay;

   public Employee(String n, double s, int year, int month, int day)
   {
      name = n;
      salary = s;
      GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
      // GregorianCalendar uses 0 for January
      hireDay = calendar.getTime();
   }

   public String getName()
   {
      return name;
   }

   public double getSalary()
   {
      return salary;
   }

   public Date getHireDay()
   {
      return hireDay;
   }

   public void raiseSalary(double byPercent)
   {
      double raise = salary * byPercent / 100;
      salary += raise;
   }
}
    注意,PackageTest.java所在目录下不能有Employee的相关文件,否则可能遮掩包中的Employee,导致程序出错.

4.7.4   包作用域

    前面已经接触过修饰符 public 和 private .标记为 public 的部分可以被任意的类使用;标记为 private 的部分只能被定义它们的类使用.如果没有指定 public 或 private,这个部分可以(类,方法或变量)可以被同一个包中的所有方法访问.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值