第一部分:Java 异常机制
一、异常的基本概念与体系结构
异常指程序运行时出现的非正常情况(如空指针、数组越界),会中断程序执行。Java 把异常封装成对象,通过统一的机制进行处理。
1. 异常的继承体系
所有异常的根类是 java.lang.Throwable,它分为两大分支:
- Error(错误) :系统级严重问题,程序无法处理,如
OutOfMemoryError(内存溢出)、StackOverflowError(栈溢出),一般不需要代码捕获。 - Exception(异常) :程序本身可以处理的问题,又分为两类:
- 运行时异常(非受检异常) :
RuntimeException及其子类,编译阶段不强制处理,运行时才触发,如空指针、数组越界。 - 编译时异常(受检异常) :除运行时异常外的其他
Exception子类,编译阶段必须处理(捕获或抛出),否则代码报错,如IOException、SQLException。
- 运行时异常(非受检异常) :
2. 核心区分:运行时异常 vs 编译时异常
表格
| 类型 | 触发时机 | 编译要求 | 常见例子 |
|---|---|---|---|
| 运行时异常 | 程序运行时 | 不强制处理 | NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException |
| 编译时异常 | 编译阶段 | 必须捕获 / 声明抛出 | IOException、ParseException、ClassNotFoundException |
二、异常的核心处理机制
Java 处理异常有 5 个核心关键字:try、catch、finally、throw、throws。
1. try-catch-finally:捕获并处理异常
try:包裹可能出现异常的代码catch:捕获对应类型的异常并处理,可写多个(异常类型从小到大)finally:无论是否发生异常,最终都会执行(除非 JVM 退出),常用于释放资源
代码示例:基础捕获 + 多 catch + finally
java
运行
public class ExceptionDemo1 {
public static void main(String[] args) {
int[] arr = {10, 20, 30};
try {
// 可能出现异常的代码
int num = arr[5]; // 数组越界异常
int result = 10 / 0; // 算术异常
System.out.println("正常执行");
} catch (ArrayIndexOutOfBoundsException e) {
// 捕获数组越界异常
System.out.println("捕获到数组越界异常:" + e.getMessage());
} catch (ArithmeticException e) {
// 捕获算术异常
System.out.println("捕获到算术异常:" + e.getMessage());
} catch (Exception e) {
// 兜底捕获其他所有异常,必须写在最后
System.out.println("捕获到其他异常:" + e.getMessage());
} finally {
// 一定会执行的代码
System.out.println("finally块执行,释放资源");
}
System.out.println("程序继续运行,不会中断");
}
}
注意:
finally优先级高于return。如果 try 中有 return,会先执行 finally,再执行 return。
2. throws:声明抛出异常
在方法声明上使用,表示当前方法不处理异常,交给调用者处理。如果抛出的是编译时异常,调用者必须继续处理。
代码示例:throws 声明编译时异常
java
运行
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ExceptionDemo2 {
public static void main(String[] args) {
try {
// 调用声明了异常的方法,调用者必须处理
Date date = parseDate("2026-08-16");
System.out.println("解析结果:" + date);
} catch (ParseException e) {
System.out.println("日期解析失败:" + e.getMessage());
}
}
// 方法声明抛出ParseException(编译时异常)
public static Date parseDate(String str) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.parse(str); // parse方法本身会抛出编译时异常
}
}
3. throw:手动抛出异常对象
在方法内部使用,手动创建一个异常对象并抛出,常用于业务逻辑校验。
代码示例:手动抛出运行时异常
java
运行
public class ExceptionDemo3 {
public static void main(String[] args) {
try {
divide(10, 0);
} catch (ArithmeticException e) {
System.out.println("捕获异常:" + e.getMessage());
}
}
public static int divide(int a, int b) {
if (b == 0) {
// 手动抛出异常对象,中断方法执行
throw new ArithmeticException("除数不能为0");
}
return a / b;
}
}
三、自定义异常
Java 内置异常无法满足所有业务场景时,可以自定义异常类。
- 自定义编译时异常 :继承
Exception - 自定义运行时异常 :继承
RuntimeException(更常用)
代码示例:自定义业务异常
java
运行
// 自定义运行时业务异常
class BusinessException extends RuntimeException {
// 无参构造
public BusinessException() {}
// 带异常信息的构造方法
public BusinessException(String message) {
super(message);
}
}
public class ExceptionDemo4 {
public static void main(String[] args) {
try {
login("admin", "123");
} catch (BusinessException e) {
System.out.println("登录失败:" + e.getMessage());
}
}
public static void login(String username, String password) {
if (!"root".equals(username)) {
throw new BusinessException("用户名不存在");
}
if (!"root123".equals(password)) {
throw new BusinessException("密码错误");
}
System.out.println("登录成功");
}
}
四、常见运行时异常汇总
NullPointerException:空指针异常,调用了 null 对象的方法 / 属性ArrayIndexOutOfBoundsException:数组索引越界StringIndexOutOfBoundsException:字符串索引越界ClassCastException:类型转换异常NumberFormatException:数字格式异常(字符串转数字失败)IllegalArgumentException:非法参数异常
第二部分:Java Collection 集合框架
一、集合框架概述
集合是 Java 提供的容器,用来存储多个对象。和数组的核心区别:
- 数组长度固定,集合长度可变
- 数组可存基本类型 + 对象,集合只能存对象(基本类型用包装类)
- 数组只能存同类型元素,集合默认存
Object,可通过泛型约束类型
Java 集合分为两大体系:
- Collection 体系:单列集合,每个元素是单独的对象(List、Set、Queue)
- Map 体系:双列集合,每个元素是键值对(Key-Value)
本次重点讲解 Collection 体系。
二、Collection 根接口
Collection 是所有单列集合的父接口,定义了所有单列集合共有的方法。
1. 核心共性方法
表格
| 方法 | 作用 |
|---|---|
boolean add(E e) |
添加元素 |
boolean remove(Object o) |
删除指定元素 |
boolean contains(Object o) |
判断是否包含指定元素 |
int size() |
获取集合元素个数 |
void clear() |
清空集合 |
boolean isEmpty() |
判断集合是否为空 |
Object[] toArray() |
集合转数组 |
Iterator<E> iterator() |
获取迭代器,用于遍历集合 |
代码示例:Collection 通用方法演示
java
运行
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo1 {
public static void main(String[] args) {
// 多态写法:父类引用指向子类对象
Collection<String> coll = new ArrayList<>();
// 1. 添加元素
coll.add("Java");
coll.add("Python");
coll.add("C++");
System.out.println("添加后集合:" + coll); // [Java, Python, C++]
// 2. 判断包含
System.out.println("是否包含Java:" + coll.contains("Java")); // true
// 3. 元素个数
System.out.println("集合长度:" + coll.size()); // 3
// 4. 删除元素
coll.remove("C++");
System.out.println("删除后:" + coll); // [Java, Python]
// 5. 集合转数组
Object[] arr = coll.toArray();
for (Object o : arr) {
System.out.println("数组元素:" + o);
}
// 6. 清空
coll.clear();
System.out.println("清空后是否为空:" + coll.isEmpty()); // true
}
}
三、List 集合体系(有序、可重复、有索引)
List 继承自 Collection,特点:
- 有序:存储顺序和取出顺序一致
- 可重复:允许存储重复元素
- 有索引:可以通过索引直接操作元素
1. ArrayList(最常用)
- 底层结构:动态数组
- 特点:查询快(通过索引直接定位),增删慢(需要移动数组元素)
- 适用场景:查询多、增删少的场景
- 扩容机制:初始容量 10,每次扩容为原来的 1.5 倍
代码示例:ArrayList 常用方法
java
运行
import java.util.ArrayList;
import java.util.List;
public class ListDemo1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// 1. 添加元素(可重复)
list.add("苹果");
list.add("香蕉");
list.add("苹果");
list.add("橙子");
// 2. 指定索引添加
list.add(2, "葡萄");
System.out.println("索引添加后:" + list); // [苹果, 香蕉, 葡萄, 苹果, 橙子]
// 3. 根据索引获取元素
System.out.println("索引1的元素:" + list.get(1)); // 香蕉
// 4. 根据索引修改元素
list.set(0, "红富士苹果");
System.out.println("修改后:" + list);
// 5. 根据索引删除
list.remove(3);
System.out.println("删除索引3后:" + list);
// 6. 普通for遍历(List特有,因为有索引)
System.out.println("===== 普通for遍历 =====");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
2. LinkedList
- 底层结构:双向链表
- 特点:增删快(只需要修改节点指针),查询慢(需要从头 / 尾遍历)
- 特有方法:大量操作首尾元素的方法,适合做栈、队列
- 适用场景:频繁首尾增删的场景
代码示例:LinkedList 特有方法
java
运行
import java.util.LinkedList;
public class ListDemo2 {
public static void main(String[] args) {
LinkedList<String> linked = new LinkedList<>();
// 1. 首尾添加
linked.addFirst("头部元素1");
linked.addFirst("头部元素2");
linked.addLast("尾部元素1");
linked.addLast("尾部元素2");
System.out.println("集合:" + linked); // [头部元素2, 头部元素1, 尾部元素1, 尾部元素2]
// 2. 获取首尾元素
System.out.println("第一个:" + linked.getFirst());
System.out.println("最后一个:" + linked.getLast());
// 3. 删除首尾元素
linked.removeFirst();
linked.removeLast();
System.out.println("删除首尾后:" + linked); // [头部元素1, 尾部元素1]
// 模拟栈:先进后出
linked.push("压栈元素"); // 等价addFirst
System.out.println("弹栈:" + linked.pop()); // 等价removeFirst
}
}
3. Vector(了解即可)
- 底层也是数组,线程安全(方法加
synchronized),效率低,现在基本被ArrayList替代。
四、Set 集合体系(无序、不可重复、无索引)
Set 继承自 Collection,特点:
- 无序 :存储顺序和取出顺序可能不一致(
LinkedHashSet除外) - 不可重复:不允许存储重复元素
- 无索引:不能通过索引操作元素
1. HashSet(最常用)
- 底层结构 :哈希表(数组 + 链表 + 红黑树),基于
HashMap实现 - 去重原理 :依赖元素的
hashCode()和equals()方法- 先调用
hashCode()计算哈希值,确定存储位置 - 位置无元素:直接存入
- 位置有元素:调用
equals()比较内容,相同则去重,不同则挂在链表下
- 先调用
- 注意 :自定义类要实现去重,必须重写
hashCode()和equals()
代码示例:HashSet 去重演示
java
运行
import java.util.HashSet;
import java.util.Objects;
// 自定义学生类
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// 重写toString方便打印
@Override
public String toString() {
return "Student{" + "name='" + name + '\'' + ", age=" + age + '}';
}
// 重写hashCode和equals实现去重
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class SetDemo1 {
public static void main(String[] args) {
HashSet<Student> set = new HashSet<>();
set.add(new Student("张三", 18));
set.add(new Student("李四", 20));
set.add(new Student("张三", 18)); // 重复元素,会被去重
set.add(new Student("王五", 19));
// 遍历:无序、无重复
for (Student stu : set) {
System.out.println(stu);
}
}
}
2. LinkedHashSet
- 底层结构:哈希表 + 双向链表
- 特点 :在 HashSet 去重基础上,额外保证存取顺序一致(有序),因为链表记录了元素顺序。
代码示例:LinkedHashSet 有序性
java
运行
import java.util.LinkedHashSet;
public class SetDemo2 {
public static void main(String[] args) {
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("北京");
set.add("上海");
set.add("广州");
set.add("北京"); // 重复去重
// 输出顺序和添加顺序一致
for (String s : set) {
System.out.println(s); // 北京 上海 广州
}
}
}
3. TreeSet
- 底层结构:红黑树
- 特点 :不仅去重,还能对元素自动排序
- 排序方式有两种 :
- 自然排序 :元素类实现
Comparable接口,重写compareTo方法 - 比较器排序 :创建 TreeSet 时传入
Comparator比较器对象
- 自然排序 :元素类实现
代码示例:TreeSet 自然排序
java
运行
import java.util.TreeSet;
// 实现Comparable接口,定义排序规则
class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
}
// 重写比较方法:按年龄升序,年龄相同按姓名排序
@Override
public int compareTo(Person o) {
// 正数:当前对象大;负数:当前对象小;0:相等(会去重)
int result = this.age - o.age;
// 年龄相同时,比较姓名
return result == 0 ? this.name.compareTo(o.name) : result;
}
}
public class SetDemo3 {
public static void main(String[] args) {
TreeSet<Person> set = new TreeSet<>();
set.add(new Person("张三", 20));
set.add(new Person("李四", 18));
set.add(new Person("王五", 20));
set.add(new Person("赵六", 22));
// 自动按年龄升序输出
for (Person p : set) {
System.out.println(p);
}
}
}
五、集合通用遍历方式
1. 迭代器 Iterator(所有 Collection 集合通用)
迭代器是专门用来遍历集合的对象,不依赖索引。
boolean hasNext():判断是否有下一个元素E next():获取下一个元素void remove():删除当前元素(遍历过程中删除元素的安全方式)
代码示例:迭代器遍历
java
运行
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class IteratorDemo {
public static void main(String[] args) {
Collection<String> coll = new ArrayList<>();
coll.add("Java");
coll.add("Python");
coll.add("C++");
coll.add("Go");
// 获取迭代器对象
Iterator<String> it = coll.iterator();
// 循环遍历
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
// 遍历中删除元素(安全方式)
if ("Python".equals(s)) {
it.remove();
}
}
System.out.println("删除后:" + coll); // [Java, C++, Go]
}
}
2. 增强 for 循环(foreach)
JDK 5 新增,底层基于迭代器实现,所有 Collection 集合和数组都能用。
java
运行
for (元素类型 变量名 : 集合/数组) {
// 操作变量
}
注意:增强 for 和迭代器遍历过程中,不能用集合对象直接增删元素 ,否则会报并发修改异常
ConcurrentModificationException。
六、List 和 Set 总结对比
表格
| 集合类型 | 有序性 | 重复性 | 索引 | 底层结构 | 核心特点 |
|---|---|---|---|---|---|
| ArrayList | 有序 | 可重复 | 有 | 动态数组 | 查询快、增删慢 |
| LinkedList | 有序 | 可重复 | 有 | 双向链表 | 首尾增删极快、查询慢 |
| HashSet | 无序 | 不可重复 | 无 | 哈希表 | 去重、查询增删都快 |
| LinkedHashSet | 有序 | 不可重复 | 无 | 哈希表 + 链表 | 去重且保证存取顺序 |
| TreeSet | 排序(自然 / 比较器) | 不可重复 | 无 | 红黑树 | 去重 + 自动排序 |
第 1 题 基础综合:学生信息管理(ArrayList + 自定义异常)
题目要求
- 定义学生类
Student,包含私有属性:String stuId(学号)、String name(姓名)、int age(年龄),提供全参构造、getter/setter、重写toString用于打印。 - 自定义运行时异常
IllegalAgeException,用于表示年龄非法,提供带异常信息的构造方法。 - 定义学生管理类
StudentManager:- 内部使用
ArrayList<Student>存储学生对象 - 方法
void addStudent(Student s):添加学生前校验年龄,若年龄不在 10~100 范围内,抛出IllegalAgeException,异常信息为「年龄非法:XX 岁,需在 10-100 之间」 - 方法
void showAllStudents():遍历集合,按格式打印所有学生信息 - 方法
Student getStudentByIndex(int index):根据索引返回学生,内部捕获索引越界异常,打印「索引越界,查询失败」并返回 null
- 内部使用
- 编写测试类
Test1:- 创建管理类对象,依次添加 3 名学生(其中 1 名年龄设为 200,用于测试异常)
- 使用 try-catch 捕获添加时的异常并打印错误信息
- 调用
showAllStudents()打印所有合法学生 - 分别查询索引 1 和索引 10 的学生,验证越界异常处理
完整代码
java
运行
// 自定义年龄非法异常(运行时异常)
class IllegalAgeException extends RuntimeException {
public IllegalAgeException(String message) {
super(message);
}
}
// 学生类
class Student {
private String stuId;
private String name;
private int age;
public Student(String stuId, String name, int age) {
this.stuId = stuId;
this.name = name;
this.age = age;
}
public String getStuId() { return stuId; }
public void setStuId(String stuId) { this.stuId = stuId; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override
public String toString() {
return "学号:" + stuId + ",姓名:" + name + ",年龄:" + age + "岁";
}
}
// 学生管理类
class StudentManager {
private ArrayList<Student> list = new ArrayList<>();
// 添加学生,带年龄校验
public void addStudent(Student s) {
if (s.getAge() < 10 || s.getAge() > 100) {
throw new IllegalAgeException("年龄非法:" + s.getAge() + "岁,需在10-100之间");
}
list.add(s);
System.out.println("学生【" + s.getName() + "】添加成功");
}
// 打印所有学生
public void showAllStudents() {
System.out.println("\n===== 全体学生信息 =====");
for (Student s : list) {
System.out.println(s);
}
}
// 根据索引查询,捕获越界异常
public Student getStudentByIndex(int index) {
try {
return list.get(index);
} catch (IndexOutOfBoundsException e) {
System.out.println("索引越界,查询失败");
return null;
}
}
}
// 测试类
public class Test1 {
public static void main(String[] args) {
StudentManager manager = new StudentManager();
// 添加学生,捕获年龄异常
try {
manager.addStudent(new Student("S001", "张三", 18));
manager.addStudent(new Student("S002", "李四", 200));
manager.addStudent(new Student("S003", "王五", 20));
} catch (IllegalAgeException e) {
System.out.println("添加失败:" + e.getMessage());
}
// 打印所有学生
manager.showAllStudents();
// 索引查询测试
System.out.println("\n===== 索引查询测试 =====");
Student s1 = manager.getStudentByIndex(1);
System.out.println("索引1的学生:" + s1);
Student s2 = manager.getStudentByIndex(10);
System.out.println("索引10的学生:" + s2);
}
}
第 2 题 进阶综合:成绩去重统计(HashSet + 编译时异常转换)
题目要求
- 自定义编译时异常
ScoreFormatException,表示成绩格式错误,提供带异常信息的构造方法。 - 编写工具方法
public static int parseScore(String scoreStr) throws ScoreFormatException:- 将字符串转为整数成绩
- 若字符串无法转为数字,捕获
NumberFormatException,转换为ScoreFormatException抛出,信息为「成绩格式错误:XXX」 - 若成绩不在 0~100 范围内,抛出
ScoreFormatException,信息为「成绩超出范围:XXX 分」
- 主方法中给定字符串数组:
{"85", "92", "abc", "78", "105", "85", "66", "null", "92"} - 遍历数组,调用
parseScore方法,使用 try-catch 处理异常,将解析成功的合法成绩存入HashSet<Integer>自动去重。 - 遍历结束后按要求打印:
- 解析失败的异常信息逐条打印
- 打印「合法成绩集合:XX」
- 打印「有效成绩个数:XX」
- 打印「最高分:XX,最低分:XX」
完整代码
java
运行
import java.util.HashSet;
// 自定义成绩格式异常(编译时异常)
class ScoreFormatException extends Exception {
public ScoreFormatException(String message) {
super(message);
}
}
public class Test2 {
public static void main(String[] args) {
String[] scoreStrs = {"85", "92", "abc", "78", "105", "85", "66", "null", "92"};
HashSet<Integer> scoreSet = new HashSet<>();
System.out.println("===== 解析异常信息 =====");
for (String s : scoreStrs) {
try {
int score = parseScore(s);
scoreSet.add(score);
} catch (ScoreFormatException e) {
System.out.println(e.getMessage());
}
}
// 统计结果
System.out.println("\n===== 成绩统计结果 =====");
System.out.println("合法成绩集合:" + scoreSet);
System.out.println("有效成绩个数:" + scoreSet.size());
// 计算最高最低分
int max = 0;
int min = 100;
for (int score : scoreSet) {
if (score > max) max = score;
if (score < min) min = score;
}
System.out.println("最高分:" + max + ",最低分:" + min);
}
// 解析成绩字符串,抛出自定义编译时异常
public static int parseScore(String scoreStr) throws ScoreFormatException {
int score;
try {
score = Integer.parseInt(scoreStr);
} catch (NumberFormatException e) {
// 转换为自定义异常抛出
throw new ScoreFormatException("成绩格式错误:" + scoreStr);
}
if (score < 0 || score > 100) {
throw new ScoreFormatException("成绩超出范围:" + score + "分");
}
return score;
}
}
第 3 题 综合应用:员工薪资管理(TreeSet 排序 + 多异常业务校验)
题目要求
- 定义员工类
Employee,私有属性:String empId(工号)、String name(姓名)、String dept(部门)、double salary(月薪)。 - 自定义两个异常:
EmployeeExistsException(运行时):员工已存在异常SalaryIllegalException(运行时):薪资非法异常
Employee类实现Comparable<Employee>接口:- 排序规则:按月薪降序 排列;月薪相同时,按工号升序排列
- 工号相同视为同一员工(TreeSet 去重依据)
- 定义员工服务类
EmployeeService:- 内部使用
TreeSet<Employee>存储员工 - 方法
void addEmployee(Employee emp):- 若薪资 ≤ 0,抛出
SalaryIllegalException,信息「薪资非法:XXX,必须大于 0」 - 若员工已存在(工号重复),抛出
EmployeeExistsException,信息「员工已存在,工号:XXX」 - 校验通过则添加成功
- 若薪资 ≤ 0,抛出
- 方法
boolean removeEmployee(String empId):根据工号删除员工,删除成功返回 true,不存在返回 false - 方法
void showByDept(String dept):遍历集合,打印指定部门的所有员工信息 - 方法
void showAll():打印所有员工(按薪资降序)
- 内部使用
- 编写测试类
Test3:- 创建服务对象,添加 5 名员工(包含 1 个薪资为负、1 个重复工号,用于测试异常)
- 使用 try-catch 捕获添加时的所有异常并打印提示
- 删除一名不存在的员工,打印结果
- 打印「研发部」所有员工
- 最后打印全体员工排序结果
完整代码
java
运行
import java.util.TreeSet;
// 自定义异常1:员工已存在
class EmployeeExistsException extends RuntimeException {
public EmployeeExistsException(String message) {
super(message);
}
}
// 自定义异常2:薪资非法
class SalaryIllegalException extends RuntimeException {
public SalaryIllegalException(String message) {
super(message);
}
}
// 员工类,实现Comparable接口用于TreeSet排序去重
class Employee implements Comparable<Employee> {
private String empId;
private String name;
private String dept;
private double salary;
public Employee(String empId, String name, String dept, double salary) {
this.empId = empId;
this.name = name;
this.dept = dept;
this.salary = salary;
}
public String getEmpId() { return empId; }
public String getName() { return name; }
public String getDept() { return dept; }
public double getSalary() { return salary; }
@Override
public String toString() {
return "工号:" + empId + ",姓名:" + name + ",部门:" + dept + ",月薪:" + salary + "元";
}
// 排序规则:薪资降序,薪资相同工号升序;工号相同视为重复元素
@Override
public int compareTo(Employee o) {
// 薪资降序
int result = Double.compare(o.salary, this.salary);
// 薪资相同,按工号升序
result = result == 0 ? this.empId.compareTo(o.empId) : result;
return result;
}
}
// 员工业务服务类
class EmployeeService {
private TreeSet<Employee> empSet = new TreeSet<>();
// 添加员工,带业务校验
public void addEmployee(Employee emp) {
// 校验薪资
if (emp.getSalary() <= 0) {
throw new SalaryIllegalException("薪资非法:" + emp.getSalary() + ",必须大于0");
}
// 校验工号是否已存在
for (Employee e : empSet) {
if (e.getEmpId().equals(emp.getEmpId())) {
throw new EmployeeExistsException("员工已存在,工号:" + emp.getEmpId());
}
}
empSet.add(emp);
System.out.println("员工【" + emp.getName() + "】添加成功");
}
// 根据工号删除员工
public boolean removeEmployee(String empId) {
for (Employee e : empSet) {
if (e.getEmpId().equals(empId)) {
empSet.remove(e);
return true;
}
}
return false;
}
// 打印指定部门员工
public void showByDept(String dept) {
System.out.println("\n===== " + dept + " 部门员工 =====");
for (Employee e : empSet) {
if (e.getDept().equals(dept)) {
System.out.println(e);
}
}
}
// 打印所有员工(已按薪资降序排列)
public void showAll() {
System.out.println("\n===== 全体员工(薪资降序) =====");
for (Employee e : empSet) {
System.out.println(e);
}
}
}
// 测试类
public class Test3 {
public static void main(String[] args) {
EmployeeService service = new EmployeeService();
System.out.println("===== 添加员工测试 =====");
try {
service.addEmployee(new Employee("E001", "张三", "研发部", 12000));
service.addEmployee(new Employee("E002", "李四", "测试部", 9500));
service.addEmployee(new Employee("E003", "王五", "研发部", -5000)); // 薪资非法
service.addEmployee(new Employee("E001", "赵六", "产品部", 11000)); // 工号重复
service.addEmployee(new Employee("E004", "孙七", "研发部", 15000));
service.addEmployee(new Employee("E005", "周八", "测试部", 9500));
} catch (SalaryIllegalException | EmployeeExistsException e) {
System.out.println("添加失败:" + e.getMessage());
}
// 删除测试
System.out.println("\n===== 删除员工测试 =====");
boolean del1 = service.removeEmployee("E002");
System.out.println("删除E002:" + (del1 ? "成功" : "失败,员工不存在"));
boolean del2 = service.removeEmployee("E999");
System.out.println("删除E999:" + (del2 ? "成功" : "失败,员工不存在"));
// 按部门查询
service.showByDept("研发部");
// 打印全体员工
service.showAll();
}
}