Skip to content

北京理工大学2019-2020学年第二学期2018级面向对象程序设计(Java语言)期末试题(A卷)

班级________ 学号________ 姓名________ 成绩________

一、填空题(每空1分,共10分)

  1. Java中的垃圾回收机制消除了程序中的内存泄露memory leak)问题。
  2. Java使用关键字super来引用当前class所继承的基类。
  3. 将方法声明为final意味着它不能被继承。
  4. 封装是指把数据成员和相关的方法都放进一个类中,并使用访问权限来控制其可见性。
  5. 单例模式的作用保证一个类仅有一个实例,并提供一个全局访问点
  6. Java不直接支持多继承,但可以通过接口实现多继承。
  7. 获取一个文件目录下文件列表的方法是File类的listFiles()方法。
  8. Java中的数据类型主要分为基本类型引用类型
  9. Math.round(-11.5)的返回值是-11
  10. List接口中查询速度较快的是ArrayList类。

二、判断正误并说明原因(每题2分,共10分)

  1. float x1=1.414f;”语句中等号右边的f是多余的,因为1.414本身就是float类型的值。()
  2. 数组有length()方法。()
  3. Java语言的跨平台性使得它不需要提供sizeof()运算符。()
  4. 内部类inner class)可以访问其外部类enclosing class)中的private成员。()
  5. 构造器可以被重写()

三、单选题(每题1分,共5分)

  1. 下面哪个是Java语言中正确的标识符()。 A. 5x B. $x C. abc@ D. (com
  2. 下列说法错误的是(): A. 数组可以动态初始化; B. 数组可以赋值给Object类型的对象; C. 数组创建以后,其长度可以修改; D. 数组可以调用Object类的所有方法;
  3. 假定类A有一个方法void method(),如果要求能够通过类名A直接调用method(),则其方法定义应该为() A. static void method() B. public void method() C. final void method() D. abstract void method()
  4. 关于Java中的多态,以下说法不正确的为()。 A. 多态不仅可以减少代码量,还可以提高代码的可扩展性和可维护性 B. 把子类转换为父类,称为向下转型,自动进行类型转换 C. 多态是指同一个实现接口,使用不同的实例而执行不同的操作 D. 继承多态的基础,没有继承就没有多态.
  5. 按照指定的格式向文件写入数据,所用的处理流是() A. BufferedReader B. DataInputStream C. DataOutputStream D. ObjectInputStream

四、写出程序运行结果(共20分)

  1. // StaticInitialization.java (每空1分,共6分)
java
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的源代码:

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分)

java
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);
    }
}
  1. //AlwaysFinally.java (5分)
java
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分)

  1. 按照要求,补齐代码(5分)
java
interface Inter { void show(); }
class Outer {
    //请在此处补齐代码 
}
class OuterDemo {
    public static void main(String[] args) {
        Outer.method().show();
    }
}

要求在控制台输出"HelloWorld" 2. 请简述重载重写的区别?(5分) 3. 在以下代码中,Parcel类以匿名内部类的方式实现了Destination接口,请改用普通内部类的方式实现之。(5分)

java
// 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; }
        };
    }
}
  1. 简述Java中异常处理的机制(5分)

六、编程题(共35分)

  1. 按以下要求编写程序(5分)
    • 创建一个Rectangle类,添加widthheight两个成员变量
    • Rectangle中添加两种方法分别计算矩形的周长和面积
    • 编程利用Rectangle输出一个矩形的周长和面积
  2. 按以下要求编写程序(5分)
    • 编写Animal接口,接口中声明run() 方法
    • 定义Bird类和Fish类实现Animal接口
    • 编写Bird类和Fish类的测试程序,并调用其中的run()方法
  3. 请设计一个表示电话号码(如010-68911205)的类Phone,其中设置相应的数据成员,分别用来表示区号(area,如010)和8位号码(number,如68911205)。该类包含两个构造函数:第一个为default constructor;第二个构造函数的参数应为表示完整电话号码的字符串,函数中使用String.indexOf()String.substring()方法或者java.util.StringTokenizer类的方法来获取各数据成员的值。类中要求实现相应的setget方法来分别设置及获取数据成员的值,并重写Object.toString()函数,使之返回完整的电话号码字符串。(6分)
  4. 文件records.dat中存放着某班学生数学课程的期末考试成绩,每条记录占一行,形如"学号, 姓名, 分数"。请完成以下程序,从文件中逐行读取每个学生的相关数据,并把文件中的每行信息转换成"学号--分数"的形式,存入一个类型为ArrayList<String>的容器对象中。接着遍历该容器对象的内容,将其中存放的字符串逐个输出到命令行中。(7分)
java
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>();
        …请在此处填充代码,将程序补充完整…
    }
}
  1. 创建一个图书类(Book),并在该类中定义3个成员变量图书编号(id)、图书名称(name)以及作者(author),使用单例模式实现仅能创建Book类的3个实例。(5分)
  2. 请设计并实现类QuickSort,使用递归方法实现快速排序算法。快速排序的基本思想为:将原问题分解为若干个规模更小但结构与原问题相似的子问题;递归求解这些子问题,然后将这些子问题的解组合为原问题的解。快速排序可分为三个步骤,即划分、求解和组合。QuickSort类中包含三个方法:partition方法用于对待排序数组r[low…high]做划分,即以r[low]为基准,将所有小于该基准的元素移到其前端,所有大于或等于该基准的元素移到其后端,这样就将该基准放到了最终有序的位置上;sort方法用于对数组r[m…n]进行递归排序;quickSort方法使用前两个方法对类中的数据成员(包含10个元素的int数组)进行排序。(7分)

答案与解析

(一)填空题

  1. 垃圾回收Garbage Collection:Java的垃圾回收机制自动回收不再被引用的对象所占用的内存,有效避免内存泄露
  2. super:通过super关键字可以访问父类的成员,包括属性和方法等。
  3. final:被final修饰的方法不能被继承,子类无法对其进行重写。
  4. 数据成员(或成员变量:封装就是将数据和操作数据的方法组合在一个类中,并利用访问权限控制外界对其的访问。
  5. 保证一个类仅有一个实例,并提供一个全局访问点:在某些场景下,确保类只有一个实例可以避免资源的重复占用和数据不一致等问题。
  6. 接口interface:Java类虽然不能多继承,但一个类可以实现多个接口,从而达到类似多继承的效果。
  7. File类的listFiles()方法:使用File类创建目录对象后,调用listFiles()方法可获取该目录下的文件和子目录列表。
  8. 引用类型:Java数据类型分为基本类型(如intfloatchar等)和引用类型(如类、接口、数组等)。
  9. -11Math.round()方法是四舍五入取整,-11.5四舍五入后为-11
  10. ArrayListArrayList基于数组实现,随机访问效率高,适合频繁查询操作;LinkedList基于链表实现,插入和删除操作效率较高。

(二)判断正误并说明原因

  1. 错误:在Java中,1.414默认是double类型常量,若要赋值给float类型变量,必须加上后缀f,否则会报错。
  2. 错误:数组有length属性,用于获取数组的长度,不是length()方法,length()方法一般用于String类获取字符串长度。
  3. 正确:Java语言跨平台,其数据类型的长度是固定的,不依赖于具体的机器硬件,所以不需要sizeof()运算符来获取数据类型的大小。
  4. 正确内部类可以访问外部类的所有成员,包括private成员,这是内部类的特性之一。
  5. 错误构造器不能被重写,因为构造器的名称必须与类名相同,且重写要求方法签名(方法名、参数列表)相同,子类构造器与父类构造器名称不同,所以无法重写。

(三)单选题

  1. B:Java标识符可以由字母、数字、下划线(_)和美元符号($)组成,但不能以数字开头,也不能是关键字,所以$x是正确的标识符。
  2. C:数组创建后,其长度是固定的,不能修改;数组可以动态初始化、赋值给Object类型的对象,也可以调用Object类的所有方法。
  3. A:能通过类名直接调用的方法必须是静态方法,用static修饰。
  4. B:把子类转换为父类,称为向上转型,是自动进行类型转换;向下转型是把父类对象转换为子类对象,需要强制类型转换,且存在风险,所以B选项说法错误。
  5. CDataOutputStream用于按照指定格式向文件写入基本数据类型的数据;BufferedReader用于从字符输入流中读取文本;DataInputStream用于从输入流中读取基本数据类型的数据;ObjectInputStream用于从输入流中读取对象。

(四)写出程序运行结果

  1. 输出结果
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类的静态成员b1b2初始化,输出Bowl(1)Bowl(2)Cupboard类的静态成员b4初始化,输出Bowl(4)。接着执行main方法,创建Cupboard对象,初始化b3,输出Bowl(3),执行Cupboard构造方法,输出Cupboard();创建Table对象,初始化b5,输出Bowl(5),执行Table构造方法,输出Table()。之后静态成员t2t3初始化,重复上述部分过程。 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)

解析:a2a1指向同一个数组,对a2元素自增会影响a1。第二个for循环中,数组下标从03,当i4时,会发生数组越界异常。 3. 输出结果

long: 8
float: 1.2

解析:方法调用时,根据参数类型选择合适的重载方法。8int类型,可自动转换为long,调用method(long i)1.2ffloat类型,调用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. 按照要求,补齐代码(补充解析)

完整代码如下:

java
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. 重载和重写的区别(补充解析)

  1. 定义不同

    • 重载(Overloading):同一个类中定义多个名称相同但参数列表不同的方法
    • 重写(Overriding):子类重新实现父类中已有的方法
  2. 条件不同

    • 重载:方法名相同,参数类型、个数或顺序不同,返回值类型可以相同也可以不同
    • 重写:方法名、参数列表和返回值类型必须相同(Java 5后返回值可以是父类方法返回值的子类型)
  3. 访问权限

    • 重载:无特殊限制
    • 重写:子类方法的访问权限不能小于父类方法的访问权限
  4. 异常抛出

    • 重载:无特殊限制
    • 重写:子类方法抛出的异常不能超过父类方法的异常范围
  5. 多态类型

    • 重载:编译时多态(静态绑定)
    • 重写:运行时多态(动态绑定)

3. 匿名内部类改为普通内部类(补充解析)

java
// 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();
    }
}

解析:将匿名内部类改为普通内部类,需要:

  1. 创建一个有名字的内部类(这里命名为PDestination
  2. 实现Destination接口
  3. 将原匿名内部类中的实现移到这个具名内部类中
  4. dest()方法中返回这个内部类的实例

4. Java中的异常处理机制(补充解析)

Java的异常处理机制主要包括以下几个方面:

  1. 异常体系

    • 所有异常都是Throwable类的子类
    • Error:严重问题,程序通常无法处理(如OutOfMemoryError
    • Exception:程序可以处理的异常
      • 检查异常(Checked Exception):必须显式处理(如IOException
      • 非检查异常(Unchecked Exception):运行时异常,不强制处理(如NullPointerException
  2. 异常处理语句

    • try-catch:捕获并处理异常
    • try-catch-finally:无论是否发生异常,finally块都会执行
    • try-with-resources:自动关闭资源
    • throw:手动抛出异常
    • throws:声明方法可能抛出的异常
  3. 异常处理原则

    • 尽早捕获,尽可能晚地抛出
    • 只捕获能够处理的异常
    • 不要忽略异常
    • 合理使用自定义异常
  4. 异常处理好处

    • 提高程序健壮性
    • 与正常代码逻辑分离
    • 提供良好的错误处理机制
    • 方便调试和维护

(六)编程题

1. Rectangle类及应用(解析)

java
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());
    }
}

解析:

  1. 创建Rectangle类,包含widthheight两个成员变量
  2. 提供构造方法初始化矩形的宽和高
  3. 实现计算周长方法:perimeter() = 2 * (width + height)
  4. 实现计算面积方法:area() = width * height
  5. main方法中创建矩形对象并调用方法计算周长和面积

2. Animal接口及实现类(解析)

java
// 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();
    }
}

解析:

  1. 定义Animal接口,声明run()方法
  2. 创建Bird类实现Animal接口,重写run()方法描述鸟类的运动方式
  3. 创建Fish类实现Animal接口,重写run()方法描述鱼类的运动方式
  4. 在测试类中创建两种动物对象并调用其run()方法

3. Phone类设计(解析)

java
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);
    }
}

解析:

  1. 创建Phone类,包含areanumber两个成员变量
  2. 实现默认构造函数,初始化为空字符串
  3. 实现带参数的构造函数,解析完整电话号码字符串,提取区号和号码
  4. 提供getset方法访问成员变量
  5. 重写toString()方法,返回完整电话号码字符串
  6. 测试代码验证类的功能

4. 文件读取与处理(解析)

java
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);
        }
    }
}

解析:

  1. 使用BufferedReader读取文件records.dat
  2. 逐行读取文件内容,处理每一行数据
  3. 使用split(",")分割每行数据,获取学号、姓名和分数
  4. 将学号和分数组合成"学号--分数"的格式,添加到ArrayList
  5. 关闭读取资源
  6. 遍历ArrayList,输出每个元素到控制台

5. Book类单例模式实现(解析)

java
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
    }
}

解析:

  1. 创建Book类,包含idnameauthor三个成员变量
  2. 使用静态变量数组存储三个实例,并使用计数器记录已创建实例数
  3. 将构造方法设为私有,防止外部直接实例化
  4. 提供静态方法getInstance()创建和获取实例,确保最多只能创建3个实例
  5. 包含测试代码验证单例的实现

6. QuickSort类实现(解析)

java
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();
    }
}

解析:

  1. 创建QuickSort类,包含一个整型数组作为成员变量
  2. 实现partition方法进行划分:选择第一个元素作为基准,将小于基准的元素放在左边,大于等于基准的元素放在右边
  3. 实现sort方法递归地对子数组进行排序
  4. 实现quickSort方法作为排序入口
  5. 添加辅助方法printArray打印数组
  6. 包含测试代码验证快速排序的实现

这些解析和代码示例应该能帮助你理解2018年试题中的编程题部分。每个程序都包含了详细的注释和解释,展示了Java面向对象编程的核心概念。