




已阅读5页,还剩66页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第4章 包和接口,4.1 包 4.2 访问保护 4.3 引入包 4.4 接口,4.1 包,在编制大程序的时候,常常会定义许多不同用途的类。如何更好地将这些类组织起来,是一个很重要的问题。这不仅可以使程序整体清晰条理,还可以使其他项目中对这些类的引用更为方便。为此,Java引入了包的概念。 包是类的容器,用于保证类名空间的一致性,例如,可以在自己的包内创建一个名为List的类而不会与别人创建的其他List类重名。由于Java是一种网络编程语言,支持在Internet上动态装载模块,因此它特别注意避免名字空间的冲突。可以认为包是一个名字空间,在该空间中,除了方法重载的情形外,同一个相同类型(类、接口、变量和方法)的名字只能出现一次。而各包之间出现重名,这时系统依靠包名对重名的标识符进行区分。实际上,在程序中使用的每一个变量和方法都隐含地用全限定名进行访问。 限定名的组成方式为:格式包名.类名.变量名或方法名,下一页,返回,4.1 包,4.1.1 定义包 创建一个包只需要包含一个package命令作为一个Java源文件的第一句就可以了。该文件中定义的任何类将属于指定的包。package语句定义了一个存储类的名字空间。如果你省略package语句,类名被输入一个默认的没有名称的包。尽管默认包对于短的例子程序很好用,但对于实际的应用程序它是不适当的。多数情况,程序编写者需要为自己的代码定义一个包。下面是package声明的通用形式: package pkg; 这里,pkg是包名。例如,下面的声明创建了一个名为MyPackage的包。 package MyPackage;,上一页,下一页,返回,4.1 包,Java用文件系统目录来存储包。例如,任何你声明的MyPackage中的一部分的类的.class文件被存储在一个MyPackage目录中。记住这种情况是很重要的,目录名必须和包名严格匹配。 多个文件可以包含相同package声明。package声明仅仅指定了文件中定义的文件属于哪一个包。它不拒绝其他文件的其他方法成为相同包的一部分。多数实际的包伸展到很多文件。你可以创建包层次。为做到这点,只要将每个包名与它的上层包名用点号“.”分隔开就可以了。一个多级包的声明的通用形式如下: package pkg1.pkg2.pkg3;,上一页,下一页,返回,4.1 包,包层次一定要在Java开发系统的文件系统中有所反映。例如,一个由下面语句定义的包: package java.awt.image; 需要在你的UNIX、Windows或Macintosh文件系统的 java/awt/image, javaawtimage或java:awt:image中分别保存。一定要仔细选用包名。你不能在没有对保存类的目录重命名的情况下重命名一个包。,上一页,下一页,返回,4.1 包,4.1.2 编译和运行包 在介绍运用包的例子之前,我们有必要先来讲解一下类路径环境变量。因为Java编译器考虑特定位置作为包层次的根被类路径(CLASSPATH)控制。 如果我们没有用到package语句,我们可以在同样的未命名的默认包中保存所有的类。这样做允许你仅仅通过在命令行键入类名编译源文件和运行Java解释器,并得到结果。这种情况下它还会工作是因为默认的当前工作目录(.)通常在类路径环境变量中为Java运行时间默认定义。然而,当有包参与时,事情就不这么简单了,如果用package语句指明一个包,则包的层次结构必须与文件目录的层次相同。,上一页,下一页,返回,4.1 包,例如,我们在test目录下创建了一个名为packTest的类放在包test中,然后保存在文件packTest.java中。对该文件进行编译后,得到字节码文件packTest.class。如果我们直接在test目录下运行javapackTest,解释器返回”cant find class packTest”(找不到类packTest),因为这时类packTest处于包test中,对它的引用应为test.packTest,于是我们运行java test.packTest,但解释器仍然返回“cant find class testpackTst”(找不到类 testpackTest)。这时我们可以查看CLASSPATH,发现它的值为.;C:javaclasses,表明Java解释器在当前目录和Java类库所在目录c:javaclasses查找,也即在testtest目录下查找类packTest,因此找不到,改正的方法可以有两种:,上一页,下一页,返回,4.1 包,(1)在test的上一级目录运行java test.packTest。 (2)修改CLASSPATH,使其包括当前目录的上一级目录。 由此可以看出,运行一个包中的类时,必须指明包含这个类的包,而且要在适当的目录下运行,同时正确地设定环境变量CLASSPATH,使解释器能够找到指定的类。 例4-1:一个简短的包的例子,我们试试下面简单的包: package MyPack; class Balance String name; double bal;,上一页,下一页,返回,4.1 包,Balance(String n, double b) name = n; bal = b; void show() if(bal “); System.out.println(name + “: $“ + bal); ,上一页,下一页,返回,4.1 包,class AccountBalance public static void main(String args) Balance current = new Balance3; current0 = new Balance(“K. J. Fielding“, 123.23); current1 = new Balance(“Will Tell“, 157.02); current2 = new Balance(“Tom Jackson“, -12.33); for(int i=0; i3; i+) currenti.show(); ,上一页,下一页,返回,4.1 包,称该文件名为 AccountBalance.java,把它存放在MyPack目录中。接着,编译文件。确信结果文件.class同样在MyPack目录中。然后用下面的命令行执行AccountBalance类: java MyPack.AccountBalance 这时我们需要注意,当你执行该命令时你必须在MyPack的上级目录,或者把类路径环境变量设置成合适的值。 如上所述,AccountBalance现在是MyPack包的一部分。这意味着它不能自己执行。也就是说你不能用下面的命令行: java AccountBalance AccountBalance必须和它的包名一起使用。,上一页,返回,4.2 访问保护,前面已经学习了Java的访问控制机制的很多方面和它的访问说明符。例如,你已经知道一个类的private成员仅可以被该类的其他成员访问。包增加了访问控制的另一个维度。如你所看到的,Java提供很多级别的保护以使在类、子类和包中有完善的访问控制。类和包都是封装和容纳名称空间和变量及方法范围的方法。包就像盛装类和下级包的容器。类就像是数据和代码的容器。类是Java的最小的抽象单元。因为类和包的相互影响,Java将类成员的可见度分为四个种类: 相同包中的子类 相同包中的非子类 不同包中的子类 既不在相同包又不在相同子类中的类,下一页,返回,4.2 访问保护,三个访问控制符,private、public和protected,提供了多种方法来产生这些种类所需访问的多个级别,表4-1总结了它们之间的相互作用。 Java的访问控制机制看上去很复杂,我们可以按下面方法简化它。任何声明为public的内容可以被从任何地方访问。被声明成private的成员不能被该类外看到。如果一个成员不含有一个明确的访问说明,它对于子类或该包中的其他类是可见的。这是默认访问。如果你希望一个元素在当前包外可见,但仅仅是元素所在类的子类直接可见,把元素定义成protected。表4-1仅适用于类成员。一个类只可能有两个访问级别:默认的或是公共的。如果一个类声明成public,它可以被任何其他代码访问。如果该类默认访问控制符,它仅可以被相同包中的其他代码访问。下面是一个访问的例子:,上一页,下一页,返回,4.2 访问保护,下面的例子显示了访问修饰符的所有组合。该例有两个包和五个类。记住这两个不同包中的类需要被存储在以它们的包p1、p2命名的目录下。第一个包定义了三个类: Protection, Derived, 和 SamePackage。第一个类以合法的保护模式定义了四个int变量。变量n声明成默认受保护型。n_pri是private型,n_pro是protected,n_pub是public的。 该例中每一个后来的类试图访问该类一个实例中的变量。根据访问权限不编译的行用单行注释/。在每个这样的行之前都是列举该级保护将允许访问的地点的注释。第二个类,Derived是同样包p1中Protection类的子类。这允许Derived访问Protection中的除n_pri以外的所有变量,因为它是private。第三个类,SamePackage,不是Protection的子类,但是是在相同的包中,也可以访问除n_pri以外的所有变量。,上一页,下一页,返回,4.2 访问保护,下面是Protection.java文件: package p1; public class Protection int n = 1; private int n_pri = 2; protected int n_pro = 3; public int n_pub = 4; public Protection() System.out.println(“base constructor“); System.out.println(“n = “ + n); System.out.println(“n_pri = “ + n_pri); System.out.println(“n_pro = “ + n_pro); System.out.println(“n_pub = “ + n_pub); ,上一页,下一页,返回,4.2 访问保护,下面是Derived.java文件: package p1; class Derived extends Protection Derived() System.out.println(“derived constructor“); System.out.println(“n = “ + n); / class only / System.out.println(“n_pri = “ + n_pri); System.out.println(“n_pro = “ + n_pro); System.out.println(“n_pub = “ + n_pub); ,上一页,下一页,返回,4.2 访问保护,下面是SamePackage.java文件: package p1; class SamePackage SamePackage() Protection p = new Protection(); System.out.println(“same package constructor“); System.out.println(“n = “ + p.n); / class only / System.out.println(“n_pri = “ + p.n_pri); System.out.println(“n_pro = “ + p.n_pro); System.out.println(“n_pub = “ + p.n_pub); ,上一页,下一页,返回,4.2 访问保护,下面是另一个包p2的源代码。p2中定义的两个类重载了另两种受访问控制影响的情况。第一个类Protection2是p1.Protection的子类。这允许访问p1.Protection中除n_pri(因为它是private的)和n之外的所有变量,n是定义成默认保护型的。记住,默认型的只能允许类中或包中的代码访问。最后,OtherPackage类只访问了一个变量n_pub,它是定义成public型的。 下面是Protection2.java文件: package p2; class Protection2 extends p1.Protection Protection2() System.out.println(“derived other package constructor“); / class or package only / System.out.println(“n = “ + n);,上一页,下一页,返回,4.2 访问保护,/ class only / System.out.println(“n_pri = “ + n_pri); System.out.println(“n_pro = “ + n_pro); System.out.println(“n_pub = “ + n_pub); 下面是OtherPackage.java文件: package p2; class OtherPackage OtherPackage() p1.Protection p = new p1.Protection(); System.out.println(“other package constructor“);,上一页,下一页,返回,4.2 访问保护,/ class or package only / System.out.println(“n = “ + p.n); / class only / System.out.println(“n_pri = “ + p.n_pri); / class, subclass or package only / System.out.println(“n_pro = “ + p.n_pro); System.out.println(“n_pub = “ + p.n_pub); ,上一页,下一页,返回,4.2 访问保护,如果你希望试试这两个包,下面是两个可以用的测试文件。包p1的测试文件如下: / Demo package p1. package p1; / Instantiate the various classes in p1. public class Demo public static void main(String args) Protection ob1 = new Protection(); Derived ob2 = new Derived(); SamePackage ob3 = new SamePackage(); ,上一页,下一页,返回,4.2 访问保护,p2的测试文件如下: / Demo package p2. package p2; / Instantiate the various classes in p2. public class Demo public static void main(String args) Protection2 ob1 = new Protection2(); OtherPackage ob2 = new OtherPackage(); ,上一页,返回,4.3 引入包,包的存在是划分不同类的很好的机制,了解为什么所有Java内部的类都存在包中是很简单的。在未命名的默认包中是没有核心Java类的;所有的标准类都存储在相同的包中。既然包中的类必须包含它们的包名才能完全有效,为每个你想用的包写一个长的逗点分离的包路径名是枯燥的。因为这点,Java包含了import语句来引入特定的类甚至是整个包。一旦被引入,类可以被直呼其名的引用。import语句对于程序员是很方便的而且在技术上并不需要编写完整的Java程序。如果你在程序中将要引用若干个类,那么用import语句将会节省很多打字时间。 Import语句的功能是引入包或包中的类。从表面上看import语句与C语言的include语句类似,但它们的作用是不同的。include语句是将后面指定的文件实际读入到所在的位置,而import语句并不将包实际读入,它只是指示编译器可以到指定的包中去寻找类、方法等。所有的Java类可以通过指定全限定名的方式使用,但使用import语句后,可以使类名、方法名的引用变得简单。,下一页,返回,4.3 引入包,在Java源文件中,可以有任意多个import语句,它们必须位于package语句(如果有的话)与这个文件中的类或接口之间。下面是import语句的般格式: 格式import包名1.包名2.包名3.(类名|*); 说明该语句中,包具有一定层次结构,最后以显式方式指定一个类名或*号,*号表示Java编译器应该引入整个包。 下面是引入包的一些例子: Import java.awt.image; Import java.lang.*; *号方式可能会增加编译时间,特别是在引入一个大包时。因此,比较好的办法是显式地引入要用到的类,而不是整个包。不过,*号对运行时间的性能或生成的类文件大小没有丝毫影响。,上一页,下一页,返回,4.3 引入包,所有Java包含的标准Java类都存储在名为java的包中。基本语言功能被存储在java包中的java.lang包中。通常,你必须引入你所要用到的每个包或类,但是,既然Java在没有java.lang中的很多函数时是无用的,因此通过编译器为所有程序隐式引入java.lang是有必要的。这与下面的在你所有程序开头的一行是一样的: import java.lang.*; 如果在你用星号形式引用的两个不同包中存在具有相同类名的类,编译器将保持沉默,除非你试图运用其中的一个。这种情况下,你会得到一个编译时错误并且必须明确的命名指定包中的类。任何你用到类名的地方,你可以使用它的全名,全名包括它所有的包层次。例如,下面的程序使用了一个引入语句:,上一页,下一页,返回,4.3 引入包,import java.util.*; class MyDate extends Date 没有import 语句的例子如下: class MyDate extends java.util.Date 如表4-1中所示,当一个包被引入,仅仅是该包中声明成public的项目可以在引入代码中对非子类可用。例如,如果你希望前面显示的MyPack 包中的Balance类在MyPack外可以被独立的类运用,那么你需要声明它为public型,并把它存在自己的文件中,如下: package MyPack;,上一页,下一页,返回,4.3 引入包,public class Balance String name; double bal; public Balance(String n, double b) name = n; bal = b; public void show() if(bal “); System.out.println(name + “: $“ + bal); ,上一页,下一页,返回,4.3 引入包,如你所见,Balance类现在是public。而且,它的构造函数和show( )方法也是public。这意味着它们可以被任何类型的MyPack包之外的代码访问。例如下面TestBalance引入了MyPack,那么它可以利用Balance类: import MyPack.*; class TestBalance public static void main(String args) Balance test = new Balance(“J. J. Jaspers“, 99.88); test.show(); / you may also call show() 作为一个试验,从Balance类移去public修饰符,然后编译TestBalance,和分析得到的结论一样,将会产生错误。,上一页,返回,4.4 接口,所谓接口,就是一系列常量和方法协议的集合,它提供了多个类共同行为的接口,但不限制每个类如何实现这些方法。 Java用接口定义了一组方法协议来实现从不同的父类中继承相似性的操作,这种协议使一个类既能实现相当于父类中的那种操作,又能解决因多重继承所带来的开销过大的问题。所谓方法协议,是指只有方法名和参数,而没有方法体的一种说明格式。它只体现方法的说明,但不指定方法体,真正的方法体是由子类来实现的。这好像与抽象方法一样,抽象方法的方法体也是由子类来实现的,但方法协议与抽象方法是有区别的。,下一页,返回,4.4 接口,接口和类存在着本质的区别。类有它的成员变量和方法,而接口只有常量和方法协议,从概念上讲,接口是一组方法协议和常量的集合。接口在方法协议与方法实体之间只起到一种界面的作用,这种界面限定了方法实体中的参数类型一定要与方法协议中所规定的参数类型保持一致,除此之外,这种界面还限定了方法名、参数个数及方法返回类型的一致性。接口是为支持运行时动态方法解决而设计的。通常,为使一个方法可以在类间调用,两个类都必须出现在编译时间里,以便Java编译器可以检查以确保方法特殊是兼容的。这个需求导致了一个静态的不可扩展的类环境。在一个系统中不可避免会出现这类情况,函数在类层次中越堆越高以致该机制可以为越来越多的子类可用。接口的设计避免了这个问题。它们把方法或方法系列的定义从类层次中分开。,上一页,下一页,返回,4.4 接口,因此,在使用接口时,类与接口之间并不存在子类和父类的那种继承关系,在实现接口规定的某些操作时只存在类中的方法与接口之间保持数据类型一致的关系,而一个类可以和多个接口之间保持这种关系,即一个类可以实现多个接口。正是由此,接口是在和类不同的层次中,与类层次无关的类实现相同的接口是可行的。这是实现接口的真正原因所在。接口为程序提供了许多强有力的手段,接口更容易理解并且在类层次发生变化时不那么脆弱。因为接口只依赖于方法,而不依赖于实例变量中的数据。 要实现一个接口,接口定义的类必须创建完整的一套方法。然而,每个类都可以自由的决定它们自己实现的细节。通过提供interface关键字,Java允许你充分利用多态性的“一个接口,多个方法”。,上一页,下一页,返回,4.4 接口,用关键字interface,你可以从类的实现中抽象一个类的接口。也就是说,用interface,你可以指定一个类必须做什么,而不是规定它如何去做。接口在语句构成上与类相似,但是它们缺少实例变量,而且它们定义的方法是不含方法体的。实际上,这意味着你可以定义不用假设它们怎样实现的接口。一旦接口被定义,任何类成员可以实现一个接口。而且,一个类可以实现多个接口。 在JAVA中,接口增添了很多应用程序所需的功能。在一种语言例如C+中这些应用程序通常借助于多重继承来完成。,上一页,下一页,返回,4.4 接口,4.4.1 接口定义 接口定义很像类定义。下面是一个接口的通用形式: access interface name return-type method-name1(parameter-list); return-type method-name2(parameter-list); type final-varname1 = value; type final-varname2 = value; / . return-type method-nameN(parameter-list); type final-varnameN = value; ,上一页,下一页,返回,4.4 接口,这里,access要么是public,要么就没有用修饰符。当没有访问修饰符时,则是默认访问范围,而接口是包中定义的惟一的可以用于其他成员的东西。当它声明为public时,则接口可以被任何代码使用。name是接口名,它可以是任何合法的标识符。注意定义的方法没有方法体。它们以参数列表后面的分号作为结束。它们本质上是抽象方法;在接口中指定的方法没有默认的实现。每个包含接口的类必需实现所有的方法。 接口声明中可以声明变量。它们一般是final 和static型的,意思是它们的值不能通过实现类而改变。它们还必须以常量值初始化。如果接口本身定义成public ,所有方法和变量都是public的。下面是一个接口定义的例子。它声明了一个简单的接口,该接口包含一个带单个整型 参数的callback( )方法。 interface Callback void callback(int param); ,上一页,下一页,返回,4.4 接口,4.4.2 实现接口 一旦接口被定义,一个或多个类可以实现该接口。为实现一个接口,在类定义中包括implements 子句,然后创建接口定义的方法。一个包括implements 子句的类的一般形式如下: access class classname extends superclass implements interface ,interface. / class-body 这里,access要么是public的,要么是没有修饰符的。如果一个类实现多个接口,这些接口被逗号分隔。如果一个类实现两个声明了同样方法的接口,那么相同的方法将被其中任一个接口客户使用。实现接口的方法必须声明成public。而且,实现方法的类型必须严格与接口定义中指定的类型相匹配。 下面是一个小的实现Callback接口的例子程序:,上一页,下一页,返回,4.4 接口,class Client implements Callback / Implement Callbacks interface public void callback(int p) System.out.println(“callback called with “ + p); 注意:callback( )用public访问修饰符声明;当实现一个接口方法时,它必须声明成public。 类在实现接口时定义它自己的附加的成员,既是允许的,也是常见的。例如,下面的Client版本实现了callback( )方法,并且增加了nonIfaceMeth( )方法。,上一页,下一页,返回,4.4 接口,class Client implements Callback / Implement Callbacks interface public void callback(int p) System.out.println(“callback called with “ + p); void nonIfaceMeth() System.out.println(“Classes that implement interfaces “ + “may also define other members, too.”); 通过接口引用实现接口,上一页,下一页,返回,4.4 接口,你可以把变量定义成使用接口的对象引用而不是类的类型。任何实现了所声明接口的类的实例都可以被这样的一个变量引用。当你通过这些引用调用方法时,在实际引用接口的实例的基础上,方法被正确调用。这是接口的最显著特性之一。被执行的方法在运行时动态操作,允许在调用方法代码后创建类。调用代码在完全不知“调用者”的情况下可以通过接口来调度。这个过程和用超类引用来访问子类对象很相似。在这里我们应该注意,因为Java中在运行时动态查询方法与通常的方法调用相比会有一个非常庞大的花费,所以在对性能要求高的代码中不应该随意的使用接口。,上一页,下一页,返回,4.4 接口,下面的例子通过接口引用变量调用callback( )方法: class TestIface public static void main(String args) Callback c = new Client(); c.callback(42); 该程序的输出如下: callback called with 42,上一页,下一页,返回,4.4 接口,注意变量c被定义成接口类型Callback,而且被一个Client实例赋值。尽管c可以用来访问Callback()方法,它不能访问Client类中的任何其他成员。一个接口引用变量仅仅知道被它的接口定义声明的方法。因此,c不能用来访问nonIfaceMeth( ),因为它是被Client定义的,而不是由Callback定义。 前面的例子机械的显示了一个接口引用变量怎样访问一个实现对象,它没有说明这样的引用的多态功能。为演示这个用途,首先创建Callback的第二个实现,如下: / Another implementation of Callback. class AnotherClient implements Callback / Implement Callbacks interface,上一页,下一页,返回,4.4 接口,public void callback(int p) System.out.println(“Another version of callback“); System.out.println(“p squared is “ + (p*p); 现在,试试下面的类: class TestIface2 public static void main(String args) Callback c = new Client(); AnotherClient ob = new AnotherClient(); c.callback(42); c = ob; / c now refers to AnotherClient object c.callback(42); ,上一页,下一页,返回,4.4 接口,程序输出如下: callback called with 42 Another version of callback p squared is 1764 如你所见,被调用的callback( )的形式由在运行时c引用的对象类型决定。这是一个非常简单的例子,下面你将会看到另一个例子,它更实用。 局部实现 如果一个类包含一个接口但是不完全实现接口定义的方法,那么该类必须定义成abstract型。例如:,上一页,下一页,返回,4.4 接口,abstract class Incomplete implements Callback int a, b; void show() System.out.println(a + “ “ + b); / . 这里,类Incomplete没有实现callback( )方法,必须定义成抽象类。任何继承Incomplete的类都必须实现callback( )方法或者它自己定义成abstract类。,上一页,下一页,返回,4.4 接口,4.4.3应用接口 为理解接口的功能,让我们看一个更实际的例子。我们可以实现一个可变化的堆栈。例如,堆栈的大小可以固定也可以不固定。堆栈还可以保存在数组、链表和二进制树中等。无论堆栈怎样实现,堆栈的接口保持不变。也就是说,push( )和pop( )方法定义了独立实现细节的堆栈的接口。因为堆栈的接口与它的实现是分离的,很容易定义堆栈接口,而不用管每个定义实现细节。让我们看下面的两个例子。 首先,下面定义了一个整数堆栈接口,把它保存在一个IntStack.java文件中。该接口将被两个堆栈实现使用。,上一页,下一页,返回,4.4 接口,/ Define an integer stack interface. interface IntStack void push(int item); / store an item int pop(); / retrieve an item 下面的程序创建了一个名为FixedStack的类,该类实现一个固定长度的整数堆栈: / An implementation of IntStack that uses fixed storage. class FixedStack implements IntStack private int stck; private int tos;,上一页,下一页,返回,4.4 接口,/ allocate and initialize stack FixedStack(int size) stck = new intsize; tos = -1; / Push an item onto the stack public void push(int item) if(tos=stck.length-1) / use length member System.out.println(“Stack is full.“); else stck+tos = item; ,上一页,下一页,返回,4.4 接口,/ Pop an item from the stack public int pop() if(tos 0) System.out.println(“Stack underflow.“); return 0; else return stcktos-; ,上一页,下一页,返回,4.4 接口,class IFTest public static void main(String args) FixedStack mystack1 = new FixedStack(5); FixedStack mystack2 = new FixedStack(8); / push some numbers onto the stack for(int i=0; i5; i+) mystack1.push(i); for(int i=0; i8; i+) mystack2.push(i); / pop those numbers off the stack System.out.println(“Stack in mystack1:“); for(int i=0; i5; i+) System.out.println(mystack1.pop();,上一页,下一页,返回,4.4 接口,System.out.println(“Stack in mystack2:“); for(int i=0; i8; i+) System.out.println(mystack2.pop(); 下面是IntStack 的另一个实现。通过运用相同的接口定义IntStack 创建了一个动态堆栈。这种实现中,每一个栈都以一个初始长度建造。如果初始化长度被超出,那么堆栈的大小将增加。每一次需要更多的空间,堆栈的大小成倍增长。,上一页,下一页,返回,4.4 接口,/ Implement a “growable“ stack. class DynStack implements IntStack private int stck; private int tos; / allocate and initialize stack DynStack(int size) stck = new intsize; tos = -1; ,上一页,下一页,返回,4.4 接口,/ Push an item onto the stack public void push(int item) / if stack is full, allocate a larger stack if(tos=stck.length-1) int temp = new intstck.length * 2; / double size for(int i=0; istck.length; i+) tempi = stcki; stck = temp; stck+tos = item; else stck+tos = item; ,上一页,下一页,返回,4.4 接口,/ Pop an item from the stack public int pop() if(tos 0) System.out.println(“Stack underflow.“); return 0; else return stcktos-; ,上一页,下一页,返回,4.4 接口,class IFTest2 public static void main(String args) DynStack mystack1 = new DynStack(5); DynStack mystack2 = new DynStack(8); / these loops cause each stack to grow for(int i=0; i12; i+) mystack1.push(i); for(int i=0; i20; i+) mystack2.push(i);,上一页,下一页,返回,4.4 接口,System.out.println(“Stack in mystack1:“); for(int i=0; i12; i+) System.out.println(mystack1.pop(); System.out.println(“Stack in mystack2:“); for(int i=0; i20; i+) System.out.println(mystack2.pop(); 下面的类运用了FixedStack和DynStack 实现。它通过一个接口引用完成。意思是说对push( )和 pop( )的调用在运行时解决而不是在编译时解决。,上一页,下一页,返回,4.4 接口,class IFTest3 public static void main(String args) IntStack mystack; / create an interface reference variable DynStack ds = new DynStack(5); FixedStack fs = new FixedStack(8); mystack = ds; / load dynamic stack / push some numbers onto the stack for(int i=0; i12; i+) mystack.push(i); mystack = fs; / load fixed stack for(int i=0; i8; i+) mystack.push(i);,上一页,下一页,返回,4.4 接口,mystack = ds; System.out.println(“Values in dynamic stack:“); for(int i=0; i12; i+) System.out.println(mystack.pop(); mystack = fs; System.out.println(“Values in fixed stack:“); for(int i=0; i8; i+) System.out.println(mystack.pop(); ,上一页,下一页,返回,4.4 接口,该程序中,mystack是IntStack接口的一个引用。因此,当它引用ds时,它使用DynStack实现所定义的push( )和pop( )方法。当它引用fs时,它使用FixedStack定义的push( )和pop( )方法。已经解释过,这些决定是在运行时做出的。通过接口引用变量获得接口的多重实现是Java完成运行时多态的最有力的方法。,上一页,下一页,返回,4.4 接口,4.4.4 接口中的变量 你可以使用接口来引入多个类的共享常量,这样做只需要简单的声明包含变量初始化想要的值的接口就可以了。如果你的一个类中包含那个接口(就是说当你实现了接口时),所有的这些变量名都将作为常量看待。这与在C/C+中用头文件来创建大量的#defined常量或const声明相似。如果接口不包含方法,那么任何包含这样接口的类实际并不实现什么。这就像类在类名字空间引入这些常量作final变量。下面的例子运用了这种技术来实现一个自动的“作决策者”:,上一页,下一页,返回,4.4 接口,import java.util.Random; interface SharedConstants int NO = 0; int YES = 1; int MAYBE = 2; int LATER = 3; int SOON = 4; int NEVER = 5; ,上一页,下一页,返回,4.4 接口,class Question implements SharedConstants Random rand = new Random(); int ask() int prob = (int) (100 * rand.nextDouble(); if (prob 30) return NO; / 30% else if (prob 60) return YES;
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 国家能源资阳市2025秋招面试专业追问及参考电气工程岗位
- 百色市中储粮2025秋招战略研究博士岗高频笔试题库含答案
- 2025年中达电子厂考试题及答案
- 昌都市中石油2025秋招笔试模拟题含答案炼油工艺技术岗
- 中国广电河池市2025秋招计算机类专业追问清单及参考回答
- 2025年春游美术考试题及答案
- 哈密市中石油2025秋招笔试模拟题含答案油品分析质检岗
- 中国联通阿里市2025秋招写作案例分析万能模板直接套用
- 2025年指南教师考试试题及答案
- 鹰潭市中石化2025秋招笔试模拟题含答案炼化装置操作岗
- (2025)辅警笔试题库及参考答案
- 退役军人服务授课课件
- 劳动保障监察投诉书格式及写作范文
- 中班幼儿在角色游戏中同伴模仿行为研究
- 2026年高考语文备考之家庭伦理小说知识点
- 联名合作授权协议书范本
- 营救小羊中班课件
- 跟岗干部管理办法中组部
- 乐理知识入门教学课件
- 法律与道德教学课件
- 归档病案无纸化管理制度
评论
0/150
提交评论