java-3

第一部分:Java 异常机制

一、异常的基本概念与体系结构

异常指程序运行时出现的非正常情况(如空指针、数组越界),会中断程序执行。Java 把异常封装成对象,通过统一的机制进行处理。

1. 异常的继承体系

所有异常的根类是 java.lang.Throwable,它分为两大分支:

  • Error(错误) :系统级严重问题,程序无法处理,如 OutOfMemoryError(内存溢出)、StackOverflowError(栈溢出),一般不需要代码捕获。
  • Exception(异常) :程序本身可以处理的问题,又分为两类:
    1. 运行时异常(非受检异常)RuntimeException 及其子类,编译阶段不强制处理,运行时才触发,如空指针、数组越界。
    2. 编译时异常(受检异常) :除运行时异常外的其他 Exception 子类,编译阶段必须处理(捕获或抛出),否则代码报错,如 IOExceptionSQLException
2. 核心区分:运行时异常 vs 编译时异常

表格

类型 触发时机 编译要求 常见例子
运行时异常 程序运行时 不强制处理 NullPointerExceptionArrayIndexOutOfBoundsExceptionArithmeticException
编译时异常 编译阶段 必须捕获 / 声明抛出 IOExceptionParseExceptionClassNotFoundException

二、异常的核心处理机制

Java 处理异常有 5 个核心关键字:trycatchfinallythrowthrows

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("登录成功");
    }
}

四、常见运行时异常汇总

  1. NullPointerException:空指针异常,调用了 null 对象的方法 / 属性
  2. ArrayIndexOutOfBoundsException:数组索引越界
  3. StringIndexOutOfBoundsException:字符串索引越界
  4. ClassCastException:类型转换异常
  5. NumberFormatException:数字格式异常(字符串转数字失败)
  6. IllegalArgumentException:非法参数异常

第二部分:Java Collection 集合框架

一、集合框架概述

集合是 Java 提供的容器,用来存储多个对象。和数组的核心区别:

  • 数组长度固定,集合长度可变
  • 数组可存基本类型 + 对象,集合只能存对象(基本类型用包装类)
  • 数组只能存同类型元素,集合默认存 Object,可通过泛型约束类型

Java 集合分为两大体系:

  1. Collection 体系:单列集合,每个元素是单独的对象(List、Set、Queue)
  2. 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() 方法
    1. 先调用 hashCode() 计算哈希值,确定存储位置
    2. 位置无元素:直接存入
    3. 位置有元素:调用 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
  • 底层结构:红黑树
  • 特点 :不仅去重,还能对元素自动排序
  • 排序方式有两种
    1. 自然排序 :元素类实现 Comparable 接口,重写 compareTo 方法
    2. 比较器排序 :创建 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 + 自定义异常)

题目要求

  1. 定义学生类 Student,包含私有属性:String stuId(学号)、String name(姓名)、int age(年龄),提供全参构造、getter/setter、重写 toString 用于打印。
  2. 自定义运行时异常 IllegalAgeException,用于表示年龄非法,提供带异常信息的构造方法。
  3. 定义学生管理类 StudentManager
    • 内部使用 ArrayList<Student> 存储学生对象
    • 方法 void addStudent(Student s):添加学生前校验年龄,若年龄不在 10~100 范围内,抛出 IllegalAgeException,异常信息为「年龄非法:XX 岁,需在 10-100 之间」
    • 方法 void showAllStudents():遍历集合,按格式打印所有学生信息
    • 方法 Student getStudentByIndex(int index):根据索引返回学生,内部捕获索引越界异常,打印「索引越界,查询失败」并返回 null
  4. 编写测试类 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 + 编译时异常转换)

题目要求

  1. 自定义编译时异常 ScoreFormatException,表示成绩格式错误,提供带异常信息的构造方法。
  2. 编写工具方法 public static int parseScore(String scoreStr) throws ScoreFormatException
    • 将字符串转为整数成绩
    • 若字符串无法转为数字,捕获 NumberFormatException,转换为 ScoreFormatException 抛出,信息为「成绩格式错误:XXX」
    • 若成绩不在 0~100 范围内,抛出 ScoreFormatException,信息为「成绩超出范围:XXX 分」
  3. 主方法中给定字符串数组:{"85", "92", "abc", "78", "105", "85", "66", "null", "92"}
  4. 遍历数组,调用 parseScore 方法,使用 try-catch 处理异常,将解析成功的合法成绩存入 HashSet<Integer> 自动去重。
  5. 遍历结束后按要求打印:
    • 解析失败的异常信息逐条打印
    • 打印「合法成绩集合: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 排序 + 多异常业务校验)

题目要求

  1. 定义员工类 Employee,私有属性:String empId(工号)、String name(姓名)、String dept(部门)、double salary(月薪)。
  2. 自定义两个异常:
    • EmployeeExistsException(运行时):员工已存在异常
    • SalaryIllegalException(运行时):薪资非法异常
  3. Employee 类实现 Comparable<Employee> 接口:
    • 排序规则:按月薪降序 排列;月薪相同时,按工号升序排列
    • 工号相同视为同一员工(TreeSet 去重依据)
  4. 定义员工服务类 EmployeeService
    • 内部使用 TreeSet<Employee> 存储员工
    • 方法 void addEmployee(Employee emp)
      1. 若薪资 ≤ 0,抛出 SalaryIllegalException,信息「薪资非法:XXX,必须大于 0」
      2. 若员工已存在(工号重复),抛出 EmployeeExistsException,信息「员工已存在,工号:XXX」
      3. 校验通过则添加成功
    • 方法 boolean removeEmployee(String empId):根据工号删除员工,删除成功返回 true,不存在返回 false
    • 方法 void showByDept(String dept):遍历集合,打印指定部门的所有员工信息
    • 方法 void showAll():打印所有员工(按薪资降序)
  5. 编写测试类 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();
    }
}
相关推荐
Zane19943 分钟前
复制算法明明要浪费一半内存,为什么新生代还偏偏用它
java·后端
worilb3 分钟前
Java/JVM 常见诊断文件对比
java·开发语言
ttwuai5 分钟前
Go 后台初始化 MySQL 脚本失败怎么办?先查 DDL 还是生成配置
开发语言·mysql·golang
2601_962885728 分钟前
如何用 Python 做 A 股全市场扫描选股?(多条件筛选实战)
开发语言·python
toolsmith9 分钟前
AI能替你读代码,但替代不了你积累洞察力:一次YouTrack逆向实战全记录
java·源码阅读
步行cgn10 分钟前
Spring Boot 绑定简单 Bean 详解
java·spring boot·后端
鹿角片ljp10 分钟前
我如何用 Frontend Design Skill 重构平台
java
未秃头的程序猿12 分钟前
分库分表一年后,我复盘了当时最该想清楚的三件事
java·数据库·后端
岁月如歌778615 分钟前
顺序消息与幂等消费完全指南:从队列有序到接口幂等
java·后端·架构
秋名RG15 分钟前
Java 泛型深度解析:从类型安全到现代实践(JDK 21 视角)
java·windows·安全