《Java编程入门官方教程》第八章练习答案

发布于:2024-12-23 ⋅ 阅读:(13) ⋅ 点赞:(0)

【练习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 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 自测题

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开始,答案是可以的。