|
|
|
| [来源]管理员
[作者]网络文摘 [时间]2005-11-05
|
推荐等级:
点击:
|
在前面的教程面向对象编程概念的内容中你学习到了面向对象的概念。现在我们来学习如何将这些概念用在JAVA中。在这部分教程中将介绍 创建类 管理继承 执行嵌套类 5.1 创建类 这节中将介绍一个更大的类Stack的完整描述,并且描述一个从它继承而来的对象的生命周期的类的所有组件。首先介绍构造函数,然后介绍成员变量和方法。 既然我们前面已经介绍了如何创建和使用对象以及怎样将对象从内存中清除,现在该是我们介绍怎样编写类的时候了。这一节中将通过一个执行LIFO(后入先出)堆栈的小例子来介绍类的组件。如图22给处了类以及识别代码的结构: (图22) 堆栈的执行使用了另外的对象,即Vector来存储它的元素。Vector是一个对象的数组,在需要空间的时候它为新对象分配内存空间。这个Stack类是通过使用Vector来存储它的元素的。但是它对Vector限定了LIFO,也就是说,你可以只增加元素并且从堆栈的顶部移出元素 5.1.1 类的声明 图22给出了组成类执行的两个主要组件:类声明和类实体。类声明定义了类的名字以及其它的属性。为Stack类的类声明是相对地简单,它指示了类为public并且它地名字为Stack。 图23给出了类声明可能的组件以及描述了它们的目的。必须的组件是class关键字。所有其它的组件是可选的。如果你没有声明这些可选的组件,JAVA编译器就将给出缺省值:没有执行接口的对象的nonpublic、nonabstract、nofinal子类。 (图23) 下面对每个类的组件给出更详细的介绍。 public :这个public关键字声明了类可以在其它任何的类中使用。 abstract :声明了这个类不能被实例化。 Final:声明了类不能被继承,即没有子类了。 class NameOfClass :关键字class指示编译器类的声明以及类的名字是NameOfClass。 extends Super :这个extends 子句认为Super是类的父类,因此在类的分级结构中插入了类。 implements Interfaces :为了声明类可执行一个或多个接口,可以使用关键字implement并且在其后面给出由类执行的接口的名字的列表,它们是以逗号分隔的。 5.1.2 类实体 类实体是跟在类声明的后面,它是嵌入在大括号{和}中间的。类实体包含了所有实例变量和类变量的声明。另外,类实体还包含了所有实例方法和类方法的声明。 5.1.3 为类提供构造函数 类可能包含一个或者多个的构造函数,它提供了从类创建的对象的初始化。 所有的JAVA类都有构造函数,它用来对新的对象进行初始化。构造函数与类的名字是相同的。比如,Stack类的构造函数的名字为Stack,而Rectangle类的构造函数的名字为Rectangle,Thread类的构造函数的名字为Thread。下面给出Stack类的构造函数。 public Stack() { items = new Vector(10); } JAVA支持对构造函数的重载,这样一个类就可以有多个构造函数,所有的构造函数的名字都是相同的。下面是定义在Stack的另外的构造函数。这个构造函数是根据它的参数来初始化堆栈的大小。 public Stack(int initialSize) { items = new Vector(initialSize); } 从上面可以看出两个构造函数都有相同的名字,但是它们有不同的参数列表。编译器会根据参数列表的数目以及类型了区分这些构造函数的。典型地,构造函数使用它的参数来初始化新的对象状态。当创建对象的时候,选择构造函数要看看它的参数是否最好反映了你想初始化的新对象。 5.1.3 为类提供构造函数 根据你传递给构造函数参数的数目和类型,编译器可以决定要使用哪个构造函数。下面的代码编译器是认识的,它是使用了单一的整型参数: new Stack(10); 相似地,当你编写下面代码的时候,编译器选择了没有参数的构造函数或者缺省的构造函数: new Stack(); 当你编写自己的类的时候,你不必为它提供构造函数。系统会自动提供缺省的构造函数。这个缺省的构造函数不会完成任何事情。因此,如果你想进行一些初始化的时候,你就要为类编写一些构造函数了。 下面是Thread的子类的构造函数,它执行动画,设置了一些缺省的数值,比如帧速度、图片的数目然后装载图片。 class AnimationThread extends Thread { int framesPerSecond; int numImages; Image[] images; AnimationThread(int fps, int num) { super("AnimationThread"); this.framesPerSecond = fps; this.numImages = num; this.images = new Image[numImages]; for (int i = 0; i <= numImages; i++) { . . . // 以下转载图片 . . . } } . . . } 5.1.3 为类提供构造函数 实际上构造函数的实体跟方法的实体是相似的,也就是说,它包含了局部变量声明、循环以及其它的语句。但是,AnimationThread构造函数中的第二行你在方法的构造函数是不会看到的: super("AnimationThread"); 这一行调用了一个由父类AnimationThread提供的构造函数Thread。这个特定的Thread构造函数有一个String参数,它用来设置Thread的名字。经常,构造函数想利用编写在父类中的初始化代码。实际上,有些类必须调用它们的父类构造函数来使对象正确完成任务。 父类的构造函数必须是子类构造函数的第一条语句。对象必须首先执行高层次的初始化。你可以在构造函数生命处通过使用一个访问的指定来指定什么其它对象可以创建类的实例,访问的指示如下: private:没有其它类可以实例化这个类。这个类可能包含public类方法,这些方法可以构造一个对象并返回,但是其它的类不行。 protected :只有子类和在相同包中的类可以创建它的实例。 public :任何类都可以创建它的实例。 没有说明的时候:只有在相同包中的类可以创建它的实例。 构造函数提供了一个初始化新对象的方法。初始化实例和类成员提供了对初始化类和由类的新对象进行初始化的方法。 5.1.4 声明成员变量 类的状态是由它的成员变量给出的。你可以在类的实体中生命一个类的成员变量。典型地,你可以在声明它的方法之前声明类的变量,虽然这不不是必要的。 classDeclaration { member variable declarations method declarations } 这里注意,为了声明变量(类的成员),声明必须在类实体中,而不是在方法的实体中。在方法的实体中声明的变量都是局部变量。 5.1.5 执行方法 众所周知,对象的行为是由它的方法来执行的。其它对象通过调用它的方法来访问对象。在这一小节中,我们将一起来为JAVA类编写方法。在JAVA众,你可以在类的实体中定义类的方法,用于执行一些行为。典型地,你可以在类实体中的变量之后声明类的方法,虽然这也不是必须的。 图24给出了Stack的push方法的代码。这个方法是一个进栈操作,它将一个Object作为参数,放置到堆栈的顶部然后返回它。 (图24) 就象一个类一样,方法也有两个主要部分:方法声明和方法实体。方法声明了方法的所有属性,比如访问等级、返回类型、方法名字以及参数,如图25所示: (图25) 方法实体中有实现方法行为的代码。它包含了执行方法的JAVA指令。 下面介绍一下方法的声明: 一个方法的声明包括方法的名字以及返回的类型(指定了由方法返回的数据类型): returnType methodName() { . . . } 这个方法声明是非常基本的。方法有许多其它的属性,比如参数、访问控制等等。 那怎样将信息传递给方法呢? 可能,在绝大多数情况下使用的可选方法声明的组件是方法的参数。类似于其它编程语言的函数,JAVA方法接收调用者传递来的参数。这些参数就提供了从方法作用域外部的信息给方法。 5.1.6 控制对类成员的访问 当我们声明JAVA类成员的时候,我们可以允许或者不允许其它类型的对象通过访问指示来访问这些成员。 其中一个是,类可以保护它们的成员变量和方法受其它对象的访问。也许你会问这很重要吗?是的。假如你编写一个类,它是对数据进行查询,而这个数据库包含了公司的各种秘密信息,所以就有必须进行保护。 在JAVA中,你可以在声明类变量和方法的时候,使用访问指示来保护它们。JAVA语言支持对成员变量和方法的四个访问等级:private、 protected、 public以及留着不指定的等级。 下面的表给出来每一种访问指示的访问等级: 第一列给出了是否类本身可以访问它的成员。从上表可以知道,类总是可以访问它自己的成员。第二列给出是否类的子类可以访问它的成员;第三列给出是否在相同包中的类可以访问成员。第四列给出是否所有的类可以访问成员。 这里注意在Protected/子类交叉的地方有一个’*’,这个是特殊的访问情况,后面的教程会给出详细的介绍。 下面对各种访问等级详细说明: Private 绝大多数代用限制性的访问等级是private。Private成员只能被它所定义的类所访问。如果外部访问这个变量就将导致前后矛盾的状态。或者如果private的方法被外部类所调用,就会使得运行的程序或者对象的状态处于不良的状态。Private成员就象一些不可告人的秘密。 为了声明一个private成员,只需在声明的时候加入private关键字即可。下面的类包含了一个private成员变量和一个Private方法: class Alpha { private int iamprivate; private void privateMethod() { System.out.println("privateMethod"); } } 这个Alpha类型的对象可以检查或者修改imaprivate变量以及可以调用privateMethod方法,但是其它类型的对象就不行,比如,以下的Beta类就不能访问iamprivate变量或者调用privateMethod,因为Beta不是Alpha类型的: class Beta { void accessMethod() { Alpha a = new Alpha(); a.iamprivate = 10; // 非法 a.privateMethod(); // 非法 } } Private 当一个类试图访问一个它不能访问的成员变量的时候,编译器就会打印出错误信息(如下)病拒绝继续编译程序: Beta.java:9: Variable iamprivate in class Alpha not accessible from class Beta. //在Alpha类中的imaprivate变量不能从Beta类中进行访问 a.iamprivate = 10; // 非法 ^ 1 error //一个错误 同时,如果你的程序试图访问一个不能访问的方法,就将导致如下的编译器错误: Beta.java:12: No method matching privateMethod() found in class Alpha. //在Alapha类中没有匹配的方法privateMethod() a.privateMethod(); // 非法 1 error //一个错误 JAVA的新手可能会问是否一个Alpha对象可以访问另外一个Alpha对象的private成员。下面给出具体例子来进行解释。假如Alpha类包含了一个实例方法,它比较当前的Alpha对象(this)以及另外一个对象的iamprivate变量: class Alpha { private int iamprivate; boolean isEqualTo(Alpha anotherAlpha) { if (this.iamprivate == anotherAlpha.iamprivate) return true; else return false; } } 结果是相当的合法。所以,相同类型的对象可以访问其它的private成员。这是因为访问限制只是在类别层次(类的所有实例)而不是在对象层次(类的特定实例)上。 (2)Protected protected允许类本身、子类以及在相同包中的类访问这个成员。在允许类的子类访问而杜绝其它不相关的类的访问的时候,可以使用protected访问等级。Protected成员就象家庭秘密,家里人知道无所谓,但是就不让外人知道,不是有“家臭不可外扬”一说吗?,虽然这里不是什么“家臭”,但它是受protected的成员。 为了声明一个protected成员,使用关键字protected。首先,让我们看看protected是怎样影响在相同包内的类的。假如上面的那个Alpha类现在被定义在一个(package)包Greek内,它有一个protected成员变量和一个protected方法: package Greek; public class Alpha { protected int iamprotected; protected void protectedMethod() { System.out.println("protectedMethod"); } } 现在,假设类Gamma也声明为Greek包的一个成员(不是Alpha的子类)。Gamma类可以合法访问Alpha对象的iamprotected成员变量并且可以合法调用它的protectedMethod: package Greek; class Gamma { void accessMethod() { Alpha a = new Alpha(); a.iamprotected = 10; // 合法 a.protectedMethod(); // 合法 } } 下面我们来研究一下protected是怎样影响Alpha的子类的访问的: 首先介绍一个新的类Delta,它是来由Alpha继承而来的,但是它处在不同的包中,即Latin。这个Delta类可以访问iamproted和protectedMethod,不仅可以访问Delta类的对象而且可以访问它的子类。Delta类不能访问Alpha类型的对象中的iamprotected或者protectedMethod。在下面代码中的accessMethod试图访问在Alpha类型对象中的imaprotected成员变量,它是不合法的,而访问Delta类型对象则是合法的。相似地,accessMethod 试图调用Alpha对象的protectedMethod也是合法的: package Latin; import Greek.*; class Delta extends Alpha { void accessMethod(Alpha a, Delta d) { a.iamprotected = 10; // 非法 d.iamprotected = 10; // 合法 a.protectedMethod(); // 非法 d.protectedMethod(); // 合法 } } 综上所述,如果一个类是里面包含protected成员的类的子类或者它们处在同一个包中,那么这个类就可以访问protected成员了。 |
|