1.2 结构与构件
现在,我们从一个典型的Java源文件来了解一下Java程序涉及到的一些重要的构件,以及这些构件的结构。
1 package trades;
2 import java.util.Math;
3 public class Quote extends Thread implements Tradable {
4 // Code goes in here
5 }
那我们从上面的例子中可以发现源文件的结构主要包含三个要素:
n 一个包声明(可选)
程序的第1行就是声明了一个包(package),把该程序放在这个包中间。
n 任意数量的包引入语句(可选)
程序的第2行是要求该程序引入系统提供的一个包java.util.Math。
n 任意数量的类的声明
程序的3-5行则是声明了一个public的类Quote,它是从它的超类Thread扩展得到,并且实现Tradable的接口。
该三要素必须以上述顺序出现。即,任何包引入语句出现在所有类定义之前;如果使用包声明,则包声明必须出现在类和引入语句之前。
结合上面的例子,我们接下来简单的讨论一下涉及到的这些构件,包package、包引入import、类class、方法method和变量variable。
1.2.1 包package
包实际上就是一个组织类class和接口interface的集合。通过包的这种机制,来把类名空间划分成更多易于管理的块。这样做其主要目的就是可以避免类名的冲突,在不同的包里的类,其名字相同也不会发生冲突。包的主要功能如下:
n 依照功能来组织和划分类。例如:java.awt包含了构成抽象窗口工具包(AWT)的类,这个包被用来构建和管理应用程序的图形用户界面。
n 有助于定义变量和方法的访问控制。
n 通过这种包和包引入的机制,也可以保持程序的相对独立性,有利于模块化程序设计的实现。
声明包的定义语句必须放在源文件的顶端。如果你没有显式的声明一个包,那么Java会把你的类放入缺省包(default package)里面,这个包没有名字。
Java使用文件系统目录来存储包,目录名必须和包严格的匹配。另外,这里还要简单谈论一下类路径(CLASSPATH)环境变量的问题。Java编译器使用参考的包层次的根是由类路径决定的。例如,你可以在DOS系统环境下使用如下的命令来指定类路径:
set CLASSPATH=C:\Mysource;C:\Mypackages
如果我们在源文件类的声明前有以下语句:
Package trades.quotes
那么,这个源文件就放在下面这个目录里面:
c:\Mypackages\trades\quotes
最后,需要提醒的是,如果你定义的类已经指定到了某个包中,那么在运行的时候你也需要指定相应的包名和类名一起使用。
同学们可以结合实验二来加深对包概念的理解。
1.2.2 类class
类是面向程序设计中最小的抽象单元,它就像是一个数据和代码的容器。它也是Java里面最重要的一个要件。它通过实例化转变为对象,在实例化的时候自动调用构造函数来初始化对象。
对于类的声明比较复杂,它可以使用很多的修饰符,我们以下面的例子来简单谈论一下这些修饰符。
public abstract class Myclass extends Parent implements A,B {}
首先我们看到的public修饰符,是定义的类的访问控制属性,这个我们在下一章会有详尽的介绍。
然后我们看到的abstract修饰符是表示该类是个抽象类,需要其他类来扩展。这里再介绍一个final修饰符,它与abstract修饰符的意义正好相反,它表示该类不能扩展。所以abstract和final不能在声明类的时候同时使用。
在Java中所有的类都扩展一个超类,如果我们没有指定一个超类,那么系统会把Object类作为它要扩展的超类,Object类就是所有类的超类。这里需要注意的是,在C++中我们使用基类(base class)和继承类(derived class)的称谓,而在Java中我们一般称为超类(super class)和子类(subclass)。
另外,需要提一下的是一个特殊的关键字this,它用来引用正在被调用方法的所属当前对象。
1.2.3 接口interface
接口是一个非功能性、抽象的类,它包含常量和没有功能性的方法声明。也就是说,用interface,你可以指定一个类必须做什么,而不是规定它如何去做。接口在语句的构成上与类十分相似,但是它们缺少实例变量,而且它们定义的方法是不含有方法体的。
接口中声明的变量,一般是final和static型的,意思是它们的值不能通过实现类而改变,也就是说它们被看作是常量,而且它们还必须以常量值初始化。
对于接口来说,它们定义以后都是需要类来实现implement的。也就是说,使用具体的类来实现上面的“如何做”。一个类可以实现多个接口,一个接口也可以被多个类来实现。
另外,接口也可以扩展其他接口来实现继承。如:
interface MyInterface extends YourInterface, Runnable {}
1.2.4 方法method
方法声明的是被调用执行的代码,它可以使用传递的参数并且返回一定类型的返回值。对于方法的修饰符也比较复杂和繁多。我们仍然以一个例子来简单谈论以下其中的一些修饰符。
public static final synchronized double getPrice ( final int itemCode, Color itemColor ) throw Exception { /* code here */}
首先,Public是定义的方法的访问控制属性,这个我们在下一章会有详细介绍。
然后,static、final和synchronized是特殊的修饰符。同样需要注意的是其中final和abstract修饰符不能同时使用的。
最后,我们可以看到该方法getPrice返回一个double数据类型的返回值,而且里面定义了两个参数(itemCode和itemColor)。
另外,我们还注意它通过throw修饰符,表示它会抛出一个Exception的异常,关于异常我们在后面的章节会介绍到。
接下来我们会介绍两种比较特殊的方法。
n Main方法
Main()方法是Java程序的入口起点,它和其他方法的定义类似,只是它包含一个字符串数组用来传递命令行方式执行程序时所跟的参数。如下的例子它把命令行方式所跟的参数全部显示出来:
public class MainTest {
public static void main ( String [] args ) {
for ( int i = 0 ; i < args.lenth; i++ ) {
System.out.println(“Argument ” + i + “: ” + args[i] );
}
}
}
执行如下:
c:\java project\Main> java MainTest Philip K Dick
Argumet 0: Philip
Argumet 1: K
Argumet 2: Dick
这里需要注意的是,如果Main()方法没有使用static修饰符,那么编译不会出错,但是如果你试图执行该程序将会报错,提示main()方法不存在。这是因为你如果这样使用命令行的形式直接执行该程序,MainTest类并没有实例化,所以其main()方法也不会存在,而使用static修饰符则表示该方法是静态的,不需要实例化即可使用。
结合实验三可以加深对Main方法及其参数的理解。
n 构造函数Constructor Method
当类被实例化的时候,第一个被调用的方法就是构造函数。构造函数的主要作用就是初始化变量。如果没有定义构造函数,那么Java会使用其超类的默认构造函数。
构造函数与其他方法相比,主要具有以下的特点:
u 构造函数的名字和其类名相同。
u 没有返回值。
u 构造函数不能像其他超类的方法那样被继承。
u 不能使用final、abstract、synchronized、native或者static修饰符。
有时候,我们在编写构造函数的时候,可能需要首先调用其超类的构造函数,这里我们使用super的关键字,实际上它同this关键字的作用类似,只是它指的是其超类。如下的例子:
class DataServer extends Server {
public String serverName;
public DataServer ( int startCode ) {
super ( startCode );
serverName = “Customer Service”;
}
}
需要注意的是,调用超类构造函数super()的语句必须放在其构造函数定义的前面,否则编译器会报错。
1.2.5 变量variable
变量是Java程序的一个基本存储单元。变量由一个标识符、类型及一个可选初始值的组合定义。此外,如同定义方法一样,它也有各种的修饰符。比如访问控制属性的修饰符public、private、protected和final、static等。这里需要注意的是,变量不能使用synchronized、abstract和native修饰符。
在定义变量的时候需要指明其类型,除了常用的基本类型(比如int型、Boolean型等)以外,也可以使用对象类型。
1.2.6 引入语句import
使用import引入语句是允许你选择某个包中的某个类或者所有类,使之能在你当前的代码中能方便的使用。例如:
import java.util.Dictionary; //引入java.util包中的Dictionary类
import javax.swing.*; //引入javax.swing包中的所有的类
当然如果你不使用引入语句也可以使用其他包中的其他的类,只要你指定引用对象的全名。比如:
java.util.Date now = new java.util.Date();
另外,java.lang包是会被自动引入到源程序中的。