编程学习笔记之Java相关:包的概念和使用

来源:互联网 发布:warframe 淘宝充值 编辑:程序博客网 时间:2024/06/05 05:28

      类似操作系统中的文件目录结构,Java也利用包来组织和管理类,一个包中可以包含着一些围绕某个主题的类。 包中还可以有子包,这就形成了包的层次结构。

      对于类来说,类中所有成员的名字都是互不相干的,比如类A与类B中都有一个方法fun,但其实这两个fun没有任何关系。在一个Java程序的运行过程中,很可能会从Internet上下载一些类,而用户并不知晓。所以在Java中需要名字空间的完全控制,用以保证类名的唯一性。而包机制就为我们的这种目的提供了一种可能性:作为包的设计人员,可以利用包来划分名字空间以避免类名冲突,换言之,一个包中的类不能重名,但不同包中的类可以重名!。

      包是一种松散的类的集合,一般不要求处于同一个包的类有明显的相互关系,如继承、包含等。由于同一个包中的类在默认情况下可以互相访问,所以为了方便管理,通常把一些在一起协同工作的类放在同一个包中。系统也是通过反映不同主题的包为用户提供不同用途的类,如输入输出类、数学运算类、Applet类等。。利用包来管理类,可以实现类库的共享和复用,类库是程序员编写Java程序的宝贵资源,是可以直接拿来使用的“程序芯片”。

      包分为无名包和有名包,无名包的好处是无需对包作任何声明,可以拿来方便快捷使用,但也因为无名包没有名字,所以其它程序无法访问无名包中的类,所以说无名包只适合在局部应用。而有名包就可以被其它程序所访问,可以通过“包名.类名”访问指定包中指定的类。

      在Java中,一个Java源文件被称作一个编译单元,Java的编译单元具有如下的特性:首先,一个编译单元中只能有一个public类,而该类的类名必须与该编译单元的原文件名相同,该编译单元中的其它类都是该public类的支撑类。其次,经过编译后,编译单元中的每个类都会产生一个后缀为“.class”的字节码文件,也称为类文件。最后,由一个编译单元所生成的所有类文件,肯定要归于一个包中,要么是无名包,要么是有名包。



2)包的使用

      使用包【创建有名包】的关键字是package,而且package语句只能作为该Java源文件的第一条语句,而且是整个编译单元中的唯一一条package语句。该语句指定该编译单元中定义的类属于哪个包,如果默认该语句,则默认为无名包。package的使用格式如下:

package 包名; // 包名一般都用小写字母

      下面我们给出一个较为具体的包使用例子:在D200_Card.Java源文件中,定义了PhoneCard类、子类NumberCard以及子子类D200_Card,在源文件的第一行用package声明了一个包,包名是cardclasses,所以该编译单元中的3个类都属于cardclasses。源文件内容如下:

package cardclasses;abstract class PhoneCard{  double balance;}abstract class Number_PhoneCard extends PhoneCard{  long cardNumber;}public class D200_Card extends Number_PhoneCard{  public D200_Card(long cn, double b){  cardNumber = cn;  balance = b;  }  public String performDial()  {    balance -= 0.5;    return "卡号:"+cardNumber+"\n余额:"+balance;  }}
       根据包与目录的对应关系,cardclasses包对应的目录名应该是cardclasses,那么该目录该如何创建呢?而上面的编译单元生成的类文件又该如何存放呢?

      首先我们要确定包的位置,如果我们要把cardclasses放在d:\ temp下,那么下面有两种创建包的方法:

第一种,手工创建;

      首先,在Java项目开发中,应该将源文件与类文件分别放在不同的目录中,因此我们在d:\ temp目录下又新建了一个d200文件夹,用它来专门存放D200_Card.java源文件,然后在该目录下编译源文件【 D:\temp\d200> javac D200_Card.java】,执行该命令后,会在源文件所在的目录,即d200下生成3个类文件,分别是PhoneCard.class、Number_PhoneCard.class和D200_Card.class。

      然后,按照我们当初对包的设计要求,需要在d:\ temp目录下创建一个与包对应的cardclasses文件夹,然后将第一步生成的3个类文件都移动到cardclasses目录下,这就相当于把类添加到包中,因为用package语句声明本编译单元中的类属于指定的包后,就必须把这些类文件放在这个包中,并且只能通过该包来访问这些类。


第二种,自动创建;

      这是一种比较方便快捷的方法,它可以让编译程序在完成编译的同时,也能实现包的创建和类的塞入,该功能的编译命令如下:【D:\temp\d200>javac -d d:\temp D200_Card.java】,我们看到,与前面那个手动方式相比,这一次命令里面多了一个“-d”选项,在它后面就是指定创建包的位置,这个javac命令会根据源文件中package声明的包名,在对应的制定目录下自动创建一个与包名一样的目录,并将编译源文件后所生成的类文件全部放在这个文件夹中,完成包的创建和类的加入。当然,如果对应的包已经存在,那么javac命令会跳过创建包目录步骤,而直接将生成的字节码文件放在该已经存在的包中。如果在这个包存在一个重名的类文件,那么程序会用新的类文件替换掉已经存在的旧的类文件。最后要说明的是,如果想在特定的当前目录下创建包,那么需要使用如下命令:【D:\temp javac -d . d200\D200_Card.java】,其中里面的“.”意思就是当前目录。


      最后介绍一下嵌套包的创建,以我们上面的代码为例,如果我们的package是如下使用:

package cardsystem.cardclasses; //这就是嵌套包
那么编译该源文件后会创建两个包,首先是cardsystem,然后在它里面再创建一个cardclasses。



3)包的使用

      将类组织成包的目的是为了更好的使用这些类,一个类要用到其它的类,无非是访问其它类的静态成员、继承这个类或创建这个类的对象并访问这个对象的属性和方法。

      针对前面我们创建的cardclasses包和里面的三个类文件,我们将在这里讨论如何在包的外部访问它们。对于同一个包中的其它类,直接通过类名就可以访问,而如果是要访问其它包中public类,就需要指定被访问类所属的包了。

      我们首先在d:\temp下建立一个测试程序:

public class TestPackage{  public static void main(String args[])  {    D200_Card my200 = new D200_Card(11111111, 50);    System.out.println(my200.performDial());  }}
但是上面的代码连编译都无法通过:“找不到D200_Card类”,这是因为D200_Card类文件并没有在当前的无名包中,程序也没有指明这个类文件在什么地方,所以编译会报错,改成如下形式即可:

public class TestPackage{  public static void main(String args[])  {   cardclasses.D200_Card my200 = new cardclasses.D200_Card(11111111, 50);    System.out.println(my200.performDial());  }}
虽然这种方法是可行的,但实际应用时却很麻烦,因为我们要在每次使用D200_Card类时都要在前面加上cardclasses,这样非常不方便。所以流行的解决方式是使用下面这种方法:

import cardclasses.D200_Card; //这样就引入了需要使用的D200_Card类public class TestPackage{  public static void main(String args[])  {    D200_Card my200 = new D200_Card(11111111, 50);    System.out.println(my200.performDial());  }}
甚至我们可以直接引入整个包:

import cardclasses.*; //引入了整个包,“*”意味着包中所有的类public class TestPackage{  public static void main(String args[])  {    D200_Card my200 = new D200_Card(11111111, 50);    System.out.println(my200.performDial());  }}
需要注意的是,在一个编译单元中,import不限于一个,但它们必须要出现在所有的类定义之前!此外,import语句仅仅是将指定包中的类引入到当前的类名空间,就是告诉编译程序去哪儿找程序中要使用的类,import语句并不加载类的定义,也不会对那些类文件产生不好的影响。

      当引入的不同包中有类重名的情况时,为区分使用的是哪个包中的类,在类名前加上包名最前缀。在import语句中,包名部分也可以包含小数点,如【import Java.awt.*;】,这句话的意思是:引进Java包中的awt子包中的所有类。


      最后还要讲一下包的定位,包放在哪个目录下可以由程序员自由指定,虽然在程序中可以通过多种方法指定包的位置,但系统又该到什么地方查找这个包呢?这就牵扯到环境变量CLASSPATH了。

      CLASSPATH就像操作系统中的PATH,它指明了查找包的路径,系统将环境变量CLASSPATH包含的一个或多个目录作为起始目录来查找包目录,并在找到的包目录下查找类文件。

      例如我们可以在命令方式下这样设置CLASSPATH环境变量:【 set CLASSPATH = .;d:\temp 】,这样将会为系统设置了两个查找包的途径:第一是查看当前目录下是否有程序需要的包,第二是查看【d:\temp】目录下是否有程序需要的包。




1 0
原创粉丝点击