简介:这份《面向对象程序设计JAVA语言程序设计》期末考试试题及部分答案,面向Java课程复习阶段的本科、高职学生,可作考前自测与查漏补缺之用。资源已整理为单文件PDF文档,压缩包约437KB,体积精炼,便于手机、平板或电脑端随时翻阅。试题分为十六个选择题与八个填空题,内容覆盖类与对象、继承关系、方法重载与成员覆盖、接口、异常处理、switch执行逻辑、浮点数比较精度、String与StringBuffer区别以及Unicode编码等高频考点,选择题侧重易混淆概念辨析,填空题侧重基础语法与机制记忆,部分题目附有参考答案,便于边练边核对、快速定位薄弱环节。目前已有1586人学习下载,对掌握Java面向对象思想并应对期末笔试具有较强参考价值。
1. 这门期末考试真正要交差的是“用 Java 写出面向对象的程序”,不是背语法
《面向对象程序设计JAVA语言程序设计》这类课程的名称往往比实际考试内容长,但期末试卷却非常统一:选择、填空、简答、读程序、编程题。很多学生复习时死记硬背“面向对象特征有封装继承多态”,结果一拿到读程序题就判断错重载与重写的调用结果,或者写五行的类定义都能把构造方法名写错。其实这门课的考试目标很实际:考察你是否能用 Java 语言把现实问题拆成对象、用类组织状态和行为、用继承和多态解决扩展问题。换句话说,面向对象程序设计考的是“抽象出来的代码长什么样”,而不是概念原文。文章会按试卷常见题型拆考点,每一节给出可直接复用的代码和参数说明,最后告诉你如何在考场上快速验证一个拿不准的 Java 语法点,这对已经工作但想补基础的 Java 工程师也有参考价值,因为很多 Java 面试题就是从这些期末考试题变体来的。
2. 封装、继承、多态:试卷里反复出现的三个得分区
所谓理论先行,但是考试里最直接给分的题目往往是把理论放到代码里让你判断。我们需要把三个特性拆开看,每一个都对应一种固定的考法。
2.1 封装:私有成员变量与 getter/setter 的固定写法
封装的考点非常单调,主要考访问权限和对外提供方法。最典型的一道填空题是:“成员变量使用______修饰,外部不能直接访问,只能通过______方法访问。”答案就是 private 和 getter/setter。但编程题不会让你填空,它会要求你写一个类,把属性设为私有并提供访问器。
下面是最标准的实现模板:
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { // 这里可以做有效性校验 if (age < 0 || age > 150) { throw new IllegalArgumentException("年龄不合法"); } this.age = age; } }这段代码要注意三个细节。第一,字段全部 private,这是封装的第一道边界;第二,构造方法可以有多个,无参构造和带参构造可以共存,这一点经常考;第三,setter 里可以加参数校验,这是加分项,说明你理解封装的意义在于控制数据有效性。Java 中访问修饰符的可访问范围是一个容易出选择题的点,需要记忆的关系如下:
| 修饰符 | 同类内 | 同包内 | 子类跨包 | 全局 |
|---|---|---|---|---|
| private | 可访问 | 不可 | 不可 | 不可 |
| 默认(不写) | 可访问 | 可访问 | 不可 | 不可 |
| protected | 可访问 | 可访问 | 可访问 | 不可 |
| public | 可访问 | 可访问 | 可访问 | 可访问 |
注意,protected 允许在同一包内任意访问,也允许不同包内的子类通过继承关系访问,但不能通过父类引用来访问子类对象里继承来的 protected 成员。这种细节是简答题“为什么使用封装”之后最常见的引申考点。
2.2 继承:构造器调用顺序和 super 的行号问题
继承部分的读程序题通常是这样的:父类和子类都有构造器,然后 new 子类对象,问你输出顺序。比如下面代码:
class Animal { public Animal() { System.out.println("Animal 构造器"); } } class Dog extends Animal { public Dog() { // 这里其实隐式调用了 super() System.out.println("Dog 构造器"); } } public class Main { public static void main(String[] args) { Dog d = new Dog(); } }输出结果是:
Animal 构造器 Dog 构造器原因是:子类构造器执行之前必须完成父类构造。Java 编译器会在子类构造器第一行自动插入super(),除非你显式写了super(参数)或this(参数)。如果父类没有无参构造而子类构造器又没有显式调用 super,编译会直接报错。这常被拿来写改错题:父类只有带参构造,子类构造器没有写super(name),编译无法通过。
如果你想控制父类构造参数,写法是:
class Cat extends Animal { public Cat(String name) { super(name); // 必须放在第一行 } }这里第一行只能写super(参数)或者this(参数),不能同时写。另一个考点是:静态成员是否被继承?Java 中静态成员可以被子类通过继承访问,但本质上静态成员属于类本身,不参与重写。读程序题里如果父类和子类都有同名的 static 方法,通过父类引用调用时执行的是父类版本,这与实例方法的重写行为不同。
2.3 多态:向上转型和向下转型的边界条件
多态是三个特性中考试权重最高的。选择题的高频考法如下:
class Father { void say() { System.out.println("Father"); } } class Son extends Father { @Override void say() { System.out.println("Son"); } } Father obj = new Son(); obj.say();这段代码输出Son。原理是:编译期只检查引用类型 Father 有没有say()方法,运行期则调用实际对象 Son 的版本,这叫做动态绑定。如果子类新增了一个方法walk(),那么Father obj = new Son(); obj.walk();会编译失败,因为 Father 中没有这个方法。
至于向下转型,前提是对象原本就是子类实例。典型错误题是:
Father f = new Father(); Son s = (Son) f; // 运行时抛出 ClassCastException记住一句话:向上转型是安全的,向下转型必须先用instanceof做检查。考试中如果你不确定某个引用调用的结果是哪一个类的方法,直接画一条“引用指向对象”的线,看方法的定义从谁开始找,运行版本找实际对象。
3. 重载重写、final 和 static:易错选择题背后的语法边界
复习到一定阶段你会发现,选择填空的失分点基本集中在这几个语法特性上。它们单独看都容易理解,放在一起就是一组一组的“坑”。
3.1 重载与重写的区别:从方法签名到异常限制
重载是在同一个类里方法名相同、参数列表不同;重写是子类对父类同名同参方法的重新实现。以下代码同时出现两种现象:
class Base { public String getInfo() { return "Base"; } } class Child extends Base { // 方法名相同,参数不同,这是重载吗?不是,因为一个是继承,一个是新增。 public String getInfo(int x) { return "Child param: " + x; } // 参数列表与父类一致,这是重写 @Override public String getInfo() { return "Child"; } }考试判断题可能会说“子类中定义一个和父类同名但参数不同的方法,是重写”,这是错的,应该叫重载。重载与重写可以用表格区分:
| 对比项 | 重载 | 重写 |
|---|---|---|
| 位置 | 同一个类内 | 子类和父类之间 |
| 方法名 | 相同 | 相同 |
| 参数列表 | 必须不同 | 必须完全一致 |
| 返回类型 | 可以不同,不限制 | 必须相同,或父类返回类型的子类型 |
| 访问权限 | 任意 | 不能比父类更严格 |
| 抛出异常 | 无限制 | 不能抛出比父类更宽泛的异常 |
这个表格中的前四行是必须背下来的。做题时如果看到“重写方法的返回类型可以不同”,反例是:父类返回Number,子类返回Integer是允许的,这叫做协变返回类型。但如果父类返回Object,子类返回String也可以,因为 String 是 Object 的子类。如果父类返回String,子类返回Object就编译错误。
3.2 final 和 static 的组合陷阱:初始化顺序与不可变
final修饰变量表示常量,修饰方法表示不能重写,修饰类表示不能继承。static修饰成员表示属于类而不是实例。两个关键字放在一起时,最常考的题目是“以下哪种写法会编译失败”。例如:
class Demo { static final int MAX = 10; // 正确,编译期常量 final static int MIN; // 正确,static final 可以在静态代码块初始化 static { MIN = 1; } final int count; // 正确,final 实例变量可以在构造器初始化 // 下面这行错误:static final 变量不能在构造器里赋值 // public Demo() { MIN = 2; } }关于初始化顺序,读代码题里经常出现静态代码块、实例代码块和构造器的输出顺序。简单规则是:程序加载类时先执行 static 变量和 static 代码块,顺序按代码书写顺序;创建对象时先执行实例变量和实例代码块,再执行构造器。如果子类继承父类,则先执行父类的静态块,再执行子类的静态块,创建对象时先执行父类的实例块和构造器,再执行子类的实例块和构造器。这种题目花三分钟手写一遍过程,基本不会错。
3.3 this 和 super 的定位差异:简答题的标准答法
简答题常问“this 和 super 的区别”。标准答案需要写三点:第一,this 指向当前对象引用,super 是父类对象的引用,但更准确的说法是“用 super 来访问父类成员”;第二,this()调用当前类构造器,super()调用父类构造器,两者都必须写在构造器第一行,所以不能同时出现;第三,this 可以单独作为返回值返回当前对象,super 不能单独存在并传递。比如写链式调用时:
public class Person { private int age; public Person setAge(int age) { this.age = age; return this; } }但如果你试图用 super 做类似的事情,写成return super;,编译不会通过,因为 super 不是对象引用,它只是“从当前对象视角访问父类成员”的语法标记。这一点答题时最好明确说“super 不是引用变量”,面试里也很容易作为 Java 面试题出现。
4. 综合编程大题:从类设计到集合输出的完整解题流程
期末考试最后一道题通常是合并考察:封装、构造器、集合、排序、输入输出。题目往往类似“设计一个图书管理类,实现添加图书、按价格排序输出”。这个部分我们要把整个解法和代码逻辑都梳理一遍,因为编程题给分的点非常固定。
4.1 典型题目和最小可运行代码
题目要求:定义 Book 类,属性书名和价格,进行封装;定义 Library 类,内部用 ArrayList 保存 Book;通过构造器传入初始数据,添加 addBook 方法,按价格升序输出。
import java.util.ArrayList; import java.util.Comparator; import java.util.List; public class Book { private String title; private double price; public Book(String title, double price) { this.title = title; this.price = price; } public String getTitle() { return title; } public double getPrice() { return price; } @Override public String toString() { return title + ":" + price; } } class Library { private List<Book> books; public Library() { books = new ArrayList<>(); } public void addBook(Book book) { books.add(book); } public void sortByPrice() { books.sort(Comparator.comparingDouble(Book::getPrice)); } public void printBooks() { for (Book b : books) { System.out.println(b); } } } public class Main { public static void main(String[] args) { Library lib = new Library(); lib.addBook(new Book("Java程序设计", 89.5)); lib.addBook(new Book("数据结构", 59.0)); lib.addBook(new Book("操作系统", 78.0)); lib.sortByPrice(); lib.printBooks(); } }这段代码使用了ArrayList的sort方法和Comparator.comparingDouble函数式接口,这是比较新的 Java 8 以后写法。如果你的教材版本比较老,试卷可能要求实现Comparable接口:
public class Book implements Comparable<Book> { // 省略其他代码 @Override public int compareTo(Book other) { return Double.compare(this.price, other.price); } }注意Double.compare是必须的,因为直接写(int)(this.price - other.price)在价格差小于 1 时会截断为 0,导致排序错误。这个坑在编程题里几乎每年能看到几个学生踩中。
4.2 阅卷给分点:你的代码哪一行在得分
综合题的给分原则并不是只要求能运行。大多数学校按以下明细给分:
| 考点 | 分数分布 | 常见扣分原因 |
|---|---|---|
| 类的封装(私有字段+getter/setter) | 30% | 字段写成 public 或没有 setter |
| 构造器定义 | 15% | 构造器方法和类名不一致 |
| 集合的创建和增删 | 20% | 直接使用数组,没有体现集合优势 |
| 排序逻辑 | 25% | 排序后忘记写输出,或 Comparator 使用错误 |
| 代码可读性和命名规范 | 10% | main 方法里堆大量逻辑 |
如果你想要一个稳妥的答题结构,建议先写实体类 Book,再写管理类 Library,最后在 main 里调用。不要把所有代码都写进 main,这会直接丢掉可读性分数。同时注意:Book的字段定义为private,然后提供getter,如果需要修改再提供setter。很多同学习惯把 setter 全部写上,但有些字段在对象初始化之后根本不应该变,比如图书的 ID,这类字段只提供 getter 就好。这也是考试中“设计是否正确”的隐含考察点。
4.3 异常处理与常见秋招 Java 面试题的交叉
编程题有时还会附加问一句“如果添加的图书价格为负数,程序会不会崩溃?”如果你有异常处理意识,可以在 setPrice 中加校验:
public void setPrice(double price) { if (price < 0) { throw new IllegalArgumentException("价格不能为负数"); } this.price = price; }然后调用方可以用 try-catch 捕获。这一部分考点其实和 Java 后端开发的日常非常接近。换句话说,期末考试不只是学期任务,很多基础概念这些年直接被拿来当 Java 面试八股文,比如“接口和抽象类的区别”“ArrayList 和 LinkedList 区别”“泛型的协变逆变”。如果你已经工作了几年,偶尔回头翻翻这套题,对那些基础问题的回答会更扎实。
5. 考场上验证一个拿不准的 Java 语法点:JShell 就是你的草稿纸
最后一章,我们专注于一个具体技巧:遇到不确定的语法,不要靠猜,直接用 JShell 在一秒内得到结果。JShell 是 Java 9 之后自带的 REPL 工具,期末考试环境下即使没有 IDE,命令行里也大概率能用,因为 JDK 安装目录的 bin 下会有 jshell 命令。它的价值在于,你不必新建 java 文件,不用写完整类结构,可以直接输入语句验证。
比如你想确认static方法能不能访问实例变量,进入 JShell 后输入:
jshell> class Foo { int x = 1; static void m() { System.out.println(x); } }JShell 会直接报错,提示非静态变量不能从静态上下文中引用。如果你想验证向上转型调用的是哪个方法,直接写:
jshell> class A { void f() { System.out.println("A"); } } jshell> class B extends A { void f() { System.out.println("B"); } } jshell> A a = new B(); jshell> a.f();输出 B,结果一目了然。用 JShell 还有一个好处:它可以执行多行代码块,支持定义变量、方法、类,甚至能重复定义同名类,这对复习期间做零散验证非常方便。进入 JShell 后输入/vars可以查看所有变量,/methods可以查看已定义方法,/open可以加载外部文件。如果你所在机房没有 JShell,也可以在 Linux 机器上使用java -X查看可用参数,但最接近 JShell 的就是这个工具。
关于手写代码时的验证顺序,建议一个动作:读代码题先画出继承关系树,再标出哪些方法被重写,哪些是重载,最后确认引用类型和实际类型。编程题写完后,如果允许编译,第一时间检查类名和文件名是否一致,然后是构造器首行有没有隐式的 super 调用,最后是泛型集合的导入语句。最快的自测方法是把主方法里每个步骤的注释写在旁边,比如“创建集合对象”,注释本身能帮你发现逻辑断点。把这些动作养成习惯,期末考试的“部分答案”其实就藏在你对自己的代码做的一次次追问里,考场上的每一分都来自平时确信过的那一行。
本文还有配套的精品资源,点击获取