北京理工大学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.
5x
B.$x
C.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.
BufferedReader
B.DataInputStream
C.DataOutputStream
D.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面向对象编程的核心概念。