《Java 100 天进阶之路》第32篇:Java常用工具类(Objects、Collections、Arrays深入)

📌 系列导航《Java 100 天进阶之路》完整目录 |
⬅️ 上一篇:第31篇:Java数组详解 |
➡️ 下一篇:第33篇:Java中的static关键字详解


一、核心知识点

  • java.util.Objects 类:null 安全的方法(equals、hashCode、toString、requireNonNull、isNull 等)
  • java.util.Collections 类:集合的操作(排序、混排、不可变集合、同步包装器)
  • java.util.Arrays 类:数组的操作(排序、二分查找、填充、拷贝、转 List)
  • java.util.ComparableComparator:对象的排序,以及 Java 8+ 的函数式增强
  • 工具类的设计模式:私有构造方法、静态方法、无状态

二、通俗讲解(1分钟开心学)

1. Objects 工具类

Java 7 引入,专门处理可能为 null 的对象,避免手动写 if (obj != null)。例如 Objects.equals(a, b) 会在 a 和 b 都为 null 时返回 true,其中一个 null 时返回 false,比 a.equals(b) 安全。

// 传统写法
public boolean compare(String s1, String s2) {
    if (s1 == s2) return true;
    if (s1 == null || s2 == null) return false;
    return s1.equals(s2);
}
// 使用 Objects
public boolean compare(String s1, String s2) {
    return Objects.equals(s1, s2); // 一行搞定
}

2. Collections 工具类

操作 CollectionMap 的静态方法。常见方法有:排序(sort)、反转(reverse)、随机打乱(shuffle)、不可变视图(unmodifiableXxx)和同步包装(synchronizedXxx)。

💡 值得注意的是CollectionsCollection是两个不同的概念。Collection是集合框架的顶层接口,定义操作规范;而Collections是一个工具类,提供操作 Collection 的静态方法。这是面试中常被问到的点,也是初学者容易混淆的地方。

3. Arrays 工具类

操作数组的静态方法,如排序(sort)、二分查找(binarySearch)、填充(fill)、拷贝(copyOf)、转列表(asList)等。

⚠️ 重要提醒:使用 Arrays.asList() 将数组转换为 List 时,返回的是一个固定大小的列表,不能进行 addremove 操作,否则会抛出 UnsupportedOperationException

4. 排序比较器

  • 自然排序:对象实现 Comparable 接口,重写 compareTo 方法,定义默认排序规则。
  • 定制排序:实现 Comparator 接口,重写 compare 方法,可以根据需要传递不同的排序规则。JDK 8 还提供了 Comparator 接口的静态方法,支持链式调用,让定制排序更加优雅。

生活类比Objects 就像安全扶手,防止你在 null 上摔跤。CollectionsArrays 就像瑞士军刀,给集合和数组提供各种常用工具。

三、实操代码案例 + 场景说明

场景:对员工列表进行多种排序(按姓名、按工资),并确保集合不可修改。

import java.util.*;
import java.util.stream.Collectors;

class Employee implements Comparable<Employee> {
    private String name;
    private double salary;
    
    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
    
    public String getName() { return name; }
    public double getSalary() { return salary; }
    
    // 自然排序:按姓名
    @Override
    public int compareTo(Employee o) {
        return this.name.compareTo(o.name);
    }
    
    @Override
    public String toString() {
        return String.format("%s(%.0f)", name, salary);
    }
    
    // 静态工厂方法用于创建 Comparator(Java 8+)
    public static Comparator<Employee> byName = Comparator.comparing(Employee::getName);
    public static Comparator<Employee> bySalary = Comparator.comparingDouble(Employee::getSalary);
}

public class UtilsDemo {
    public static void main(String[] args) {
        // 1. Objects 工具类
        String s1 = null;
        String s2 = "hello";
        System.out.println(Objects.equals(s1, s2));      // false
        System.out.println(Objects.hashCode(s1));        // 0
        System.out.println(Objects.toString(s1, "默认")); // 默认
        Objects.requireNonNull(s2, "s2 不能为 null");     // 不抛异常
        // Objects.requireNonNull(s1, "s1 不能为 null"); // 会抛 NPE
        
        // 2. Collections 与 Arrays
        List<Integer> list = new ArrayList<>(Arrays.asList(3, 1, 4, 1, 5));
        Collections.sort(list);
        System.out.println(list);   // [1,1,3,4,5]
        Collections.reverse(list);
        System.out.println(list);   // [5,4,3,1,1]
        Collections.shuffle(list);
        System.out.println("随机:" + list);
        
        // 不可变集合
        List<Integer> unmod = Collections.unmodifiableList(list);
        // unmod.add(10); // 抛 UnsupportedOperationException
        
        // 3. 数组工具
        int[] arr = {5,2,8,1};
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));
        int index = Arrays.binarySearch(arr, 8);
        System.out.println("8的位置:" + index);
        int[] copy = Arrays.copyOf(arr, 10);
        System.out.println("拷贝并扩容:" + Arrays.toString(copy));
        
        // 4. 员工排序
        List<Employee> employees = Arrays.asList(
            new Employee("Bob", 5000),
            new Employee("Alice", 7000),
            new Employee("Charlie", 6000)
        );
        // 按姓名排序
        employees.sort(Employee.byName);
        System.out.println("按姓名:" + employees);
        // 按工资排序
        employees.sort(Employee.bySalary);
        System.out.println("按工资:" + employees);
        // 链式排序:先按工资,再按姓名
        employees.sort(Employee.bySalary.thenComparing(Employee.byName));
    }
}

四、避坑要点(高频踩坑汇总)

错误/误区 后果 正确做法
Arrays.asList(arr) 返回的 List 调用 add/remove UnsupportedOperationException new ArrayList<>(Arrays.asList(arr))
修改 Collections.unmodifiableXxx 返回的集合 抛异常 不要尝试修改,或先拷贝再修改
Objects.equals(a, b)a.equals(b) 混淆 前者 null 安全,后者可能 NPE 不确定是否为 null 时用 Objects.equals
自定义 Comparator 时未处理 null 可能 NPE 使用 Comparator.nullsFirstnullsLast
Collections.sort() 对未实现 Comparable 的对象排序 编译错误 确保元素实现 Comparable 或传入 Comparator
混淆 Collections(工具类)和 Collection(接口) 概念不清,面试减分 Collections 是操作工具,Collection 是集合顶层接口
认为 System.arraycopy()Arrays.copyOf() 完全一样 前者需预分配目标数组,更灵活;后者自动创建新数组 按需选择,前者适合多次复制到同一目标,后者适合简单拷贝
binarySearch() 前未排序 结果不确定,可能正确也可能错误 确保数组/列表已按升序排序

五、面试高频考点

  • Q1:Objects.equals(a, b)a.equals(b) 的区别?

    Objects.equals 是 null 安全的,两个都为 null 返回 true,一个为 null 返回 false,否则调用 a.equals(b)。后者在 a 为 null 时抛 NPE。

  • Q2:Collections.sort(list)list.sort(null) 的区别?

    功能相同,list.sort(null)List 接口的默认方法(Java 8 引入),可以直接被集合调用。Collections.sort 是旧 API,内部调用了 list.sort

  • Q3:如何将数组转换成可变的 ArrayList

    new ArrayList<>(Arrays.asList(arr))

  • Q4:CollectionsCollection 的区别?

    Collection 是集合框架的根接口,定义了集合的基本操作规范(如 addremovesize)。Collections 是一个工具类,完全由静态方法组成,用于对集合进行排序、查找、同步化等操作,不能被实例化。

  • Q5:什么是 Java 工具类的经典设计模式?

    Java 工具类的经典设计模式是:私有构造方法(防止外部通过 new 创建实例)、所有方法均为 static(无需实例即可通过类名调用)、无状态(没有成员变量)。典型代表如 MathArraysCollectionsObjects 等。

  • Q6:Comparator 接口中 comparing()thenComparing() 分别的作用?

    comparing() 用于提取排序键,创建单级排序的 ComparatorthenComparing() 用于在已有排序基础上追加次级排序条件,实现多字段排序的链式调用,如 Comparator.comparing(Person::getAge).thenComparing(Person::getName)

  • Q7:使用 Arrays.binarySearch() 时,有哪些注意事项和返回值规则?

    必须在已排序的数组或列表上使用。查找成功返回元素索引;查找失败时返回 -(插入点) - 1,其中插入点是元素应插入的位置(第一个大于查找值的元素索引)。binarySearch 方法的时间复杂度为 O(log n),比线性查找的 O(n) 快得多。

六、练习题

  1. 填空:使用 Objects.requireNonNull 替代手动 null 检查。
  2. 排序:对一组字符串按长度排序,长度相同再按字典序。
  3. 动手:实现一个 Comparator<Person>,先按年龄降序,再按姓名升序。

📊 你的学习进度

  • 当前:第32篇 / 共44篇 · 第五阶段:工具类、异常最佳实践、序列化(第32~35篇)
  • ✅ 已完成:第1~31篇
  • 📖 正在学:第32篇
  • ⏳ 待学习:第33~44篇

👉 📚 完整目录 & 学习指南 | 🔥 订阅本专栏,不错过每一篇

💡 本专栏每篇都包含:避坑表 + 面试高频考点 + 练习题。每天30分钟,100天拿offer!


👉 下一篇文章预告

《第33篇:Java中的static关键字详解》

内容简介:静态变量/方法/代码块,内存位置(方法区),静态导入,静态方法的重写问题。

💡 学完这篇,你将彻底掌握static的语义,面试再问静态成员轻松回答。

📌 《Java 100 天进阶之路 | 从入门到上岗就业》 每天一篇,建议收藏 + 关注,一起100天拿offer!
👉 点击关注我,更新后第一时间收到推送!
📌 除了Java,我也在深挖智能物流实战(出版社WMS、托盘调度、机器学习落地)。如果你对技术在不同领域的实战感兴趣,欢迎点击我的头像,看看专栏《出版社物流WMS智能调度实战》。技术相通,思路可鉴。

更多推荐