【练习8-1(CharQ.java、IQDemo.java)】创建队列接口
为在实际中了解接口的强大功能,下面看一个实际例子。在以前的章节中,使用过一个名为Queue的类,该类实现了一个简单的固定大小的字符队列。然而,有许多方法可以实现一个队列。例如,队列可以是固定大小的,或者是可变大小的。队列可以是线性的,在这些情况下可以被用完;或者可能是循环的,在这种情况下只要有元素被拿掉就可以再放入元素。队列也可以装进数组、链表或二叉树中。不管怎样实现队列,该队列的接口始终保持一样。方法put()和get()为队列定义了接口,而没有定义实现细节。因为队列的接口和它的实现是分开的,所以定义队列接口很容易,由每一种实现方式去定义具体的内容。
在本练习中,将为一个字符队列创建一个接口和三种实现方式。这三种实现方式都使用一个数组来存储字符。一种是以前使用过的固定大小的线性队列,另一种是循环队列。在循环队列中当到达内部的数组末尾时,get和put索引将自动返回到起点。这样,任何数目的元素都能存储在循环队列中,只要这些元素也可以被取出。最后一种实现方式创建了一个动态队列,当超过其大小时就会根据需要自动增长。
java
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 queue
private 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 queue
putloc = getloc = 0;
}
// Put a character into the queue.
@Override
public void put(char ch) {
if(putloc == q.length){
System.out.println(" - Queue is full.");
return;
}
q[putloc++] = ch;
}
// Get a character from the queue.
@Override
public 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 queue
private 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 queue
putloc = getloc = 0;
}
// Put a character into the queue.
@Override
public void put(char ch) {
/*
Queue is full if either putloc is one less than
getloc, or if putloc is at the end of the array
and 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.
@Override
public 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 queue
private 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 queue
putloc = getloc = 0;
}
// Put a character into the queue.
@Override
public void put(char ch) {
if(putloc == q.length){
// increase queue size
char 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.
@Override
public 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 自测题
- 使用练习8-1中的代码,把ICharQ接口和它的3种实现方式放进名为qpack的包内。把演示队列的类IQDemo保存在默认的包内,说明如何导入和使用qpack中的类。
答案:
要将ICharQ和它的实现放到qpack包中,必须把每一种实现放在单独的文件中,并让每一种实现类都是公共的,然后把下面的语句添加到每个文件的头部:
package qpack;
完成上述工作后,可通过把下面的import语句添加到IQDemo中来使用qpack:
import qpack.*;
- 什么是名称空间?为什么Java允许区分名称空间很重要?
答案:名称空间是一种声明的区域,通过区分名称空间,可以防止名称冲突。
- 包存储在____________中?
答案: 目录。
- 解释受保护访问与默认访问方式的不同?
答案:受保护访问方式的成员可以在其他所在的包中使用,也可以由任何包的子类使用。默认访问方式只能在其所在的包中使用。
- 解释一个包的成员被其他包使用的两种方式。
答案: 要使用包的成员,可完全限定其名称,或使用import导入它。
- "一个接口,多个方法"是Java的关键原则,什么特性可以最好地体现这一点?
答案:接口最好地体现了OOP的这一原则。
- 多少类可以实现一个接口?一个类可以实现多少个接口?
答案:一个接口可以由任意多个类实现。一个类也可以实现任意多个个接口。
- 接口可以扩展吗?
答案: 可以。通过使用关键字extends,一个接口可以继承另一个接口。扩展接口的语法与继承类的语法一样。当一个类实现继承了其他接口的接口时,它必须为在接口继承链中定义的所有方法提供实现方式。
- 为第7章中的Vehicle类创建一个接口,把该接口命名为IVehicle。
java
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);
}
- 在接口中变量被隐式声明为static和final,它们可以在程序的其他部分共享吗?
答案:接口变量的作用在于命名的常量可由程序中的所有文件共享。通过导入变量所在的接口来使用接口变量。
- 包实际上是类的容器,这种说法正确还是错误?
答案:正确。
- 什么标准Java包是自动导入到程序中的?
答案: java.lang。
- 声明默认接口方法时使用的是哪个关键字?
答案: default。
- 从JDK 8开始,可以在接口中定义static方法吗?
答案: 可以。
- 假定练习8-1中的ICharQ接口已广泛使用了多年。现在,想给它添加一个名为reset()的方法,该方法用于将队列重置为空队列,即开始状态。JDK 8或后续版本在不破坏先前存在的代码的情况下,如何实现这一点呢?
答案: 为了避免破坏先前存在的代码,必须使用默认接口方法。因为不知道如何重置每个队列实现,所以默认的reset()实现就会报告用来还没有实现的错误(为此,最佳方法是使用异常)。幸运的是,先前存在的代码并没有假定ICharQ定义了reset()方法,因此这些代码既不会碰到错误,也不会被破坏。
- 如何调用接口中的static方法?
答案: 使用点(.)运算符,通过接口名称来调用static接口方法。
- 接口可以有私有方法吗?
答案:从JDK 9开始,答案是可以的。