面向对象程序设计 Java 试题
考试时间: 120分钟 总分: 100分

第一部分:选择题 (共20分,每题2分)
说明: 请在每道题的四个选项中选择一个最合适的答案。
-
下列关于面向对象编程的描述中,不正确的是? A. 封装是将对象的属性和操作捆绑在一起,并对外部隐藏实现细节。 B. 继承允许一个类获取另一个类的属性和方法,是实现代码复用的重要手段。 C. 多态是指同一个接口,使用不同的实例而执行不同操作的能力。 O. D. 面向对象编程的核心思想是“过程化”,即关注解决问题的步骤。
-
在Java中,以下哪个关键字用于创建一个类的实例(对象)? A.
typeB.classC.newD.instanceof -
下列哪个是Java中合法的标识符? A.
123variableB.$_variableC.classD.variable-name
(图片来源网络,侵删) -
关于
final关键字,以下说法正确的是? A.final类可以被继承。 B.final方法可以被重写。 C. C.final变量一旦被赋值,就不能再被修改。 D.final修饰的局部变量必须在声明时就初始化。 -
在Java中,
String类是? A. 基本数据类型 B. 引用数据类型,且是不可变的 C. 引用数据类型,是可变的 D. 一个接口 -
以下哪个选项是
ArrayList和LinkedList最主要的区别? A.ArrayList是线程安全的,LinkedList不是。 B.ArrayList存储的是对象,LinkedList存储的是基本数据类型。 C. C.ArrayList基于动态数组,查找快;LinkedList基于链表,增删快。 D.ArrayList有容量限制,LinkedList没有。 -
以下代码的输出是什么?
public class Test { public static void main(String[] args) { int a = 10; int b = 20; System.out.println(a + b + " is the sum."); System.out.println("The sum is " + a + b); } }A.
30 is the sum. The sum is 30B.
30 is the sum. The sum is 1020C.
10+20 is the sum. The sum is 30D. 编译错误
-
以下关于抽象类和接口的说法,错误的是? A. 一个类可以实现多个接口,但只能继承一个抽象类。 B. 接口中的方法默认是
public abstract的。 C. C. 抽象类中可以有构造方法,而接口中不能有。 D. Java 8开始,接口中可以包含默认方法和静态方法。 -
以下哪个方法可以被重写? A.
private void methodA() {}B.static void methodB() {}C.final void methodC() {}D. D.protected void methodD() {} -
在Java中,
super关键字的作用不包括? A. 调用父类的构造方法 B. 调用父类的成员变量 C. 调用父类的成员方法 D. D. 创建父类的对象
第二部分:编程题 (共50分)
说明: 请根据题目要求,编写完整的Java代码。
(15分) 设计一个简单的银行账户系统
要求:
- 创建一个
BankAccount类,包含以下属性:accountNumber(账户号,String类型)ownerName(户主姓名,String类型)balance(余额,double类型)
- 为
BankAccount类提供构造方法,用于初始化账户号和户主姓名,初始余额为0。 - 实现以下方法:
deposit(double amount): 存款方法,如果存款金额大于0,则增加余额。withdraw(double amount): 取款方法,如果取款金额大于0且小于等于余额,则减少余额;否则打印提示信息。getBalance(): 获取当前余额。displayAccountInfo(): 打印账户信息(账户号、户主、余额)。
- 在
main方法中,创建一个BankAccount对象,并进行存款和取款操作,最后显示账户信息。
// 在此编写你的代码
(20分) 实现一个员工管理系统
要求:
- 创建一个抽象类
Employee,包含:- 属性:
name(姓名),id(工号) - 构造方法:
Employee(String name, String id) - 抽象方法:
calculateSalary()(计算工资) - 普通方法:
displayInfo()(显示员工基本信息)
- 属性:
- 创建两个子类
SalariedEmployee(月薪员工) 和HourlyEmployee(时薪员工) 继承自Employee。SalariedEmployee增加属性monthlySalary(月薪),并重写calculateSalary()方法返回monthlySalary。HourlyEmployee增加属性hourlyRate(时薪) 和hoursWorked(工作小时数),并重写calculateSalary()方法返回hourlyRate * hoursWorked。
- 创建一个
EmployeeManagementSystem类,包含一个Employee类型的数组,用于存储员工对象。 - 在
main方法中,创建几个SalariedEmployee和HourlyEmployee对象,添加到系统中,并遍历数组,打印每个员工的信息和工资。
// 在此编写你的代码
(15分) 使用集合和异常处理
要求:
- 创建一个
Product类,包含productId(产品ID) 和productName(产品名称) 属性,以及相应的构造方法和toString()方法。 - 编写一个
ProductManager类,使用HashMap<String, Product>来管理产品,其中键是productId,值是Product对象。 ProductManager类提供以下方法:addProduct(Product product): 添加产品,如果产品ID已存在,则抛出一个自定义异常DuplicateProductException。getProduct(String productId): 根据ID获取产品,如果产品不存在,则抛出一个NullPointerException或自定义异常。listAllProducts(): 遍历并打印所有产品信息。
- 在
main方法中,测试ProductManager的功能,包括正常添加、重复添加异常、获取不存在的产品异常等情况,并使用try-catch块来处理异常。
// 在此编写你的代码
第三部分:简答题 (共30分)
说明: 请用简洁明了的语言回答下列问题。
- (10分) 简述Java中和
equals()方法的区别。 - (10分) 什么是多态?请结合Java代码(例如一个父类引用指向子类对象)举例说明它的好处。
- (10分) 请解释
String,StringBuilder, 和StringBuffer三者之间的主要区别。
参考答案与解析
第一部分:选择题
- D,面向对象编程的核心思想是“对象化”,将数据和处理数据的方法封装在一起,以对象为中心,而“过程化”是面向过程编程的核心思想。
- C。
new是Java中用于在堆内存上创建对象实例的关键字。 - B,标识符必须以字母、下划线
_或美元符开头,不能是数字。class是关键字,不能用作标识符。 - C。
final变量(常量)一旦被初始化,就不能再被修改。final类不能被继承,final方法不能被重写。final局部变量可以在声明时或在使用前初始化。 - B。
String是一个位于java.lang包中的类,是引用数据类型,它的所有对象都是不可变的,一旦创建,内容不能改变。 - C,这是两者最核心的区别。
ArrayList基于数组,索引查找(get)时间复杂度为O(1);LinkedList基于双向链表,增删节点(add,remove)时间复杂度为O(1),但查找需要遍历,为O(n)。 - B,Java中,字符串连接是从左到右进行的,第一句
a + b + "...",先计算a+b得到30,再与字符串连接,第二句"..." + a + b,先进行字符串连接,得到"The sum is 10",然后再与b连接,最终得到"The sum is 1020"。 - C,抽象类也是类,可以有构造方法,用于在创建子类实例时初始化父类的部分属性,接口默认没有构造方法(虽然可以有静态初始化块),接口中也不能有实例字段。
- D,可以被重写的方法不能是
private,static, 或final的。protected和public(在同一个包或不同包的子类中)的方法可以被重写。 - D。
super用于在子类中引用父类的成员(变量、方法)或调用父类的构造方法,它本身不能用来创建对象。
第二部分:编程题
设计一个简单的银行账户系统
public class BankAccount {
private String accountNumber;
private String ownerName;
private double balance;
public BankAccount(String accountNumber, String ownerName) {
this.accountNumber = accountNumber;
this.ownerName = ownerName;
this.balance = 0.0;
}
public void deposit(double amount) {
if (amount > 0) {
this.balance += amount;
System.out.println("存款成功: " + amount);
} else {
System.out.println("存款金额必须大于0。");
}
}
public void withdraw(double amount) {
if (amount > 0 && amount <= this.balance) {
this.balance -= amount;
System.out.println("取款成功: " + amount);
} else if (amount > this.balance) {
System.out.println("余额不足,取款失败。");
} else {
System.out.println("取款金额必须大于0。");
}
}
public double getBalance() {
return this.balance;
}
public void displayAccountInfo() {
System.out.println("===== 账户信息 =====");
System.out.println("账户号: " + this.accountNumber);
System.out.println("户主姓名: " + this.ownerName);
System.out.println("当前余额: " + this.balance);
System.out.println("===================");
}
public static void main(String[] args) {
BankAccount myAccount = new BankAccount("123456789", "张三");
myAccount.displayAccountInfo();
myAccount.deposit(1000);
System.out.println("当前余额: " + myAccount.getBalance());
myAccount.withdraw(500);
System.out.println("当前余额: " + myAccount.getBalance());
myAccount.withdraw(600); // 测试余额不足的情况
myAccount.displayAccountInfo();
}
}
实现一个员工管理系统
// 抽象类 Employee
abstract class Employee {
protected String name;
protected String id;
public Employee(String name, String id) {
this.name = name;
this.id = id;
}
public abstract double calculateSalary();
public void displayInfo() {
System.out.println("员工ID: " + id + ", 姓名: " + name);
}
}
// 子类 SalariedEmployee
class SalariedEmployee extends Employee {
private double monthlySalary;
public SalariedEmployee(String name, String id, double monthlySalary) {
super(name, id);
this.monthlySalary = monthlySalary;
}
@Override
public double calculateSalary() {
return monthlySalary;
}
@Override
public void displayInfo() {
super.displayInfo();
System.out.println("类型: 月薪员工, 月薪: " + monthlySalary);
}
}
// 子类 HourlyEmployee
class HourlyEmployee extends Employee {
private double hourlyRate;
private double hoursWorked;
public HourlyEmployee(String name, String id, double hourlyRate, double hoursWorked) {
super(name, id);
this.hourlyRate = hourlyRate;
this.hoursWorked = hoursWorked;
}
@Override
public double calculateSalary() {
return hourlyRate * hoursWorked;
}
@Override
public void displayInfo() {
super.displayInfo();
System.out.println("类型: 时薪员工, 时薪: " + hourlyRate + ", 工作时长: " + hoursWorked);
}
}
// 管理系统类
public class EmployeeManagementSystem {
public static void main(String[] args) {
Employee[] employees = new Employee[3];
employees[0] = new SalariedEmployee("李四", "E001", 15000);
employees[1] = new HourlyEmployee("王五", "E002", 100, 160);
employees[2] = new SalariedEmployee("赵六", "E003", 20000);
System.out.println("--- 员工工资单 ---");
for (Employee emp : employees) {
emp.displayInfo();
System.out.println("应发工资: " + emp.calculateSalary());
System.out.println("-----------------");
}
}
}
使用集合和异常处理
// 自定义异常
class DuplicateProductException extends Exception {
public DuplicateProductException(String message) {
super(message);
}
}
// Product 类
class Product {
private String productId;
private String productName;
public Product(String productId, String productName) {
this.productId = productId;
this.productName = productName;
}
@Override
public String toString() {
return "ID: " + productId + ", 名称: " + productName;
}
}
// ProductManager 类
import java.util.HashMap;
class ProductManager {
private HashMap<String, Product> productMap;
public ProductManager() {
this.productMap = new HashMap<>();
}
public void addProduct(Product product) throws DuplicateProductException {
if (productMap.containsKey(product.getProductId())) {
throw new DuplicateProductException("产品ID " + product.getProductId() + " 已存在!");
}
productMap.put(product.getProductId(), product);
System.out.println("产品添加成功: " + product);
}
public Product getProduct(String productId) throws NullPointerException {
Product product = productMap.get(productId);
if (product == null) {
throw new NullPointerException("产品ID " + productId + " 不存在!");
}
return product;
}
public void listAllProducts() {
System.out.println("--- 所有产品列表 ---");
if (productMap.isEmpty()) {
System.out.println("暂无产品。");
} else {
for (Product p : productMap.values()) {
System.out.println(p);
}
}
System.out.println("--------------------");
}
// 假设Product类有getter
public String getProductId() { return productId; } // 需要添加到Product类中
public String getProductName() { return productName; } // 需要添加到Product类中
}
// 测试类
public class TestProductManager {
public static void main(String[] args) {
ProductManager manager = new ProductManager();
try {
manager.addProduct(new Product("P001", "笔记本电脑"));
manager.addProduct(new Product("P002", "无线鼠标"));
// 尝试添加重复ID的产品
manager.addProduct(new Product("P001", "键盘")); // 会抛出异常
} catch (DuplicateProductException e) {
System.out.println("错误: " + e.getMessage());
}
manager.listAllProducts();
try {
Product p = manager.getProduct("P002");
System.out.println("找到产品: " + p);
// 尝试获取不存在的产品
Product pNotFound = manager.getProduct("P999"); // 会抛出异常
} catch (NullPointerException e) {
System.out.println("错误: " + e.getMessage());
}
}
}
注意:为了让ProductManager的getProduct方法能正确编译,需要在Product类中添加getProductId()和getProductName()方法。
第三部分:简答题
-
和
equals()的区别- :
- 如果比较的是基本数据类型(如
int,double),它比较的是两个值是否相等。 - 如果比较的是引用数据类型(如
Object,String),它比较的是两个引用变量是否指向内存中的同一个对象(即地址是否相同)。
- 如果比较的是基本数据类型(如
equals():equals()是Object类中的一个方法,用于比较两个对象在“逻辑上”是否相等。Object类中的equals()方法默认实现也是使用来比较,即比较地址。- 许多类(如
String,Integer,ArrayList等)都重写了equals()方法,以实现更有意义的逻辑比较。String类的equals()方法比较的是字符串内容是否相同。
- 比较的是内存地址(引用)或值,而
equals()比较的是对象的内容(逻辑相等),对于自定义类,如果不重写equals(),其行为和一样。
- :
-
什么是多态?举例说明好处。
-
定义: 多态是同一个行为具有多个不同表现形式或形态的能力,在Java中,多态通常指“一个父类类型的引用,可以指向其任何一个子类的对象”,并且通过这个引用调用方法时,会执行子类重写后的版本。
-
代码示例:
class Animal { public void makeSound() { System.out.println("动物发出声音"); } } class Dog extends Animal { @Override public void makeSound() { System.out.println("汪汪汪!"); } } class Cat extends Animal { @Override public void makeSound() { System.out.println("喵喵喵!"); } } public class TestPolymorphism { public static void main(String[] args) { Animal myDog = new Dog(); // 父类引用指向子类对象 Animal myCat = new Cat(); myDog.makeSound(); // 输出: 汪汪汪! myCat.makeSound(); // 输出: 喵喵喵! } } -
好处:
- 提高代码的可扩展性和灵活性: 当需要增加新的子类(如
Bird)时,不需要修改调用makeSound()方法的代码,只要Bird也继承Animal并重写makeSound()即可。 - 提高代码的通用性: 可以编写一个方法,它接收父类类型的参数,这样就可以接受任何子类的对象,大大减少了代码的重复。
public void letAnimalMakeSound(Animal animal) { animal.makeSound(); } letAnimalMakeSound(new Dog()); letAnimalMakeSound(new Cat());
- 提高代码的可扩展性和灵活性: 当需要增加新的子类(如
-
-
String,StringBuilder,StringBuffer的区别String:- 不可变性:
String对象一旦创建,其内容就不能被修改,任何修改String的操作(如concat,replace)都会生成一个新的String对象。 - 线程安全: 因为不可变,所以天生是线程安全的。
- 适用场景: 适用于少量字符串操作,或作为常量使用,频繁修改字符串会造成性能问题和内存浪费。
- 不可变性:
StringBuilder:- 可变性:
StringBuilder可以被修改,它是一个可变的字符序列。 - 线程安全: 非线程安全。
- 性能: 由于没有线程安全方面的开销,它的性能通常比
StringBuffer要好,尤其是在单线程环境下。 - 适用场景: 在单线程环境下进行大量的字符串拼接、修改操作。
- 可变性:
StringBuffer:- 可变性:
StringBuffer也是可变的。 - 线程安全: 线程安全,它的大部分方法都使用了
synchronized关键字进行同步。 - 性能: 因为有线程安全的开销,所以性能比
StringBuilder稍差。 - 适用场景: 在多线程环境下,如果需要共享和修改字符串内容。
- 可变性:
-
| 特性 |
String|StringBuilder|StringBuffer| | :--- | :--- | :--- | :--- | | 可变性 | 不可变 | 可变 | 可变 | | 线程安全 | 安全 | 不安全 | 安全 | | 性能 | (修改时差) | 高 | 较低 | | 适用场景 | 少量操作,常量 | 单线程,大量修改 | 多线程,大量修改 |
