北京理工大学2019-2020学年第二学期2018级面向对象程序设计(Java语言)期末试题(A卷) 
班级________ 学号________ 姓名________ 成绩________
一、填空题(每空1分,共10分) 
- Java中的垃圾回收机制消除了程序中的内存泄露(memory leak)问题。
- Java使用关键字super来引用当前class所继承的基类。
- 将方法声明为final意味着它不能被继承。
- 封装是指把数据成员和相关的方法都放进一个类中,并使用访问权限来控制其可见性。
- 单例模式的作用- 保证一个类仅有一个实例,并提供一个全局访问点。
- Java不直接支持多继承,但可以通过接口实现多继承。
- 获取一个文件目录下文件列表的方法是File类的listFiles()方法。
- Java中的数据类型主要分为基本类型和引用类型。
- Math.round(-11.5)的返回值是- -11。
- List接口中查询速度较快的是- ArrayList类。
二、判断正误并说明原因(每题2分,共10分) 
- “float x1=1.414f;”语句中等号右边的f是多余的,因为1.414本身就是float类型的值。()
- 数组有length()方法。()
- Java语言的跨平台性使得它不需要提供sizeof()运算符。()
- 内部类(- inner class)可以访问其- 外部类(- enclosing class)中的- private成员。()
- 构造器可以被重写()
三、单选题(每题1分,共5分) 
- 下面哪个是Java语言中正确的标识符()。 A. 5xB.$xC.abc@D.(com
- 下列说法错误的是(): A. 数组可以动态初始化; B. 数组可以赋值给Object类型的对象; C. 数组创建以后,其长度可以修改; D. 数组可以调用Object类的所有方法;
- 假定类A有一个方法void method(),如果要求能够通过类名A直接调用method(),则其方法定义应该为() A.static void method()B.public void method()C.final void method()D.abstract void method()
- 关于Java中的多态,以下说法不正确的为()。 A.多态不仅可以减少代码量,还可以提高代码的可扩展性和可维护性 B. 把子类转换为父类,称为向下转型,自动进行类型转换 C.多态是指同一个实现接口,使用不同的实例而执行不同的操作 D.继承是多态的基础,没有继承就没有多态.
- 按照指定的格式向文件写入数据,所用的处理流是() A. BufferedReaderB.DataInputStreamC.DataOutputStreamD.ObjectInputStream
四、写出程序运行结果(共20分) 
- // StaticInitialization.java(每空1分,共6分)
class Bowl {
    Bowl(int marker) {
        System.out.println("Bowl(" + marker + ")");
    }
}
class Table {
    static Bowl b1 = new Bowl(1);
    Table() {
        System.out.println("Table()");
    }
    Bowl b5 = new Bowl(5);
    static Bowl b2 = new Bowl(2);
}
class Cupboard {
    Bowl b3 = new Bowl(3);
    static Bowl b4 = new Bowl(4);
    Cupboard() {
        System.out.println("Cupboard()");
    }
}
public class StaticInitialization {
    public static void main(String[] args) {
        System.out.println(
                "Creating new Cupboard() in main");
        new Cupboard();
        new Table();
    }
    static Table t2 = new Table();
    static Cupboard t3 = new Cupboard();
}该程序的输出结果为: 2. 以下是Arrays.java的源代码:
public class Arrays {
    public static void main(String[] args) {
        int[] a1 = { 1, 2, 3, 4 };
        int[] a2 = a1;
        for(int i = 0; i < a2.length; i++)
            a2[i]++;
        for(int i = 0; i <= a1.length; i++)
            System.out.println("a1[" + i + "] = " + a1[i]);
    }
}请问该程序执行后的输出是什么?(5分) 3. 请写出输出结果(4分)
public class T1 {
    public static void main(String[] args) {
        T1 a = new T1();
        a.method(8);
        a.method(1.2f);
    }
    void method(float i) {
        System.out.println("float: " + i);
    }
    void method(long i) {
        System.out.println("long: " + i);
    }
}- //AlwaysFinally.java(5分)
class FourException extends Exception {}
public class AlwaysFinally {
    public static void main(String[] args) {
        System.out.println("Entering first try block");
        try {
            System.out.println("Entering second try block");
            try {
                throw new FourException();
            } finally {
                System.out.println("finally in 2nd try block");
            }
        } catch(FourException e) {
            System.out.println("Caught FourException in 1st try block");
        } finally {
            System.out.println("finally in 1st try block");
        }
    }
}该程序的输出结果为:
五、简答题(共20分) 
- 按照要求,补齐代码(5分)
interface Inter { void show(); }
class Outer {
    //请在此处补齐代码 
}
class OuterDemo {
    public static void main(String[] args) {
        Outer.method().show();
    }
}要求在控制台输出"HelloWorld" 2. 请简述重载和重写的区别?(5分) 3. 在以下代码中,Parcel类以匿名内部类的方式实现了Destination接口,请改用普通内部类的方式实现之。(5分)
// Destination.java
public interface Destination {
    String readLabel();
}
// Parcel.java
public class Parcel {
    public Destination dest() {
        return new Destination() {
            private String label = "ABCD";
            public String readLabel() { return label; }
        };
    }
}- 简述Java中异常处理的机制(5分)
六、编程题(共35分) 
- 按以下要求编写程序(5分) - 创建一个Rectangle类,添加width和height两个成员变量
- 在Rectangle中添加两种方法分别计算矩形的周长和面积
- 编程利用Rectangle输出一个矩形的周长和面积
 
- 创建一个
- 按以下要求编写程序(5分) - 编写Animal接口,接口中声明run()方法
- 定义Bird类和Fish类实现Animal接口
- 编写Bird类和Fish类的测试程序,并调用其中的run()方法
 
- 编写
- 请设计一个表示电话号码(如010-68911205)的类Phone,其中设置相应的数据成员,分别用来表示区号(area,如010)和8位号码(number,如68911205)。该类包含两个构造函数:第一个为default constructor;第二个构造函数的参数应为表示完整电话号码的字符串,函数中使用String.indexOf()和String.substring()方法或者java.util.StringTokenizer类的方法来获取各数据成员的值。类中要求实现相应的set和get方法来分别设置及获取数据成员的值,并重写Object.toString()函数,使之返回完整的电话号码字符串。(6分)
- 文件records.dat中存放着某班学生数学课程的期末考试成绩,每条记录占一行,形如"学号, 姓名, 分数"。请完成以下程序,从文件中逐行读取每个学生的相关数据,并把文件中的每行信息转换成"学号--分数"的形式,存入一个类型为ArrayList<String>的容器对象中。接着遍历该容器对象的内容,将其中存放的字符串逐个输出到命令行中。(7分)
import java.io.*;
import java.util.*;
public class ReadRec {
    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new FileReader("records.dat"));
        List<String> l1 = new ArrayList<String>();
        …请在此处填充代码,将程序补充完整…
    }
}- 创建一个图书类(Book),并在该类中定义3个成员变量图书编号(id)、图书名称(name)以及作者(author),使用单例模式实现仅能创建Book类的3个实例。(5分)
- 请设计并实现类QuickSort,使用递归方法实现快速排序算法。快速排序的基本思想为:将原问题分解为若干个规模更小但结构与原问题相似的子问题;递归求解这些子问题,然后将这些子问题的解组合为原问题的解。快速排序可分为三个步骤,即划分、求解和组合。QuickSort类中包含三个方法:partition方法用于对待排序数组r[low…high]做划分,即以r[low]为基准,将所有小于该基准的元素移到其前端,所有大于或等于该基准的元素移到其后端,这样就将该基准放到了最终有序的位置上;sort方法用于对数组r[m…n]进行递归排序;quickSort方法使用前两个方法对类中的数据成员(包含10个元素的int数组)进行排序。(7分)
答案与解析 
(一)填空题 
- 垃圾回收(- Garbage Collection):Java的- 垃圾回收机制自动回收不再被引用的对象所占用的内存,有效避免- 内存泄露。
- super:通过- super关键字可以访问父类的成员,包括属性和方法等。
- final:被- final修饰的方法不能被继承,子类无法对其进行重写。
- 数据成员(或- 成员变量):封装就是将数据和操作数据的方法组合在一个类中,并利用访问权限控制外界对其的访问。
- 保证一个类仅有一个实例,并提供一个全局访问点:在某些场景下,确保类只有一个实例可以避免资源的重复占用和数据不一致等问题。
- 接口(- interface):Java类虽然不能多继承,但一个类可以实现多个接口,从而达到类似多继承的效果。
- File类的- listFiles()方法:使用- File类创建目录对象后,调用- listFiles()方法可获取该目录下的文件和子目录列表。
- 引用类型:Java数据类型分为- 基本类型(如- int、- float、- char等)和- 引用类型(如类、接口、数组等)。
- -11:- Math.round()方法是四舍五入取整,- -11.5四舍五入后为- -11。
- ArrayList:- ArrayList基于数组实现,随机访问效率高,适合频繁查询操作;- LinkedList基于链表实现,插入和删除操作效率较高。
(二)判断正误并说明原因 
- 错误:在Java中,1.414默认是double类型常量,若要赋值给float类型变量,必须加上后缀f,否则会报错。
- 错误:数组有length属性,用于获取数组的长度,不是length()方法,length()方法一般用于String类获取字符串长度。
- 正确:Java语言跨平台,其数据类型的长度是固定的,不依赖于具体的机器硬件,所以不需要sizeof()运算符来获取数据类型的大小。
- 正确:内部类可以访问外部类的所有成员,包括private成员,这是内部类的特性之一。
- 错误:构造器不能被重写,因为构造器的名称必须与类名相同,且重写要求方法签名(方法名、参数列表)相同,子类构造器与父类构造器名称不同,所以无法重写。
(三)单选题 
- B:Java标识符可以由字母、数字、下划线(_)和美元符号($)组成,但不能以数字开头,也不能是关键字,所以$x是正确的标识符。
- C:数组创建后,其长度是固定的,不能修改;数组可以动态初始化、赋值给Object类型的对象,也可以调用Object类的所有方法。
- A:能通过类名直接调用的方法必须是静态方法,用static修饰。
- B:把子类转换为父类,称为向上转型,是自动进行类型转换;向下转型是把父类对象转换为子类对象,需要强制类型转换,且存在风险,所以B选项说法错误。
- C:DataOutputStream用于按照指定格式向文件写入基本数据类型的数据;BufferedReader用于从字符输入流中读取文本;DataInputStream用于从输入流中读取基本数据类型的数据;ObjectInputStream用于从输入流中读取对象。
(四)写出程序运行结果 
- 输出结果:
Bowl(1)
Bowl(2)
Bowl(4)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
Bowl(5)
Table()
Bowl(1)
Bowl(2)
Bowl(5)
Table()
Bowl(3)
Bowl(4)
Cupboard()解析:程序运行时,先加载静态成员。Table类的静态成员b1和b2初始化,输出Bowl(1)和Bowl(2);Cupboard类的静态成员b4初始化,输出Bowl(4)。接着执行main方法,创建Cupboard对象,初始化b3,输出Bowl(3),执行Cupboard构造方法,输出Cupboard();创建Table对象,初始化b5,输出Bowl(5),执行Table构造方法,输出Table()。之后静态成员t2和t3初始化,重复上述部分过程。 2. 输出结果:
a1[0] = 2
a1[1] = 3
a1[2] = 4
a1[3] = 5
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 4 out of bounds for length 4
	at Arrays.main(Arrays.java:7)解析:a2和a1指向同一个数组,对a2元素自增会影响a1。第二个for循环中,数组下标从0到3,当i为4时,会发生数组越界异常。 3. 输出结果:
long: 8
float: 1.2解析:方法调用时,根据参数类型选择合适的重载方法。8是int类型,可自动转换为long,调用method(long i);1.2f是float类型,调用method(float i)。 4. 输出结果:
Entering first try block
Entering second try block
finally in 2nd try block
Caught FourException in 1st try block
finally in 1st try block解析:程序进入第一个try块,再进入第二个try块,抛出异常后,先执行第二个try块的finally代码块,然后在第一个try块的catch块捕获异常,最后执行第一个try块的finally代码块。
(五)简答题 
1. 按照要求,补齐代码(补充解析) 
完整代码如下:
interface Inter { void show(); }
class Outer {
    public static Inter method() {
        return new Inter() {
            @Override
            public void show() {
                System.out.println("HelloWorld");
            }
        };
    }
}
class OuterDemo {
    public static void main(String[] args) {
        Outer.method().show();
    }
}解析:需要在Outer类中定义一个静态方法method(),返回一个实现了Inter接口的匿名内部类对象。关键点是:
- 方法必须是静态的,因为在main方法中是通过类名直接调用的
- 返回类型必须是Inter接口类型
- 使用匿名内部类实现该接口的show()方法,在方法中输出"HelloWorld"
2. 重载和重写的区别(补充解析) 
- 定义不同: - 重载(Overloading):同一个类中定义多个名称相同但参数列表不同的方法
- 重写(Overriding):子类重新实现父类中已有的方法
 
- 条件不同: - 重载:方法名相同,参数类型、个数或顺序不同,返回值类型可以相同也可以不同
- 重写:方法名、参数列表和返回值类型必须相同(Java 5后返回值可以是父类方法返回值的子类型)
 
- 访问权限: - 重载:无特殊限制
- 重写:子类方法的访问权限不能小于父类方法的访问权限
 
- 异常抛出: - 重载:无特殊限制
- 重写:子类方法抛出的异常不能超过父类方法的异常范围
 
- 多态类型: - 重载:编译时多态(静态绑定)
- 重写:运行时多态(动态绑定)
 
3. 匿名内部类改为普通内部类(补充解析) 
// Destination.java
public interface Destination {
    String readLabel();
}
// Parcel.java
public class Parcel {
    // 普通内部类实现
    private class PDestination implements Destination {
        private String label = "ABCD";
        
        public String readLabel() { 
            return label; 
        }
    }
    
    public Destination dest() {
        return new PDestination();
    }
}解析:将匿名内部类改为普通内部类,需要:
- 创建一个有名字的内部类(这里命名为PDestination)
- 实现Destination接口
- 将原匿名内部类中的实现移到这个具名内部类中
- 在dest()方法中返回这个内部类的实例
4. Java中的异常处理机制(补充解析) 
Java的异常处理机制主要包括以下几个方面:
- 异常体系: - 所有异常都是Throwable类的子类
- Error:严重问题,程序通常无法处理(如- OutOfMemoryError)
- Exception:程序可以处理的异常- 检查异常(Checked Exception):必须显式处理(如IOException)
- 非检查异常(Unchecked Exception):运行时异常,不强制处理(如NullPointerException)
 
- 检查异常(Checked Exception):必须显式处理(如
 
- 所有异常都是
- 异常处理语句: - try-catch:捕获并处理异常
- try-catch-finally:无论是否发生异常,finally块都会执行
- try-with-resources:自动关闭资源
- throw:手动抛出异常
- throws:声明方法可能抛出的异常
 
- 异常处理原则: - 尽早捕获,尽可能晚地抛出
- 只捕获能够处理的异常
- 不要忽略异常
- 合理使用自定义异常
 
- 异常处理好处: - 提高程序健壮性
- 与正常代码逻辑分离
- 提供良好的错误处理机制
- 方便调试和维护
 
(六)编程题 
1. Rectangle类及应用(解析) 
public class Rectangle {
    private double width;
    private double height;
    
    // 构造方法
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    
    // 计算周长的方法
    public double perimeter() {
        return 2 * (width + height);
    }
    
    // 计算面积的方法
    public double area() {
        return width * height;
    }
    
    // getter和setter方法
    public double getWidth() {
        return width;
    }
    
    public void setWidth(double width) {
        this.width = width;
    }
    
    public double getHeight() {
        return height;
    }
    
    public void setHeight(double height) {
        this.height = height;
    }
    
    // 测试代码
    public static void main(String[] args) {
        Rectangle rect = new Rectangle(5.0, 3.0);
        System.out.println("矩形周长:" + rect.perimeter());
        System.out.println("矩形面积:" + rect.area());
    }
}解析:
- 创建Rectangle类,包含width和height两个成员变量
- 提供构造方法初始化矩形的宽和高
- 实现计算周长方法:perimeter() = 2 * (width + height)
- 实现计算面积方法:area() = width * height
- 在main方法中创建矩形对象并调用方法计算周长和面积
2. Animal接口及实现类(解析) 
// Animal接口
public interface Animal {
    void run();
}
// Bird类实现Animal接口
public class Bird implements Animal {
    @Override
    public void run() {
        System.out.println("鸟儿飞翔...");
    }
}
// Fish类实现Animal接口
public class Fish implements Animal {
    @Override
    public void run() {
        System.out.println("鱼儿游泳...");
    }
}
// 测试类
public class AnimalTest {
    public static void main(String[] args) {
        Animal bird = new Bird();
        Animal fish = new Fish();
        
        System.out.println("鸟的运动方式:");
        bird.run();
        
        System.out.println("鱼的运动方式:");
        fish.run();
    }
}解析:
- 定义Animal接口,声明run()方法
- 创建Bird类实现Animal接口,重写run()方法描述鸟类的运动方式
- 创建Fish类实现Animal接口,重写run()方法描述鱼类的运动方式
- 在测试类中创建两种动物对象并调用其run()方法
3. Phone类设计(解析) 
public class Phone {
    private String area; // 区号
    private String number; // 电话号码
    
    // 默认构造函数
    public Phone() {
        this.area = "";
        this.number = "";
    }
    
    // 带参数的构造函数
    public Phone(String phoneNumber) {
        int index = phoneNumber.indexOf('-');
        if (index != -1) {
            this.area = phoneNumber.substring(0, index);
            this.number = phoneNumber.substring(index + 1);
        } else {
            this.area = "";
            this.number = phoneNumber;
        }
    }
    
    // 设置区号
    public void setArea(String area) {
        this.area = area;
    }
    
    // 获取区号
    public String getArea() {
        return area;
    }
    
    // 设置号码
    public void setNumber(String number) {
        this.number = number;
    }
    
    // 获取号码
    public String getNumber() {
        return number;
    }
    
    // 重写toString方法
    @Override
    public String toString() {
        if (area.isEmpty()) {
            return number;
        } else {
            return area + "-" + number;
        }
    }
    
    // 测试代码
    public static void main(String[] args) {
        Phone phone1 = new Phone("010-68911205");
        System.out.println("区号: " + phone1.getArea());
        System.out.println("号码: " + phone1.getNumber());
        System.out.println("完整号码: " + phone1);
        
        Phone phone2 = new Phone();
        phone2.setArea("020");
        phone2.setNumber("88889999");
        System.out.println("完整号码: " + phone2);
    }
}解析:
- 创建Phone类,包含area和number两个成员变量
- 实现默认构造函数,初始化为空字符串
- 实现带参数的构造函数,解析完整电话号码字符串,提取区号和号码
- 提供get和set方法访问成员变量
- 重写toString()方法,返回完整电话号码字符串
- 测试代码验证类的功能
4. 文件读取与处理(解析) 
import java.io.*;
import java.util.*;
public class ReadRec {
    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new FileReader("records.dat"));
        List<String> l1 = new ArrayList<String>();
        
        String line;
        // 逐行读取文件内容
        while ((line = in.readLine()) != null) {
            // 分割每行数据,格式为"学号, 姓名, 分数"
            String[] parts = line.split(",");
            if (parts.length >= 3) {
                // 转换成"学号--分数"的形式
                String studentId = parts[0].trim();
                String score = parts[2].trim();
                String result = studentId + "--" + score;
                // 添加到ArrayList中
                l1.add(result);
            }
        }
        
        // 关闭资源
        in.close();
        
        // 遍历ArrayList,输出内容
        for (String s : l1) {
            System.out.println(s);
        }
    }
}解析:
- 使用BufferedReader读取文件records.dat
- 逐行读取文件内容,处理每一行数据
- 使用split(",")分割每行数据,获取学号、姓名和分数
- 将学号和分数组合成"学号--分数"的格式,添加到ArrayList中
- 关闭读取资源
- 遍历ArrayList,输出每个元素到控制台
5. Book类单例模式实现(解析) 
class Book {
    private String id;      // 图书编号
    private String name;    // 图书名称
    private String author;  // 作者
    // 使用静态变量存储三个实例
    private static Book[] instances = new Book[3];
    // 记录已创建实例数量
    private static int count = 0;
    // 私有构造方法,防止外部直接实例化
    private Book(String id, String name, String author) {
        this.id = id;
        this.name = name;
        this.author = author;
    }
    // 获取实例的静态方法
    public static synchronized Book getInstance(String id, String name, String author) {
        if (count < 3)
            return instances[count++] = new Book(id, name, author);
        return null; // 已创建3个实例,不能再创建
    }
    @Override
    public String toString() {
        return "Book [id=" + id + ", name=" + name + ", author=" + author + "]";
    }
    // 测试代码
    public static void main(String[] args) {
        Book book1 = Book.getInstance("001", "Java编程", "张三");
        Book book2 = Book.getInstance("002", "数据结构", "李四");
        Book book3 = Book.getInstance("003", "算法导论", "王五");
        Book book4 = Book.getInstance("004", "设计模式", "赵六"); // 返回null
        System.out.println(book1);
        System.out.println(book2);
        System.out.println(book3);
        System.out.println(book4); // 输出null
        // 验证单例
        Book anotherBook = Book.getInstance("005", "新书", "新作者");
        System.out.println(anotherBook == null); // 输出true
    }
}解析:
- 创建Book类,包含id、name和author三个成员变量
- 使用静态变量数组存储三个实例,并使用计数器记录已创建实例数
- 将构造方法设为私有,防止外部直接实例化
- 提供静态方法getInstance()创建和获取实例,确保最多只能创建3个实例
- 包含测试代码验证单例的实现
6. QuickSort类实现(解析) 
public class QuickSort {
    private int[] array;
    
    // 构造方法,初始化数组
    public QuickSort(int[] array) {
        this.array = array;
    }
    
    // 划分方法
    public int partition(int[] r, int low, int high) {
        int pivot = r[low]; // 选择第一个元素作为基准
        
        while (low < high) {
            // 从右向左找第一个小于基准的元素
            while (low < high && r[high] >= pivot) {
                high--;
            }
            r[low] = r[high]; // 将小于基准的元素移到左端
            
            // 从左向右找第一个大于等于基准的元素
            while (low < high && r[low] < pivot) {
                low++;
            }
            r[high] = r[low]; // 将大于等于基准的元素移到右端
        }
        
        r[low] = pivot; // 将基准放到最终位置
        return low; // 返回基准的位置
    }
    
    // 递归排序方法
    public void sort(int[] r, int low, int high) {
        if (low < high) {
            int pivotPos = partition(r, low, high); // 划分
            sort(r, low, pivotPos - 1); // 排序左子数组
            sort(r, pivotPos + 1, high); // 排序右子数组
        }
    }
    
    // 快速排序方法
    public void quickSort() {
        if (array == null || array.length <= 1) {
            return; // 空数组或只有一个元素,无需排序
        }
        sort(array, 0, array.length - 1);
    }
    
    // 打印数组方法
    public void printArray() {
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + " ");
        }
        System.out.println();
    }
    
    // 测试代码
    public static void main(String[] args) {
        int[] arr = {64, 34, 25, 12, 22, 11, 90, 78, 45, 36};
        System.out.println("原始数组:");
        for (int num : arr) {
            System.out.print(num + " ");
        }
        
        QuickSort sorter = new QuickSort(arr);
        sorter.quickSort();
        
        System.out.println("\n排序后数组:");
        sorter.printArray();
    }
}解析:
- 创建QuickSort类,包含一个整型数组作为成员变量
- 实现partition方法进行划分:选择第一个元素作为基准,将小于基准的元素放在左边,大于等于基准的元素放在右边
- 实现sort方法递归地对子数组进行排序
- 实现quickSort方法作为排序入口
- 添加辅助方法printArray打印数组
- 包含测试代码验证快速排序的实现
这些解析和代码示例应该能帮助你理解2018年试题中的编程题部分。每个程序都包含了详细的注释和解释,展示了Java面向对象编程的核心概念。