【练习8-1(CharQ.java、IQDemo.java)】创建队列接口
为在实际中了解接口的强大功能,下面看一个实际例子。在以前的章节中,使用过一个名为Queue的类,该类实现了一个简单的固定大小的字符队列。然而,有许多方法可以实现一个队列。例如,队列可以是固定大小的,或者是可变大小的。队列可以是线性的,在这些情况下可以被用完;或者可能是循环的,在这种情况下只要有元素被拿掉就可以再放入元素。队列也可以装进数组、链表或二叉树中。不管怎样实现队列,该队列的接口始终保持一样。方法put()和get()为队列定义了接口,而没有定义实现细节。因为队列的接口和它的实现是分开的,所以定义队列接口很容易,由每一种实现方式去定义具体的内容。
在本练习中,将为一个字符队列创建一个接口和三种实现方式。这三种实现方式都使用一个数组来存储字符。一种是以前使用过的固定大小的线性队列,另一种是循环队列。在循环队列中当到达内部的数组末尾时,get和put索引将自动返回到起点。这样,任何数目的元素都能存储在循环队列中,只要这些元素也可以被取出。最后一种实现方式创建了一个动态队列,当超过其大小时就会根据需要自动增长。
package javaone.a.beginners.guide;// A character queue interface.
interface ICharQ{// Put a character into the queue.void put(char ch);// Get a character from the queue.char get();
}// A fixed-size queue class for characters.
class FixedQueue implements ICharQ{private char q[]; // this array holds the queueprivate int putloc, getloc; // the put and get indices// Construct an empty queue given its size.public FixedQueue(int size){q = new char[size]; // allocate memory for queueputloc = getloc = 0;}// Put a character into the queue.@Overridepublic void put(char ch) {if(putloc == q.length){System.out.println(" - Queue is full.");return;}q[putloc++] = ch;}// Get a character from the queue.@Overridepublic char get() {if(getloc == putloc){System.out.println(" - Queue is empty.");return (char) 0;}return q[getloc++];}
}// A circular queue.
class CircularQueue implements ICharQ{private char q[]; // this array holds the queueprivate int putloc, getloc; // the put and get indices// Construct an empty queue given its size.public CircularQueue(int size){q = new char[size + 1]; // allocate memory for queueputloc = getloc = 0;}// Put a character into the queue.@Overridepublic void put(char ch) {/*Queue is full if either putloc is one less thangetloc, or if putloc is at the end of the arrayand getloc is at the beginning.*/if((putloc+1 == getloc) | ((putloc==q.length-1) & (getloc == 0))){System.out.println(" - Queue is full.");return;}q[putloc++] = ch;if(putloc==q.length){putloc = 0; // loop back}}// Get a character from the queue.@Overridepublic char get() {if(getloc == putloc){System.out.println(" - Queue is empty.");return (char) 0;}char ch = q[getloc++];if(getloc==q.length){getloc = 0; // loop back}return ch;}
}// A dynamic queue.
class DynQueue implements ICharQ{private char q[]; // this array holds the queueprivate int putloc, getloc; // the put and get indices// Construct an empty queue given its size.public DynQueue(int size){q = new char[size]; // allocate memory for queueputloc = getloc = 0;}// Put a character into the queue.@Overridepublic void put(char ch) {if(putloc == q.length){// increase queue sizechar t[] = new char[q.length * 2];// copy element into the new queue.for(int i = 0; i < q.length; i++){t[i] = q[i];}q = t;}q[putloc++] = ch;}// Get a character from the queue.@Overridepublic char get() {if(getloc == putloc){System.out.println(" - Queue is empty.");return (char) 0;}return q[getloc++];}
}public class ChapterEightProgramOne {public static void main(String[] args) {FixedQueue qOne = new FixedQueue(10);DynQueue qTwo = new DynQueue(5);CircularQueue qThree = new CircularQueue(10);ICharQ iQ;char ch;int i;iQ = qOne;// Put some characters into the fixed queue.for(i = 0; i < 10; i++){iQ.put((char) ('A' + i));}// Show the queue.System.out.println("Contents of fixed queue: ");for(i = 0; i < 10; i++){ch = qOne.get();System.out.print(ch);}System.out.println();iQ = qTwo;// Put some characters into dynamic queue.for(i = 0; i < 10; i++){iQ.put((char) ('Z' - i));}// Show the queue.System.out.println("Contents of dynamic queue: ");for(i = 0; i < 10; i++){ch = qTwo.get();System.out.print(ch);}System.out.println();iQ = qThree;// Put some characters into the circular queue.for(i = 0; i < 10; i++){iQ.put((char) ('A' + i));}// Show the queue.System.out.println("Contents of circular queue: ");for(i = 0; i < 10; i++){ch = qThree.get();System.out.print(ch);}System.out.println();// Put more characters into circular queue.for(i = 0; i < 20; i++){iQ.put((char) ('A' + i));}// Show the queue.System.out.println("Contents of circular queue: ");for(i = 0; i < 10; i++){ch = qThree.get();System.out.print(ch);}System.out.println("\nStore and consume from circular queue.");// Store in and consume from circular queue.for(i = 0; i < 20; i++){iQ.put((char) ('A' + i));ch = iQ.get();System.out.print(ch);}}}
8.15 自测题
1. 使用练习8-1中的代码,把ICharQ接口和它的3种实现方式放进名为qpack的包内。把演示队列的类IQDemo保存在默认的包内,说明如何导入和使用qpack中的类。
答案:
要将ICharQ和它的实现放到qpack包中,必须把每一种实现放在单独的文件中,并让每一种实现类都是公共的,然后把下面的语句添加到每个文件的头部:
package qpack;
完成上述工作后,可通过把下面的import语句添加到IQDemo中来使用qpack:
import qpack.*;
2. 什么是名称空间?为什么Java允许区分名称空间很重要?
答案:名称空间是一种声明的区域,通过区分名称空间,可以防止名称冲突。
3. 包存储在____________中?
答案: 目录。
4. 解释受保护访问与默认访问方式的不同?
答案:受保护访问方式的成员可以在其他所在的包中使用,也可以由任何包的子类使用。默认访问方式只能在其所在的包中使用。
5. 解释一个包的成员被其他包使用的两种方式。
答案: 要使用包的成员,可完全限定其名称,或使用import导入它。
6. “一个接口,多个方法”是Java的关键原则,什么特性可以最好地体现这一点?
答案:接口最好地体现了OOP的这一原则。
7. 多少类可以实现一个接口?一个类可以实现多少个接口?
答案:一个接口可以由任意多个类实现。一个类也可以实现任意多个个接口。
8. 接口可以扩展吗?
答案: 可以。通过使用关键字extends,一个接口可以继承另一个接口。扩展接口的语法与继承类的语法一样。当一个类实现继承了其他接口的接口时,它必须为在接口继承链中定义的所有方法提供实现方式。
9. 为第7章中的Vehicle类创建一个接口,把该接口命名为IVehicle。
interface IVehicle{// Return the range.int range();// Compute fuel needed for a given distance.double fuelneeded(int miles);// Access methods for instance variables.int getPassengers();void setPassengers(int p);int getFuelcap();void setFuelcap(int f);int getMpg();void setMpg(int m);
}
10. 在接口中变量被隐式声明为static和final,它们可以在程序的其他部分共享吗?
答案:接口变量的作用在于命名的常量可由程序中的所有文件共享。通过导入变量所在的接口来使用接口变量。
11. 包实际上是类的容器,这种说法正确还是错误?
答案:正确。
12. 什么标准Java包是自动导入到程序中的?
答案: java.lang。
13. 声明默认接口方法时使用的是哪个关键字?
答案: default。
14. 从JDK 8开始,可以在接口中定义static方法吗?
答案: 可以。
15. 假定练习8-1中的ICharQ接口已广泛使用了多年。现在,想给它添加一个名为reset()的方法,该方法用于将队列重置为空队列,即开始状态。JDK 8或后续版本在不破坏先前存在的代码的情况下,如何实现这一点呢?
答案: 为了避免破坏先前存在的代码,必须使用默认接口方法。因为不知道如何重置每个队列实现,所以默认的reset()实现就会报告用来还没有实现的错误(为此,最佳方法是使用异常)。幸运的是,先前存在的代码并没有假定ICharQ定义了reset()方法,因此这些代码既不会碰到错误,也不会被破坏。
16. 如何调用接口中的static方法?
答案: 使用点(.)运算符,通过接口名称来调用static接口方法。
17. 接口可以有私有方法吗?
答案:从JDK 9开始,答案是可以的。