Hutool:Java开发者的实用工具库介绍 – wiki基地


Hutool:Java开发者的瑞士军刀——实用工具库深度解析

在Java开发的日常工作中,我们经常会遇到各种各样的基础性、重复性任务,例如字符串处理、日期时间操作、文件读写、集合操作、网络请求、数据加密等等。虽然Java标准库提供了一些基本功能,但往往不够便捷,写出的代码冗长且易错。此时,一个强大、易用的工具库就显得尤为重要。

在众多Java工具库中,Hutool 以其“小而全”、“开箱即用”的特点脱颖而出,被誉为Java开发者的“瑞士军刀”。它整合了许多日常开发中常用的工具类,旨在减少重复代码,提高开发效率。

本文将对Hutool进行深度解析,详细介绍其起源、设计理念、核心模块以及如何在实际项目中应用,帮助Java开发者充分发挥Hutool的价值。

1. Hutool 的起源与设计理念

Hutool 最初由国人开发并维护,其名称意为“糊涂”,寓意着大道至简、开箱即用,让开发者能够“糊里糊涂”地使用各种功能而无需关心底层复杂细节。它起源于开发者自身在实际项目中遇到的痛点,将常用工具类进行归纳整理,逐步发展成为一个涵盖广泛领域的综合性工具库。

Hutool 的设计理念主要体现在以下几个方面:

  • 大道至简,开箱即用: 提供简洁易懂的API,大多数功能通过静态方法调用即可完成,无需实例化对象,降低使用门槛。
  • 无额外依赖(Core模块): Hutool 的核心模块 hutool-core 设计得尽可能独立,不依赖第三方库,避免潜在的版本冲突问题。即使是其他模块,也尽量减少外部依赖。
  • 全面覆盖,模块化: Hutool 涵盖了日常开发的方方面面,从基础的数据类型操作到文件、网络、加密、Web等,应有尽有。同时,它采用了模块化设计,用户可以根据需求选择引入特定的模块,避免整个库的臃肿。
  • 命名规范,易于查找: 工具类和方法命名清晰直观,遵循一定的规范,开发者可以快速找到所需功能。
  • 持续维护,活跃社区: Hutool 项目保持活跃更新,不断完善功能、修复Bug,并且拥有一个活跃的开发者社区,用户遇到问题可以及时获得帮助。
  • 中文文档完善: 对于国内开发者而言,Hutool 提供了详尽的中文文档,学习成本低。

正是这些理念,使得 Hutool 在国内Java开发者群体中迅速流行起来。

2. 为什么选择 Hutool?

相较于Java标准库或其他知名的工具库(如Apache Commons Lang、Google Guava),Hutool 有其独特的优势:

  • 一站式解决方案: Hutool 提供了Java开发中绝大多数常用工具类,无需引入多个独立的库,简化了项目依赖管理。例如,处理字符串用 StrUtil,处理日期用 DateUtil,处理集合用 CollUtil,处理文件用 FileUtil,处理HTTP请求用 HttpUtil,处理JSON用 JSONUtil 等等, API 风格一致,学习成本低。
  • 简洁易用: 对比Java标准库,Hutool 提供了大量简化的API。例如,判断字符串是否为空白、复制文件、计算两个日期的时间差等操作,Hutool 都可以用一行代码搞定,极大地减少了boilerplate code(样板代码)。
  • 无侵入性: Hutool 大多数是静态工具类,通过类名直接调用方法,不会对现有代码结构产生侵入性影响。
  • 中文友好: 完善的中文文档和社区支持对于国内开发者来说非常方便。
  • 快速迭代: Hutool 的更新速度较快,能够及时响应社区需求和技术发展。

当然,像 Apache Commons Lang、Guava 等库也非常优秀,各自有其特点和优势。但对于追求开发效率、希望一个库解决大部分日常问题的场景,Hutool 是一个非常值得考虑的选择。

3. 如何开始使用 Hutool

使用 Hutool 非常简单,只需要在项目的构建工具中添加相应的依赖即可。Hutool 支持 Maven 和 Gradle。

Maven 依赖:

如果你想引入 Hutool 的所有模块,可以使用 hutool-all 依赖:

xml
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.26</version> <!-- 请替换为最新版本 -->
</dependency>

如果你只想使用 Hutool 的核心模块 hutool-core,可以引入:

xml
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.8.26</version> <!-- 请替换为最新版本 -->
</dependency>

推荐根据实际需要引入特定的模块,这样可以减少项目体积和依赖。例如,只需要使用日期工具和文件工具:

“`xml

cn.hutool
hutool-core
5.8.26


cn.hutool
hutool-extra
5.8.26

“`

Gradle 依赖:

“`gradle
// 引入所有模块
implementation ‘cn.hutool:hutool-all:5.8.26’ // 请替换为最新版本

// 或者引入核心模块
implementation ‘cn.hutool:hutool-core:5.8.26’ // 请替换为最新版本

// 或者引入特定模块
// implementation ‘cn.hutool:hutool-core:5.8.26’
// implementation ‘cn.hutool:hutool-http:5.8.26’
// implementation ‘cn.hutool:hutool-json:5.8.26’
// …
“`

添加依赖后,刷新 Maven 或 Gradle 项目,即可在代码中使用 Hutool 的各种工具类。

4. Hutool 核心模块功能详解与示例

Hutool 包含了非常多的工具类,覆盖了开发中的各个方面。下面我们将详细介绍一些最常用和最重要的核心模块及其典型用法。

4.1 字符串工具:StrUtil

字符串操作是开发中最常见的任务之一。StrUtil 提供了大量方便的方法来处理字符串,避免了Java标准库中冗长和不够直观的代码。

“`java
import cn.hutool.core.util.StrUtil;

public class StrUtilExample {
public static void main(String[] args) {
// 1. 判断字符串是否为空或空白
String str1 = “”;
String str2 = ” “;
String str3 = null;
String str4 = “hello”;

    System.out.println("str1 isBlank: " + StrUtil.isBlank(str1)); // true
    System.out.println("str2 isBlank: " + StrUtil.isBlank(str2)); // true
    System.out.println("str3 isBlank: " + StrUtil.isBlank(str3)); // true
    System.out.println("str4 isBlank: " + StrUtil.isBlank(str4)); // false

    System.out.println("str1 isEmpty: " + StrUtil.isEmpty(str1)); // true
    System.out.println("str2 isEmpty: " + StrUtil.isEmpty(str2)); // false (包含空格)
    System.out.println("str3 isEmpty: " + StrUtil.isEmpty(str3)); // true
    System.out.println("str4 isEmpty: " + StrUtil.isEmpty(str4)); // false

    // 2. 去除空白字符
    String str5 = "  hello world  ";
    System.out.println("trim: '" + StrUtil.trim(str5) + "'"); // 'hello world'
    System.out.println("trimStart: '" + StrUtil.trimStart(str5) + "'"); // 'hello world  '
    System.out.println("trimEnd: '" + StrUtil.trimEnd(str5) + "'"); // '  hello world'

    // 3. 字符串格式化(占位符)
    String template = "你好,{}!今天是{}。";
    String formatted = StrUtil.format(template, "Hutool", "星期五");
    System.out.println("formatted: " + formatted); // 你好,Hutool!今天是星期五。

    // 4. 字符串拼接
    String[] arr = {"a", "b", "c"};
    String joined = StrUtil.join(",", arr);
    System.out.println("joined: " + joined); // a,b,c

    String joinedList = StrUtil.join("-", java.util.Arrays.asList("x", "y", "z"));
    System.out.println("joinedList: " + joinedList); // x-y-z

    // 5. 字符串分割
    String str6 = "a,b,c,d";
    java.util.List<String> list = StrUtil.split(str6, ',');
    System.out.println("split list: " + list); // [a, b, c, d]

    String str7 = "a,,b,c";
    java.util.List<String> listIgnoreEmpty = StrUtil.splitTrim(str7, ',', true); // 分割并去除空白,忽略空元素
    System.out.println("splitTrim list: " + listIgnoreEmpty); // [a, b, c]

    // 6. 截取字符串
    String str8 = "abcdefg";
    System.out.println("sub(2): " + StrUtil.sub(str8, 2)); // cdefg
    System.out.println("sub(2, 5): " + StrUtil.sub(str8, 2, 5)); // cde

    // 7. 判断是否包含
    System.out.println("contains 'def': " + StrUtil.contains(str8, "def")); // true
    System.out.println("containsIgnoreCase 'AB': " + StrUtil.containsIgnoreCase(str8, "AB")); // false
    System.out.println("containsIgnoreCase 'AB': " + StrUtil.containsIgnoreCase(str8, "ab")); // true

    // 8. 重复字符串
    System.out.println("repeat 'abc' 3 times: " + StrUtil.repeat("abc", 3)); // abcabcabc

    // 9. 首字母大小写转换
    System.out.println("upperFirst: " + StrUtil.upperFirst("hello")); // Hello
    System.out.println("lowerFirst: " + StrUtil.lowerFirst("World")); // world

    // 10. 驼峰转下划线,下划线转驼峰
    System.out.println("toUnderlineCase: " + StrUtil.toUnderlineCase("userName")); // user_name
    System.out.println("toCamelCase: " + StrUtil.toCamelCase("user_name")); // userName
}

}
“`

StrUtil 提供了远不止这些功能,还包括字符查找、替换、字符集转换、生成随机字符串、判断字符类型等等,极大地提高了字符串处理的效率。

4.2 集合工具:CollUtil

CollUtil 是一个处理集合(Collection)和数组的强大工具类,弥补了Java标准库在集合操作方面的一些不足。

“`java
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.Map;

public class CollUtilExample {
public static void main(String[] args) {
// 1. 判断集合是否为空
List list1 = new ArrayList<>();
List list2 = null;
List list3 = CollUtil.newArrayList(“a”, “b”); // Hutool 创建List的便捷方法

    System.out.println("list1 isEmpty: " + CollUtil.isEmpty(list1)); // true
    System.out.println("list2 isEmpty: " + CollUtil.isEmpty(list2)); // true
    System.out.println("list3 isEmpty: " + CollUtil.isEmpty(list3)); // false

    System.out.println("list1 isNotEmpty: " + CollUtil.isNotEmpty(list1)); // false
    System.out.println("list3 isNotEmpty: " + CollUtil.isNotEmpty(list3)); // true

    // 2. 创建集合
    List<String> list4 = CollUtil.newArrayList("x", "y", "z"); // 创建 ArrayList
    Set<String> set1 = CollUtil.newHashSet("a", "b", "c"); // 创建 HashSet
    Map<String, Integer> map1 = CollUtil.newHashMap(); // 创建 HashMap
    map1.put("apple", 1);
    map1.put("banana", 2);

    // 3. 集合转字符串
    String joinList = CollUtil.join(list4, "-");
    System.out.println("join list4: " + joinList); // x-y-z

    // 4. 遍历并对元素进行操作 (流式API或者ForEach)
    list3.forEach(item -> System.out.print(item + " ")); // a b

    // 5. 集合分组
    List<String> list5 = CollUtil.newArrayList("apple", "banana", "cherry", "apricot");
    Map<Character, List<String>> groupByFirstLetter = CollUtil.groupBy(list5, item -> item.charAt(0));
    System.out.println("Group by first letter: " + groupByFirstLetter); // {a=[apple, apricot], b=[banana], c=[cherry]}

    // 6. 获取集合的第一个和最后一个元素
    System.out.println("First element of list4: " + CollUtil.getFirst(list4)); // x
    System.out.println("Last element of list4: " + CollUtil.getLast(list4)); // z

    // 7. 移除集合中的空元素或空白字符串
    List<String> list6 = CollUtil.newArrayList("a", "", " ", null, "b");
    CollUtil.removeEmpty(list6); // 移除 "", null
    System.out.println("removeEmpty: " + list6); // [a,  , b] -- 注意,不会移除只有空格的元素

    List<String> list7 = CollUtil.newArrayList("a", "", " ", null, "b");
    CollUtil.removeBlank(list7); // 移除 "", " ", null
    System.out.println("removeBlank: " + list7); // [a, b]

    // 8. 数组工具 ArrayUtil
    int[] intArr = {1, 2, 3, 4};
    System.out.println("contains 3 in intArr: " + ArrayUtil.contains(intArr, 3)); // true
    System.out.println("length of intArr: " + ArrayUtil.length(intArr)); // 4

    String[] strArr = {"hello", "world"};
    System.out.println("Array to list: " + ArrayUtil.asList(strArr)); // [hello, world]
}

}
“`

CollUtilArrayUtil 提供了集合和数组的各种常用操作,如查找、过滤、转换、交集、并集、差集等,让集合处理变得更加便捷。

4.3 日期时间工具:DateUtil

处理日期时间是开发中的常见痛点,Java 8 之前的 java.util.DateCalendar API 设计复杂,线程不安全。Java 8 引入了 java.time 包,虽然有所改善,但 Hutool 的 DateUtil 在此基础上提供了更多便捷的方法和对旧版 Date 的兼容。

“`java
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.BetweenFormater.Level;

import java.util.Date;

public class DateUtilExample {
public static void main(String[] args) {
// 1. 获取当前时间
Date date = DateUtil.date(); // 等同于 new Date()
System.out.println(“Current date: ” + date);

    String dateStr = DateUtil.now(); // 当前时间字符串,默认 yyyy-MM-dd HH:mm:ss
    System.out.println("Current date string: " + dateStr);

    String today = DateUtil.today(); // 当前日期字符串,默认 yyyy-MM-dd
    System.out.println("Today string: " + today);

    // 2. 字符串转日期
    String dateString = "2023-10-26 10:30:00";
    Date parsedDate = DateUtil.parse(dateString);
    System.out.println("Parsed date: " + parsedDate);

    // 支持多种格式的自动识别
    Date parsedDate2 = DateUtil.parse("2023/10/26");
    System.out.println("Parsed date 2: " + parsedDate2);

    // 指定格式解析
    Date parsedDate3 = DateUtil.parse("2023年10月26日", DatePattern.NORM_DATE_PATTERN); // 使用常量指定格式
    System.out.println("Parsed date 3: " + parsedDate3);

    // 3. 日期转字符串
    String formattedDate = DateUtil.format(parsedDate, DatePattern.KAIFA_DATE_TIME_PATTERN); // 使用开发常用格式
    System.out.println("Formatted date: " + formattedDate); // 2023-10-26 10:30:00

    String simpleFormat = DateUtil.formatDate(parsedDate); // 只格式化日期 yyyy-MM-dd
    System.out.println("Simple date format: " + simpleFormat);

    String timeFormat = DateUtil.formatTime(parsedDate); // 只格式化时间 HH:mm:ss
    System.out.println("Simple time format: " + timeFormat);

    // 4. 日期部分获取
    System.out.println("Year: " + DateUtil.year(date));
    System.out.println("Month (0-based): " + DateUtil.month(date)); // 月份从0开始
    System.out.println("Day of month: " + DateUtil.dayOfMonth(date));
    System.out.println("Day of week: " + DateUtil.dayOfWeek(date)); // 1:周日, 7:周六
    System.out.println("Day of week in Chinese: " + DateUtil.dayOfWeekEnum(date).toChinese()); // 星期五

    // 5. 日期偏移
    Date tomorrow = DateUtil.offsetDay(date, 1); // 增加1天
    System.out.println("Tomorrow: " + tomorrow);

    Date lastMonth = DateUtil.offsetMonth(date, -1); // 减少1个月
    System.out.println("Last month: " + lastMonth);

    Date nextHour = DateUtil.offsetHour(date, 1); // 增加1小时
    System.out.println("Next hour: " + nextHour);

    // 6. 计算日期时间差
    Date begin = DateUtil.parse("2023-10-25 10:00:00");
    Date end = DateUtil.parse("2023-10-26 11:35:45");

    long betweenDays = DateUtil.between(begin, end, cn.hutool.core.date.DateUnit.DAY); // 相差天数
    System.out.println("Between days: " + betweenDays); // 1 (只计算整天)

    long betweenHours = DateUtil.between(begin, end, cn.hutool.core.date.DateUnit.HOUR); // 相差小时数
    System.out.println("Between hours: " + betweenHours); // 25 (只计算整小时)

    // 格式化时间差
    String formatBetween = DateUtil.formatBetween(begin, end, Level.MINUTE); // 格式化到分钟级别
    System.out.println("Format between: " + formatBetween); // 1天1小时35分

    // 7. 判断闰年
    System.out.println("Is 2024 a leap year? " + DateUtil.isLeapYear(2024)); // true

    // 8. 判断是否是今天、昨天、明天
    System.out.println("Is today today? " + DateUtil.isToday(date)); // true
}

}
“`

DateUtil 还提供了更多功能,如获取季节、星期、月份的起始和结束日期、计算星座和属相、计时器(TimeInterval)等等,极大地简化了日期时间相关的开发工作。

4.4 文件和IO工具:FileUtilIoUtil

文件和IO操作在很多应用中都不可或缺。Hutool 的 FileUtilIoUtil 提供了一系列静态方法,使得文件创建、读写、复制、删除以及流处理变得非常方便。

“`java
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.ClassPathResource;

import java.io.File;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;

public class FileIoUtilExample {
public static void main(String[] args) {
String filePath = “test_hutool_file.txt”;
String copyFilePath = “test_hutool_file_copy.txt”;
String dirPath = “test_hutool_dir”;
File file = null;
File copyFile = null;
File dir = null;

    try {
        // 1. 创建文件和目录
        file = FileUtil.touch(filePath); // 如果文件不存在则创建,如果存在则不操作
        System.out.println("Created file: " + file.getAbsolutePath());

        dir = FileUtil.mkdir(dirPath); // 如果目录不存在则创建
        System.out.println("Created directory: " + dir.getAbsolutePath());

        // 2. 写文件
        FileUtil.writeUtf8String("Hello, Hutool!\nLine 2", file);
        System.out.println("Content written to " + filePath);

        // 3. 读文件
        String content = FileUtil.readUtf8String(filePath);
        System.out.println("Content read from " + filePath + ":\n" + content);

        List<String> lines = FileUtil.readLines(filePath, StandardCharsets.UTF_8); // 按行读取
        System.out.println("Lines read from " + filePath + ": " + lines);

        // 4. 复制文件
        copyFile = FileUtil.copy(file, FileUtil.file(copyFilePath), true); // 复制文件,true表示覆盖
        System.out.println("File copied to: " + copyFile.getAbsolutePath());

        // 5. 获取文件信息
        System.out.println("File name: " + FileUtil.getName(file));
        System.out.println("File extension: " + FileUtil.getSuffix(file));
        System.out.println("File size: " + FileUtil.size(file) + " bytes");
        System.out.println("Is file: " + FileUtil.isFile(file));
        System.out.println("Is directory: " + FileUtil.isDirectory(dir));

        // 6. 资源文件读取 (例如从 classpath)
        ClassPathResource resource = new ClassPathResource("test_resource.properties"); // 假设你的classpath下有 test_resource.properties 文件
        if(resource.exists()){
             System.out.println("Resource content: " + resource.readUtf8Str());
        } else {
             System.out.println("Resource test_resource.properties not found in classpath.");
        }


        // 7. IO流处理 (IoUtil)
        // 示例:从一个流复制到另一个流 (这里用文件的输入输出流模拟)
        File sourceFile = FileUtil.file(filePath);
        File targetFile = FileUtil.file("test_stream_copy.txt");

        BufferedInputStream bis = FileUtil.getInputStream(sourceFile);
        BufferedOutputStream bos = FileUtil.getOutputStream(targetFile);

        long copiedBytes = IoUtil.copy(bis, bos); // 复制流
        System.out.println("Copied " + copiedBytes + " bytes using IoUtil.");

        // 别忘了关闭流,Hutool也提供了关闭工具
        IoUtil.close(bis);
        IoUtil.close(bos);

        System.out.println("Stream copy file content: " + FileUtil.readUtf8String(targetFile));
        FileUtil.del(targetFile); // 删除临时文件

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 8. 删除文件和目录
        if (file != null) FileUtil.del(file);
        if (copyFile != null) FileUtil.del(copyFile);
        if (dir != null) FileUtil.del(dir);
        System.out.println("Cleaned up temporary files.");
    }
}

}
“`

FileUtil 提供了路径拼接、规范化、遍历目录、移动文件、修改时间等更多功能。IoUtil 则包含了各种流的关闭、复制、转为Reader/Writer、转换为byte数组等通用IO操作,并且很多方法都自动处理了流的关闭,非常方便且安全。

4.5 HTTP客户端工具:HttpUtil

在微服务和前后端分离的架构下,进行HTTP请求是家常便饭。Hutool 的 HttpUtil 提供了一个简单易用的HTTP客户端,支持GET、POST等多种请求方式,方便进行数据交互。

“`java
import cn.hutool.http.HttpUtil;
import cn.hutool.http.HttpResponse;

import java.util.HashMap;
import java.util.Map;

public class HttpUtilExample {
public static void main(String[] args) {
// 1. 发送 GET 请求
String getUrl = “http://httpbin.org/get”; // 一个测试用的API,返回请求信息
String getResult = HttpUtil.get(getUrl);
System.out.println(“GET request result:\n” + getResult);

    // 2. 发送带参数的 GET 请求
    Map<String, Object> params = new HashMap<>();
    params.put("name", "Hutool");
    params.put("age", 10);
    String getWithParamsResult = HttpUtil.get(getUrl, params);
    System.out.println("GET with params result:\n" + getWithParamsResult);

    // 3. 发送 POST 请求
    String postUrl = "http://httpbin.org/post";
    String postResult = HttpUtil.post(postUrl, "这是POST请求体"); // 可以是字符串或Map
    System.out.println("POST request result:\n" + postResult);

    // 4. 发送带表单参数的 POST 请求
    Map<String, Object> formParams = new HashMap<>();
    formParams.put("username", "admin");
    formParams.put("password", "123456");
    String postFormResult = HttpUtil.post(postUrl, formParams);
    System.out.println("POST form result:\n" + postFormResult);

    // 5. 发送请求并获取 HttpResponse 对象 (可以获取状态码、头部等)
    try (HttpResponse response = HttpUtil.createGet(getUrl).execute()) {
        System.out.println("Response status code: " + response.getStatus());
        System.out.println("Response body:\n" + response.body());
        System.out.println("Content-Type: " + response.header("Content-Type"));
    } catch (Exception e) {
        e.printStackTrace();
    }

    // 6. 下载文件
    // String fileUrl = "http://example.com/path/to/your/file.zip"; // 替换为实际文件URL
    // String downloadPath = "downloaded_file.zip";
    // long fileSize = HttpUtil.downloadFile(fileUrl, downloadPath);
    // System.out.println("Downloaded file size: " + fileSize);

    // 7. 上传文件
    // String uploadUrl = "http://example.com/upload"; // 替换为实际上传URL
    // File uploadFile = FileUtil.file("path/to/your/local/file.txt"); // 替换为实际文件
    // Map<String, Object> fileParams = new HashMap<>();
    // fileParams.put("file", uploadFile);
    // fileParams.put("description", "This is a test file.");
    // String uploadResult = HttpUtil.post(uploadUrl, fileParams);
    // System.out.println("Upload result: " + uploadResult);
}

}
“`

HttpUtil 支持设置请求头、超时时间、代理、HTTPS、cookie等,功能比较全面,能够满足大部分常见的HTTP请求需求。

4.6 JSON 工具:JSONUtil

在现代应用中,JSON 是主要的数据交换格式。Hutool 的 JSONUtil 提供了便捷的JSON字符串与Java对象/集合之间的转换功能。

“`java
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class JsonUtilExample {

// 示例Bean
static class User {
    private String name;
    private int age;
    private boolean isStudent;
    private List<String> courses;

    // 必须有无参构造函数,以及getter/setter
    public User() {}

    public User(String name, int age, boolean isStudent, List<String> courses) {
        this.name = name;
        this.age = age;
        this.isStudent = isStudent;
        this.courses = courses;
    }

    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; }
    public boolean isStudent() { return isStudent; }
    public void setStudent(boolean student) { isStudent = student; }
    public List<String> getCourses() { return courses; }
    public void setCourses(List<String> courses) { this.courses = courses; }

    @Override
    public String toString() {
        return "User{" +
               "name='" + name + '\'' +
               ", age=" + age +
               ", isStudent=" + isStudent +
               ", courses=" + courses +
               '}';
    }
}

public static void main(String[] args) {
    // 1. Java对象转JSON字符串
    User user = new User("张三", 20, true, CollUtil.newArrayList("Math", "English"));
    String jsonStr = JSONUtil.toJsonStr(user);
    System.out.println("Object to JSON string: " + jsonStr); // {"name":"张三","age":20,"isStudent":true,"courses":["Math","English"]}

    // 2. Map转JSON字符串
    Map<String, Object> map = new HashMap<>();
    map.put("city", "Beijing");
    map.put("zipCode", 100000);
    String jsonStrFromMap = JSONUtil.toJsonStr(map);
    System.out.println("Map to JSON string: " + jsonStrFromMap); // {"city":"Beijing","zipCode":100000}

    // 3. List转JSON字符串
    List<String> fruits = CollUtil.newArrayList("apple", "banana", "cherry");
    String jsonStrFromList = JSONUtil.toJsonStr(fruits);
    System.out.println("List to JSON string: " + jsonStrFromList); // ["apple","banana","cherry"]


    // 4. JSON字符串转Java对象
    String userJson = "{\"name\":\"李四\",\"age\":22,\"isStudent\":false,\"courses\":null}";
    User parsedUser = JSONUtil.toBean(userJson, User.class);
    System.out.println("JSON string to Object: " + parsedUser); // User{name='李四', age=22, isStudent=false, courses=null}

    // 5. JSON字符串转JSONObject或JSONArray
    String jsonObjectStr = "{\"key\":\"value\", \"number\":123}";
    JSONObject jsonObject = JSONUtil.parseObj(jsonObjectStr);
    System.out.println("JSON string to JSONObject: " + jsonObject);
    System.out.println("Get value from JSONObject: " + jsonObject.getStr("key"));
    System.out.println("Get number from JSONObject: " + jsonObject.getInt("number"));

    String jsonArrayStr = "[{\"id\":1,\"name\":\"A\"}, {\"id\":2,\"name\":\"B\"}]";
    JSONArray jsonArray = JSONUtil.parseArray(jsonArrayStr);
    System.out.println("JSON string to JSONArray: " + jsonArray);
    System.out.println("Get first element from JSONArray: " + jsonArray.get(0));
    System.out.println("Convert JSONArray to List of Beans:");
    List<User> userListFromJson = JSONUtil.toList(jsonArray, User.class); // 将JSONArray转换为指定Bean的List
    userListFromJson.forEach(System.out::println);

    // 6. 判断是否是JSON
    System.out.println("'{}' is JSON: " + JSONUtil.isJson("{}")); // true
    System.out.println("'[]' is JSON: " + JSONUtil.isJson("[]")); // true
    System.out.println("'abc' is JSON: " + JSONUtil.isJson("abc")); // false
}

}
“`

JSONUtil 的内部实现可以依赖于 Jackson、Fastjson 或 Gson 等第三方库,Hutool 会优先使用classpath中存在的库,如果没有找到则使用自带的极简JSON解析器。这提供了很好的灵活性和兼容性。

4.7 Bean 工具:BeanUtil

BeanUtil 主要用于Java Bean(POJO)之间的属性复制、Bean与Map之间的转换等操作,在对象之间传递数据时非常有用。

“`java
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;

import java.util.Map;

public class BeanUtilExample {

static class SourceBean {
    private String name;
    private int age;
    private String address; // 源Bean有,目标Bean没有
    private String commonField; // 源和目标都有
    private boolean isStudent; // boolean类型

    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; }
    public String getAddress() { return address; }
    public void setAddress(String address) { this.address = address; }
    public String getCommonField() { return commonField; }
    public void setCommonField(String commonField) { this.commonField = commonField; }
    public boolean isStudent() { return isStudent; } // boolean类型
    public void setStudent(boolean student) { isStudent = student; }


    @Override
    public String toString() {
        return "SourceBean{" +
               "name='" + name + '\'' +
               ", age=" + age +
               ", address='" + address + '\'' +
               ", commonField='" + commonField + '\'' +
               ", isStudent=" + isStudent +
               '}';
    }
}

static class TargetBean {
    private String name; // 源和目标都有
    private Integer age; // 类型不同 (int -> Integer)
    private String email; // 源Bean没有,目标Bean有
    private String commonField; // 源和目标都有
    private Boolean student; // Boolean类型

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public String getCommonField() { return commonField; }
    public void setCommonField(String commonField) { this.commonField = commonField; }
    public Boolean getStudent() { return student; } // Boolean类型
    public void setStudent(Boolean student) { this.student = student; }


    @Override
    public String toString() {
        return "TargetBean{" +
               "name='" + name + '\'' +
               ", age=" + age +
               ", email='" + email + '\'' +
               ", commonField='" + commonField + '\'' +
               ", student=" + student +
               '}';
    }
}


public static void main(String[] args) {
    SourceBean source = new SourceBean();
    source.setName("Alice");
    source.setAge(30);
    source.setAddress("Beijing");
    source.setCommonField("Value1");
    source.setStudent(true);

    TargetBean target = new TargetBean();
    target.setEmail("[email protected]"); // 目标Bean已有的属性不会被覆盖,除非指定CopyOptions


    // 1. 属性复制 (Bean to Bean)
    // 默认只复制源对象非null属性到目标对象,同名属性且类型兼容的会被复制
    // 这里的 age (int -> Integer) 会自动转换
    BeanUtil.copyProperties(source, target);
    System.out.println("After copyProperties (default): " + target);
    // Output: TargetBean{name='Alice', age=30, email='[email protected]', commonField='Value1', student=true}
    // Notice: address was not copied as it's not in TargetBean
    // Notice: email was not overwritten
    // Notice: isStudent (boolean) was copied to student (Boolean)

    // 2. 属性复制,忽略某些属性
    TargetBean target2 = new TargetBean();
    target2.setEmail("[email protected]");
    BeanUtil.copyProperties(source, target2, "name", "age"); // 复制除了 name 和 age 之外的属性
    System.out.println("After copyProperties (ignore name, age): " + target2);
    // Output: TargetBean{name='null', age=null, email='[email protected]', commonField='Value1', student=true}


    // 3. 属性复制,使用CopyOptions
    TargetBean target3 = new TargetBean();
    target3.setEmail("[email protected]");
    // CopyOptions可以控制是否覆盖null值、是否忽略某些属性、是否进行类型转换等
    CopyOptions options = CopyOptions.create()
                                     .setIgnoreNullValue(false) // 不忽略null值(如果源属性为null,会覆盖目标属性)
                                     .setIgnoreProperties("address"); // 忽略address属性
                                     // .setMapUnderscoreToCamelCase(true); // 如果源是Map,key是下划线,目标是驼峰,可以开启转换

    BeanUtil.copyProperties(source, target3, options);
    System.out.println("After copyProperties (with options): " + target3);
    // Output: TargetBean{name='Alice', age=30, email='[email protected]', commonField='Value1', student=true}
    // In this case, IgnoreNullValue(false) didn't change the output because source has no null values for relevant fields.


    // 4. Bean转Map
    Map<String, Object> beanMap = BeanUtil.beanToMap(source);
    System.out.println("Bean to Map: " + beanMap);
    // Output: {address=Beijing, commonField=Value1, name=Alice, isStudent=true, age=30}

    // 5. Map转Bean
    Map<String, Object> mapForBean = new HashMap<>();
    mapForBean.put("name", "Wangwu");
    mapForBean.put("age", 25);
    mapForBean.put("email", "[email protected]"); // Map中有,目标Bean没有,不会复制
    mapForBean.put("student", false); // Map中有,目标Bean有
    mapForBean.put("extraField", "ignored"); // Map中没有对应的bean属性,忽略

    TargetBean beanFromMap = BeanUtil.mapToBean(mapForBean, TargetBean.class, false, CopyOptions.create()); // 第三个参数: 是否使用Spring的BeanUtils,第四个参数: CopyOptions
    System.out.println("Map to Bean: " + beanFromMap);
    // Output: TargetBean{name='Wangwu', age=25, email='null', commonField='null', student=false}
    // Notice: email and commonField were not copied from mapForBean because their keys don't match directly or weren't intended to map.
    // Correct mapping requires keys matching target bean property names (case-insensitive usually, but exact match safer).
    // commonField in mapForBean is missing, so it remains null. email is in mapForBean but not in TargetBean's expected properties for mapping.

    // Map to Bean with mapping options
    Map<String, Object> mapForBean2 = new HashMap<>();
    mapForBean2.put("name", "ZhaoLiu");
    mapForBean2.put("age", "28"); // 字符串年龄,Hutool会尝试转换
    mapForBean2.put("common_field", "MapValue"); // 下划线命名
    mapForBean2.put("is_student", true); // 下划线命名 boolean

    TargetBean beanFromMap2 = BeanUtil.mapToBean(mapForBean2, TargetBean.class, CopyOptions.create().setIgnoreError(true).setMapUnderscoreToCamelCase(true));
    System.out.println("Map to Bean (with underscore mapping): " + beanFromMap2);
    // Output: TargetBean{name='ZhaoLiu', age=28, email='null', commonField='MapValue', student=true}
    // Notice: age (String -> Integer) conversion happened.
    // Notice: common_field mapped to commonField due to setMapUnderscoreToCamelCase(true).
    // Notice: is_student mapped to student due to setMapUnderscoreToCamelCase(true).
}

}
“`

BeanUtil 在处理DTO、VO、Entity之间的转换时非常方便,减少了大量的手动get/set代码。它支持丰富的配置选项来控制复制行为,如忽略属性、忽略大小写、转换命名风格等。

4.8 加密解密工具:SecureUtil

SecureUtil 提供了各种常见的加密(如MD5、SHA系列)、对称加密(如AES)、非对称加密(如RSA)以及Base64编码等功能。

“`java
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.core.codec.Base64;

public class SecureUtilExample {
public static void main(String[] args) {
String data = “这是一段需要加密的文本”;

    // 1. MD5加密
    String md5Hash = SecureUtil.md5(data);
    System.out.println("MD5 Hash: " + md5Hash); // 1a2b... (32位小写)

    // 2. SHA256加密
    String sha256Hash = SecureUtil.sha256(data);
    System.out.println("SHA256 Hash: " + sha256Hash); // abcd... (64位小写)

    // 3. Base64 编码与解码
    String base64Encoded = Base64.encode(data);
    System.out.println("Base64 Encoded: " + base64Encoded); // ...

    String base64Decoded = Base64.decodeStr(base64Encoded);
    System.out.println("Base64 Decoded: " + base64Decoded); // 这是一段需要加密的文本

    // 4. 对称加密 - AES
    // 生成AES密钥
    byte[] key = SecureUtil.generateKey(cn.hutool.crypto.symmetric.SymmetricAlgorithm.AES).getEncoded();
    AES aes = SecureUtil.aes(key); // 使用密钥创建AES加密器

    String aesEncrypted = aes.encryptHex(data); // 加密为16进制字符串
    System.out.println("AES Encrypted: " + aesEncrypted);

    String aesDecrypted = aes.decryptStr(aesEncrypted); // 解密16进制字符串
    System.out.println("AES Decrypted: " + aesDecrypted); // 这是一段需要加密的文本

    // 5. 非对称加密 - RSA
    // 生成RSA密钥对
    RSA rsa = SecureUtil.rsa();
    String privateKey = rsa.getPrivateKeyBase64(); // 获取私钥Base64字符串
    String publicKey = rsa.getPublicKeyBase64(); // 获取公钥Base64字符串
    System.out.println("RSA Private Key: " + privateKey.substring(0, 50) + "...");
    System.out.println("RSA Public Key: " + publicKey.substring(0, 50) + "...");

    // 使用公钥加密,私钥解密
    String rsaEncrypted = rsa.encryptBcd(data, cn.hutool.core.lang.Dict.create().set("usePublic", true)); // 使用公钥加密,返回BCD码
    System.out.println("RSA Encrypted (Public Key): " + rsaEncrypted);

    String rsaDecrypted = rsa.decryptStrFromBcd(rsaEncrypted, cn.hutool.core.lang.Dict.create().set("usePrivate", true)); // 使用私钥解密BCD码
    System.out.println("RSA Decrypted (Private Key): " + rsaDecrypted); // 这是一段需要加密的文本

    // 使用私钥签名,公钥验签 (仅作示例,RSA签名和加解密是不同的用途)
    // String signed = rsa.sign(SecureUtil.sha256(data), cn.hutool.crypto.SignAlgorithm.SHA256withRSA); // 签名
    // System.out.println("RSA Signed (SHA256withRSA): " + signed);
    // boolean verify = SecureUtil.rsa(publicKey, null).verify(SecureUtil.sha256(data), cn.hutool.crypto.SignAlgorithm.SHA256withRSA, signed); // 验签
    // System.out.println("RSA Signature Verified: " + verify);
}

}
“`

SecureUtil 简化了Java标准库中复杂繁琐的加密API调用,提供了各种算法的链式调用和多种输出格式支持,非常实用。

4.9 还有更多…

除了上述核心模块,Hutool 还提供了大量其他实用工具,例如:

  • ObjectUtil: 对象操作,如判空、克隆、默认值、Equals、HashCode等。
  • NumberUtil: 数字操作,如判断是否是数字、四舍五入、百分比、随机数、阶乘、质数等。
  • IdUtil: ID生成器,如UUID、Snowflake算法。
  • SystemUtil: 获取系统信息,如操作系统、Java版本、内存信息等。
  • RuntimeUtil: 执行外部命令。
  • ZipUtil: 压缩和解压缩文件。
  • XmlUtil: XML解析和生成。
  • ExcelUtil: 读取和写入Excel文件(需要额外模块)。
  • CsvUtil: 读取和写入CSV文件(需要额外模块)。
  • CaptchaUtil: 生成验证码图片(需要额外模块)。
  • CreditCodeUtil: 社会信用代码校验。
  • CronUtil: 定时任务工具。
  • ImageUtil: 图片处理,如缩放、裁剪、加水印、转换格式等(需要额外模块)。
  • Validator: 数据校验工具。
  • ReflectUtil: 反射工具,简化反射操作。
  • ClassUtil: 类相关的工具,如获取包名、类名、父类、接口等。
  • EnumUtil: 枚举相关的工具。
  • RuntimeUtil: 执行外部命令。
  • NetUtil: 网络工具,如判断端口是否可用、是否是内网IP、Ping等。

这些工具类都遵循了 Hutool 简洁易用的设计风格,能够帮助开发者解决各种各样的实际问题。

5. 高级用法和注意事项

  • 链式调用: Hutool 中的一些工具类支持链式调用,例如 HttpUtil.createGet(url).header(...).form(...).execute(),可以使代码更加流畅。
  • 模块化按需引入: 尽管 hutool-all 方便,但对于对包大小有要求的项目,强烈建议按需引入特定的模块,例如 hutool-corehutool-httphutool-json 等。
  • 文档是最好的老师: Hutool 的官方文档(主要在 Gitee Pages)非常详尽,包含每个工具类的方法列表和示例,遇到问题时查阅文档通常能快速解决。
  • 源码学习: Hutool 的源码结构清晰,注释完善,是学习编写实用工具类的好范例。
  • 版本兼容性: Hutool 保持了较好的API稳定性,但在升级大版本时,还是建议查阅更新日志,了解是否有API变动。

6. 总结

Hutool 作为一个强大、易用、无额外复杂依赖的Java实用工具库,极大地提升了Java开发者的工作效率。它提供的一站式解决方案涵盖了日常开发的方方面面,简洁的API设计和完善的中文文档降低了学习和使用门槛。

无论是初学者还是经验丰富的开发者,Hutool 都能成为你工具箱中不可或缺的一部分。通过合理利用 Hutool 提供的各种工具类,你可以写出更少、更清晰、更健壮的代码,将更多精力投入到业务逻辑的实现上。

如果你还没有在项目中使用 Hutool,不妨现在就尝试一下,相信它会给你带来惊喜。让 Hutool 这把“瑞士军刀”助你在Java开发的道路上更加得心应手!


注意: 文中Hutool的版本号 5.8.26 仅为撰写时的示例,实际使用时请查阅官方仓库获取最新稳定版本。文中提供的代码示例可以直接复制到IDE中运行,但需要确保已正确引入Hutool依赖。


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部