找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2220

积分

0

好友

298

主题
发表于 昨天 21:46 | 查看: 3| 回复: 0

核心亮点:本文将深入剖析 Hutool 5.x 的核心工具类,通过 20 个高频实战场景,展示如何用一行代码替代百行原生 Java 代码。涵盖字符串处理、日期计算、加密解密、HTTP 请求、文件操作等开发必备技能,让你的 Java 代码更简洁、更优雅。

一、为什么需要 Hutool?Java 开发的效率利器

在日常 Java 开发中,你是否经常需要编写大量重复、繁琐的工具代码?处理日期、校验字符串、发送 HTTP 请求……这些看似简单的任务,用原生 JDK 写起来却异常冗长。

1.1 原生 Java 开发的常见痛点

痛点 真实场景 代码复杂度
日期计算繁琐 计算两个日期相差天数、获取本月第一天 10+ 行代码,涉及 Calendar、SimpleDateFormat
字符串处理重复 判空、去空格、截取、替换 需要 Apache Commons Lang 或手写工具类
HTTP 请求冗长 发起一个 POST 请求 需要 HttpClient/RestTemplate,配置复杂
文件操作麻烦 读取文件、复制文件、计算 MD5 需要处理流、关闭资源、异常处理
JSON 转换繁琐 对象转 JSON、JSON 转对象 需要引入 Jackson/Gson,配置繁多

1.2 Hutool 的核心价值:小而全的 Java 工具包

Hutool(谐音“糊涂”,寓意“难得糊涂”)是一个小而全的 Java 工具类库,旨在减少代码搜索成本,提高开发效率。

Hutool = Hu(糊涂)+ Tool(工具)

核心优势

  • 零依赖:核心模块不依赖任何第三方库(JDK 8+)
  • 功能全面:覆盖 Java 开发 90% 的常用工具需求
  • 中文文档:全中文 API 文档,学习成本低
  • 持续更新:社区活跃,Bug 修复及时
  • 企业级应用:已在阿里巴巴、腾讯、字节等大厂广泛使用

作为一个功能强大的Java工具库,它解决了我们日常开发中的诸多痛点。

二、快速开始:5 分钟上手 Hutool

2.1 Maven 依赖配置

<!-- Hutool 全部模块 -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.23</version>
</dependency>

<!-- 或者按需引入(推荐生产环境) -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-core</artifactId>
    <version>5.8.23</version>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-http</artifactId>
    <version>5.8.23</version>
</dependency>

2.2 Gradle 配置

implementation 'cn.hutool:hutool-all:5.8.23'

2.3 模块说明

模块 功能 依赖
hutool-core 核心工具类(字符串、日期、数字等)
hutool-crypto 加密解密(MD5、SHA、AES、RSA)
hutool-http HTTP 请求
hutool-json JSON 处理
hutool-db JDBC 封装、数据库操作
hutool-extra 扩展模块(邮件、模板引擎等) 有第三方依赖

三、字符串处理:StrUtil 的 8 个必会技巧

3.1 判空与默认值

import cn.hutool.core.util.StrUtil;

public class StrUtilDemo {

    public static void main(String[] args) {
        String str = null;

        // 1. 判断是否为空(null、""、"   " 都视为空)
        System.out.println(StrUtil.isBlank(str));       // true
        System.out.println(StrUtil.isBlank("  "));      // true
        System.out.println(StrUtil.isNotBlank("hello")); // true

        // 2. 为空时返回默认值
        String result = StrUtil.blankToDefault(str, "默认值");
        System.out.println(result); // "默认值"

        // 3. 链式判空(类似 Optional)
        String value = StrUtil.emptyToNull(null);        // null
        String value2 = StrUtil.nullToEmpty(null);       // ""
    }
}

实战场景:接口参数校验

@PostMapping("/user")
public Result createUser(@RequestBody UserDTO dto) {
    // 传统写法
    if (dto.getUsername() == null || dto.getUsername().trim().isEmpty()) {
        return Result.error("用户名不能为空");
    }

    // Hutool 写法
    if (StrUtil.isBlank(dto.getUsername())) {
        return Result.error("用户名不能为空");
    }

    // 更简洁的写法
    String username = StrUtil.blankToDefault(dto.getUsername(), "");
}

3.2 字符串截取与切割

public class StrCutDemo {

    public static void main(String[] args) {
        String str = "hello@world@hutool";

        // 1. 截取子串(支持负数索引)
        System.out.println(StrUtil.sub(str, 0, 5));    // "hello"
        System.out.println(StrUtil.sub(str, -6, -1));  // "hutoo"

        // 2. 按分隔符切割
        String[] parts = StrUtil.split(str, "@");
        // ["hello", "world", "hutool"]

        // 3. 限制切割次数
        String[] parts2 = StrUtil.split(str, "@", 2);
        // ["hello", "world@hutool"]

        // 4. 切割并去除空白
        String csv = "apple,  banana,  orange  ";
        List<String> fruits = StrUtil.splitTrim(csv, ",");
        // ["apple", "banana", "orange"]
    }
}

实战场景:处理 URL 参数

public class UrlParser {

    public Map<String, String> parseQueryString(String url) {
        String query = StrUtil.subAfter(url, "?", false);

        Map<String, String> params = new HashMap<>();
        String[] pairs = StrUtil.split(query, "&");

        for (String pair : pairs) {
            String[] kv = StrUtil.split(pair, "=", 2);
            if (kv.length == 2) {
                params.put(kv[0], kv[1]);
            }
        }
        return params;
    }
}

3.3 字符串格式化与模板

public class StrFormatDemo {

    public static void main(String[] args) {
        // 1. 占位符格式化(类似 Python 的 format)
        String msg = StrUtil.format("用户 {} 在 {} 登录成功", "张三", "2024-01-15");
        // "用户 张三 在 2024-01-15 登录成功"

        // 2. 模板引擎(类似变量替换)
        String template = "你好,{name}!您的订单 {orderNo} 已发货。";
        String result = StrUtil.format(template,
            Dict.create().set("name", "李四").set("orderNo", "ORD2024001"));

        // 3. 字符串填充
        String padded = StrUtil.padPre("42", 5, '0');  // "00042"
        String padded2 = StrUtil.padAfter("42", 5, '0'); // "42000"
    }
}

实战场景:生成订单号

@Service
public class OrderService {

    public String generateOrderNo() {
        // 格式:年月日 + 6位流水号
        String date = DateUtil.format(new Date(), "yyyyMMdd");
        long seq = redisTemplate.opsForValue().increment("order:seq");
        return date + StrUtil.padPre(String.valueOf(seq), 6, '0');
        // 结果:20240115000001
    }
}

3.4 字符串脱敏处理

public class DesensitizedDemo {

    public static void main(String[] args) {
        // 1. 手机号脱敏
        String phone = "13812345678";
        System.out.println(StrUtil.hide(phone, 3, 7));  // "138****5678"

        // 2. 身份证号脱敏
        String idCard = "110101199001011234";
        System.out.println(StrUtil.hide(idCard, 6, 14)); // "110101********1234"

        // 3. 银行卡脱敏
        String bankCard = "6222021234567890123";
        System.out.println(StrUtil.hide(bankCard, 4, 15)); // "6222***********0123"

        // 4. 邮箱脱敏
        String email = "zhangsan@qq.com";
        String prefix = StrUtil.subBefore(email, "@", false);
        String domain = StrUtil.subAfter(email, "@", false);
        String masked = StrUtil.hide(prefix, 1, prefix.length()) + "@" + domain;
        // "z*******@qq.com"
    }
}

实战场景:用户隐私数据脱敏

@Component
public class UserInfoMasker {

    public UserVO maskSensitiveInfo(User user) {
        UserVO vo = new UserVO();
        vo.setUsername(user.getUsername());

        // 手机号脱敏
        if (StrUtil.isNotBlank(user.getPhone())) {
            vo.setPhone(StrUtil.hide(user.getPhone(), 3, 7));
        }

        // 身份证号脱敏
        if (StrUtil.isNotBlank(user.getIdCard())) {
            vo.setIdCard(StrUtil.hide(user.getIdCard(), 6, 14));
        }

        return vo;
    }
}

四、日期时间处理:DateUtil 的 10 个高频用法

4.1 日期格式化与解析

import cn.hutool.core.date.DateUtil;
import java.util.Date;

public class DateFormatDemo {

    public static void main(String[] args) {
        Date now = new Date();

        // 1. 格式化日期(常用格式内置)
        System.out.println(DateUtil.format(now, "yyyy-MM-dd HH:mm:ss"));
        System.out.println(DateUtil.formatDate(now));      // "2024-01-15"
        System.out.println(DateUtil.formatTime(now));      // "14:30:25"
        System.out.println(DateUtil.formatDateTime(now));  // "2024-01-15 14:30:25"

        // 2. 解析日期字符串
        Date date1 = DateUtil.parse("2024-01-15");
        Date date2 = DateUtil.parse("2024-01-15 14:30:25");
        Date date3 = DateUtil.parse("15/01/2024", "dd/MM/yyyy");

        // 3. 获取日期对象
        Date today = DateUtil.date();           // 当前时间
        Date yesterday = DateUtil.yesterday();  // 昨天
        Date tomorrow = DateUtil.tomorrow();    // 明天
    }
}

4.2 日期计算

public class DateCalcDemo {

    public static void main(String[] args) {
        Date date = DateUtil.parse("2024-01-15 10:00:00");

        // 1. 日期偏移
        Date newDate = DateUtil.offsetDay(date, 3);      // +3天
        Date newHour = DateUtil.offsetHour(date, -2);    // -2小时
        Date newMonth = DateUtil.offsetMonth(date, 1);   // +1月

        // 2. 获取月初、月末
        Date monthStart = DateUtil.beginOfMonth(date);   // 2024-01-01 00:00:00
        Date monthEnd = DateUtil.endOfMonth(date);       // 2024-01-31 23:59:59

        // 3. 获取周初、周末
        Date weekStart = DateUtil.beginOfWeek(date);     // 周一
        Date weekEnd = DateUtil.endOfWeek(date);         // 周日

        // 4. 获取当天开始、结束
        Date dayStart = DateUtil.beginOfDay(date);       // 00:00:00
        Date dayEnd = DateUtil.endOfDay(date);           // 23:59:59
    }
}

实战场景:统计报表日期范围

@Service
public class ReportService {

    /**
     * 获取本周报表数据
     */
    public List<Order> getThisWeekOrders() {
        Date today = DateUtil.date();
        Date weekStart = DateUtil.beginOfWeek(today);
        Date weekEnd = DateUtil.endOfWeek(today);

        return orderMapper.selectByDateRange(weekStart, weekEnd);
    }

    /**
     * 获取上月报表数据
     */
    public List<Order> getLastMonthOrders() {
        Date today = DateUtil.date();
        Date lastMonth = DateUtil.offsetMonth(today, -1);
        Date monthStart = DateUtil.beginOfMonth(lastMonth);
        Date monthEnd = DateUtil.endOfMonth(lastMonth);

        return orderMapper.selectByDateRange(monthStart, monthEnd);
    }
}

4.3 日期间隔计算

public class DateBetweenDemo {

    public static void main(String[] args) {
        Date start = DateUtil.parse("2024-01-01");
        Date end = DateUtil.parse("2024-01-15");

        // 1. 计算相差天数
        long days = DateUtil.betweenDay(start, end, false);  // 14

        // 2. 计算相差小时
        long hours = DateUtil.betweenHour(start, end, false);

        // 3. 计算相差月份
        long months = DateUtil.betweenMonth(start, end, false);

        // 4. 友好的时间差描述
        String duration = DateUtil.formatBetween(start, end);
        // "14天"

        // 5. 更详细的间隔
        DateUtil.BetweenFormatter.Level level = DateUtil.BetweenFormatter.Level.DAY;
        String detail = DateUtil.formatBetween(start, end, level);
    }
}

实战场景:计算会员剩余天数

@Service
public class MemberService {

    public MemberStatus getMemberStatus(Long userId) {
        Member member = memberMapper.selectByUserId(userId);

        Date now = DateUtil.date();
        Date expireDate = member.getExpireDate();

        if (expireDate.before(now)) {
            return MemberStatus.EXPIRED;
        }

        long remainingDays = DateUtil.betweenDay(now, expireDate, false);

        return MemberStatus.builder()
            .isValid(true)
            .remainingDays(remainingDays)
            .expireDate(expireDate)
            .build();
    }
}

4.4 年龄与星座计算

public class AgeAndZodiacDemo {

    public static void main(String[] args) {
        String idCard = "110101199001011234";

        // 从身份证号提取生日
        String birthStr = StrUtil.sub(idCard, 6, 14);  // "19900101"
        Date birthDate = DateUtil.parse(birthStr, "yyyyMMdd");

        // 计算年龄
        int age = DateUtil.ageOfNow(birthDate);
        System.out.println("年龄:" + age);  // 34

        // 获取星座
        String zodiac = DateUtil.getZodiac(DateUtil.month(birthDate) + 1,
                                                DateUtil.dayOfMonth(birthDate));
        System.out.println("星座:" + zodiac);  // "摩羯座"

        // 获取生肖
        String chineseZodiac = DateUtil.getChineseZodiac(DateUtil.year(birthDate));
        System.out.println("生肖:" + chineseZodiac);  // "马"
    }
}

五、集合工具:CollUtil 与 BeanUtil 的高效操作

5.1 集合判空与转换

import cn.hutool.core.collection.CollUtil;
import java.util.*;

public class CollUtilDemo {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("a", "b", "c");

        // 1. 判空(null、空集合都视为空)
        System.out.println(CollUtil.isEmpty(list));      // false
        System.out.println(CollUtil.isNotEmpty(list));   // true

        // 2. 创建集合(避免空指针)
        List<String> safeList = CollUtil.emptyIfNull(null);
        // 返回空列表,而非 null

        // 3. 集合转字符串
        String joined = CollUtil.join(list, ", ");
        // "a, b, c"

        // 4. 添加元素(自动处理 null)
        List<String> newList = CollUtil.newArrayList();
        CollUtil.addAll(newList, list);
        CollUtil.addAll(newList, null);  // 不会报错
    }
}

5.2 列表分组与排序

public class ListGroupDemo {

    public static void main(String[] args) {
        List<User> users = Arrays.asList(
            new User("张三", "技术部", 25),
            new User("李四", "产品部", 30),
            new User("王五", "技术部", 28)
        );

        // 1. 按部门分组
        Map<String, List<User>> groupByDept = CollUtil.groupByKey(users, User::getDept);
        // {技术部=[张三, 王五], 产品部=[李四]}

        // 2. 提取属性列表
        List<String> names = CollUtil.map(users, User::getName, true);
        // ["张三", "李四", "王五"]

        // 3. 查找符合条件的元素
        List<User> techUsers = CollUtil.filter(users, u -> "技术部".equals(u.getDept()));

        // 4. 字段去重
        List<String> depts = CollUtil.distinct(CollUtil.map(users, User::getDept, true));
        // ["技术部", "产品部"]
    }
}

实战场景:数据导出前的处理

@Service
public class ExportService {

    public void exportUserReport() {
        List<User> users = userMapper.selectAll();

        // 1. 过滤无效数据
        List<User> validUsers = CollUtil.filter(users,
            u -> StrUtil.isNotBlank(u.getEmail()) && u.getStatus() == 1);

        // 2. 按部门分组统计
        Map<String, List<User>> groupByDept = CollUtil.groupByKey(validUsers, User::getDept);

        // 3. 生成报表数据
        List<ReportRow> rows = new ArrayList<>();
        groupByDept.forEach((dept, list) -> {
            ReportRow row = new ReportRow();
            row.setDept(dept);
            row.setCount(list.size());
            row.setAvgAge(CollUtil.averageInt(list, User::getAge));
            rows.add(row);
        });

        // 4. 按人数排序
        CollUtil.sortByProperty(rows, "count");
    }
}

5.3 Bean 拷贝与属性操作

import cn.hutool.core.bean.BeanUtil;
import java.util.*;

public class BeanUtilDemo {

    public static void main(String[] args) {
        User user = new User();
        user.setId(1L);
        user.setName("张三");
        user.setAge(25);

        // 1. Bean 转 Map
        Map<String, Object> map = BeanUtil.beanToMap(user);
        // {id=1, name=张三, age=25}

        // 2. Map 转 Bean
        User newUser = BeanUtil.mapToBean(map, User.class, true);

        // 3. Bean 拷贝(属性名相同才拷贝)
        UserDTO dto = new UserDTO();
        BeanUtil.copyProperties(user, dto);

        // 4. 忽略 null 值拷贝
        BeanUtil.copyProperties(user, dto, CopyOptions.create().ignoreNullValue());

        // 5. 批量拷贝
        List<User> users = getUsers();
        List<UserDTO> dtos = BeanUtil.copyToList(users, UserDTO.class);
    }
}

实战场景:DTO 与 Entity 转换

@Service
public class UserService {

    public UserDTO getUserById(Long id) {
        User user = userMapper.selectById(id);
        if (user == null) {
            return null;
        }

        // 传统写法:手动 set
        UserDTO dto = new UserDTO();
        dto.setId(user.getId());
        dto.setName(user.getName());
        dto.setAge(user.getAge());
        // ... 更多字段

        // Hutool 写法:一行搞定
        UserDTO dto2 = BeanUtil.copyProperties(user, UserDTO.class);

        return dto2;
    }

    public List<UserVO> listUsers() {
        List<User> users = userMapper.selectList();

        // 批量转换
        return BeanUtil.copyToList(users, UserVO.class);
    }
}

六、HTTP 请求:HttpUtil 的 6 种实战模式

6.1 GET 请求

import cn.hutool.http.HttpUtil;
import cn.hutool.http.HttpResponse;

public class HttpGetDemo {

    public static void main(String[] args) {
        // 1. 简单 GET 请求
        String result = HttpUtil.get("https://api.example.com/users");

        // 2. 带参数的 GET 请求
        Map<String, Object> params = new HashMap<>();
        params.put("page", 1);
        params.put("size", 10);
        String result2 = HttpUtil.get("https://api.example.com/users", params);

        // 3. 获取响应头等信息
        HttpResponse response = HttpUtil.createGet("https://api.example.com/users")
            .form(params)
            .timeout(5000)
            .execute();

        int status = response.getStatus();
        String body = response.body();
        String contentType = response.header("Content-Type");
    }
}

6.2 POST 请求

public class HttpPostDemo {

    public static void main(String[] args) {
        // 1. 表单提交
        Map<String, Object> form = new HashMap<>();
        form.put("username", "admin");
        form.put("password", "123456");
        String result = HttpUtil.post("https://api.example.com/login", form);

        // 2. JSON 提交
        String jsonBody = "{\"username\":\"admin\",\"password\":\"123456\"}";
        String result2 = HttpUtil.post("https://api.example.com/login")
            .body(jsonBody)
            .header("Content-Type", "application/json")
            .execute()
            .body();

        // 3. 完整控制
        HttpResponse response = HttpUtil.createPost("https://api.example.com/login")
            .header("Authorization", "Bearer token123")
            .header("X-Request-ID", UUID.randomUUID().toString())
            .body(jsonBody)
            .timeout(10000)
            .execute();
    }
}

实战场景:调用第三方 API

@Service
public class SmsService {

    @Value("${sms.api.url}")
    private String smsApiUrl;

    @Value("${sms.api.key}")
    private String apiKey;

    public boolean sendSms(String phone, String templateCode, Map<String, String> params) {
        try {
            // 构建请求参数
            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("phone", phone);
            requestBody.put("templateCode", templateCode);
            requestBody.put("params", params);

            String json = JSONUtil.toJsonStr(requestBody);

            // 发送请求
            HttpResponse response = HttpUtil.createPost(smsApiUrl)
                .header("Authorization", "Bearer " + apiKey)
                .header("Content-Type", "application/json")
                .body(json)
                .timeout(5000)
                .execute();

            // 解析响应
            if (response.getStatus() == 200) {
                String body = response.body();
                JSONObject jsonObject = JSONUtil.parseObj(body);
                return jsonObject.getBool("success", false);
            }

            return false;
        } catch (Exception e) {
            log.error("发送短信失败", e);
            return false;
        }
    }
}

6.3 文件上传与下载

public class HttpFileDemo {

    public static void main(String[] args) {
        // 1. 文件上传
        String result = HttpUtil.post("https://api.example.com/upload")
            .form("file", new File("/path/to/file.pdf"))
            .form("description", "合同文件")
            .execute()
            .body();

        // 2. 文件下载
        HttpUtil.downloadFile("https://example.com/file.pdf",
            FileUtil.file("/local/path/file.pdf"));

        // 3. 下载到字节数组
        byte[] bytes = HttpUtil.downloadBytes("https://example.com/file.pdf");

        // 4. 带进度监控的下载
        HttpUtil.downloadFile("https://example.com/large-file.zip",
            FileUtil.file("/local/path/large-file.zip"),
            new StreamProgress() {
                @Override
                public void start() {
                    System.out.println("开始下载...");
                }

                @Override
                public void progress(long progressSize) {
                    System.out.println("已下载: " + progressSize + " bytes");
                }

                @Override
                public void finish() {
                    System.out.println("下载完成!");
                }
            });
    }
}

七、文件操作:FileUtil 的 8 个实用技巧

7.1 文件读写

import cn.hutool.core.io.FileUtil;
import java.io.File;

public class FileReadWriteDemo {

    public static void main(String[] args) {
        // 1. 读取文件为字符串
        String content = FileUtil.readUtf8String("/path/to/file.txt");

        // 2. 读取为行列表
        List<String> lines = FileUtil.readLines("/path/to/file.txt", "UTF-8");

        // 3. 写入字符串到文件
        FileUtil.writeUtf8String("Hello Hutool!", "/path/to/output.txt");

        // 4. 追加写入
        FileUtil.appendUtf8String("\n新内容", "/path/to/output.txt");

        // 5. 写入行列表
        List<String> data = Arrays.asList("第一行", "第二行", "第三行");
        FileUtil.writeLines(data, "/path/to/output.txt", "UTF-8");
    }
}

7.2 文件与目录操作

public class FileOperateDemo {

    public static void main(String[] args) {
        // 1. 创建目录
        File dir = FileUtil.mkdir("/path/to/new/dir");

        // 2. 创建临时文件
        File tempFile = FileUtil.createTempFile("prefix", ".tmp", true);

        // 3. 复制文件
        FileUtil.copy("/source/file.txt", "/dest/file.txt", true);

        // 4. 移动文件
        FileUtil.move("/source/file.txt", "/dest/file.txt", true);

        // 5. 删除文件或目录
        FileUtil.del("/path/to/delete");

        // 6. 清空目录(不删除目录本身)
        FileUtil.clean("/path/to/clean");

        // 7. 遍历目录
        List<File> files = FileUtil.loopFiles("/path/to/dir");

        // 8. 按扩展名过滤
        List<File> txtFiles = FileUtil.loopFiles("/path/to/dir",
            file -> file.getName().endsWith(".txt"));
    }
}

实战场景:日志文件归档

@Component
public class LogArchiver {

    @Scheduled(cron = "0 0 2 * * ?")  // 每天凌晨2点执行
    public void archiveLogs() {
        String logDir = "/var/log/myapp";
        String archiveDir = "/var/log/myapp/archive" +
            DateUtil.format(new Date(), "yyyyMM");

        // 1. 创建归档目录
        FileUtil.mkdir(archiveDir);

        // 2. 获取7天前的日志文件
        Date sevenDaysAgo = DateUtil.offsetDay(new Date(), -7);

        List<File> oldLogs = FileUtil.loopFiles(logDir, file -> {
            if (!file.getName().endsWith(".log")) {
                return false;
            }
            return FileUtil.lastModifiedTime(file).before(sevenDaysAgo);
        });

        // 3. 压缩并移动
        for (File log : oldLogs) {
            String zipName = log.getName() + ".zip";
            ZipUtil.zip(log.getAbsolutePath(), archiveDir + "/" + zipName);
            FileUtil.del(log);
        }

        log.info("归档完成,共处理 {} 个文件", oldLogs.size());
    }
}

7.3 文件类型与编码检测

public class FileTypeDemo {

    public static void main(String[] args) {
        File file = new File("/path/to/file.jpg");

        // 1. 获取扩展名
        String ext = FileUtil.extName(file);  // "jpg"

        // 2. 获取文件名(不带扩展名)
        String name = FileUtil.mainName(file);  // "file"

        // 3. 检测文件类型(通过魔数)
        String type = FileTypeUtil.getType(file);
        // "jpg", "png", "pdf", "doc" 等

        // 4. 计算文件大小(友好显示)
        long size = FileUtil.size(file);
        String readableSize = FileUtil.readableFileSize(size);
        // "2.5 MB"
    }
}

八、加密解密:SecureUtil 的安全实践

8.1 摘要算法(MD5、SHA)

import cn.hutool.crypto.SecureUtil;

public class DigestDemo {

    public static void main(String[] args) {
        String password = "123456";

        // 1. MD5 加密(32位小写)
        String md5 = SecureUtil.md5(password);
        // "e10adc3949ba59abbe56e057f20f883e"

        // 2. MD5 加密(16位)
        String md5_16 = SecureUtil.md5(password).substring(8, 24);

        // 3. SHA256 加密
        String sha256 = SecureUtil.sha256(password);

        // 4. 文件 MD5
        String fileMd5 = SecureUtil.md5(new File("/path/to/file.zip"));

        // 5. 加盐 MD5(更安全)
        String salt = RandomUtil.randomString(6);
        String saltedMd5 = SecureUtil.md5(password + salt);
    }
}

实战场景:用户密码加密

@Service
public class UserService {

    private static final String SALT = "hutool_salt_2024";

    public void register(String username, String password) {
        // 密码加密存储
        String encryptedPwd = SecureUtil.md5(password + SALT);

        User user = new User();
        user.setUsername(username);
        user.setPassword(encryptedPwd);
        userMapper.insert(user);
    }

    public boolean login(String username, String password) {
        User user = userMapper.selectByUsername(username);
        if (user == null) {
            return false;
        }

        String encryptedPwd = SecureUtil.md5(password + SALT);
        return encryptedPwd.equals(user.getPassword());
    }
}

8.2 对称加密(AES)

import cn.hutool.crypto.symmetric.AES;

public class AesDemo {

    public static void main(String[] args) {
        // 1. 生成随机密钥
        byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded();

        // 2. 创建 AES 实例
        AES aes = SecureUtil.aes(key);

        // 3. 加密
        String content = "Hello Hutool!";
        String encryptBase64 = aes.encryptBase64(content);
        // "加密后的Base64字符串"

        // 4. 解密
        String decryptStr = aes.decryptStr(encryptBase64);
        // "Hello Hutool!"
    }
}

实战场景:敏感数据加密存储

@Service
public class SensitiveDataService {

    @Value("${aes.key}")
    private String aesKey;

    private AES getAes() {
        return SecureUtil.aes(aesKey.getBytes());
    }

    public void saveBankCard(Long userId, String cardNo) {
        // 加密存储
        String encrypted = getAes().encryptBase64(cardNo);

        BankCard card = new BankCard();
        card.setUserId(userId);
        card.setCardNo(encrypted);
        bankCardMapper.insert(card);
    }

    public String getBankCard(Long userId) {
        BankCard card = bankCardMapper.selectByUserId(userId);
        if (card == null) {
            return null;
        }

        // 解密返回
        return getAes().decryptStr(card.getCardNo());
    }
}

8.3 非对称加密(RSA)

import cn.hutool.crypto.asymmetric.RSA;

public class RsaDemo {

    public static void main(String[] args) {
        // 1. 生成 RSA 密钥对
        RSA rsa = SecureUtil.rsa();

        // 获取公钥和私钥
        String publicKey = rsa.getPublicKeyBase64();
        String privateKey = rsa.getPrivateKeyBase64();

        // 2. 公钥加密
        String content = "敏感数据";
        String encryptBase64 = rsa.encryptBase64(content, CharsetUtil.CHARSET_UTF_8,
                                                        KeyType.PublicKey);

        // 3. 私钥解密
        String decryptStr = rsa.decryptStr(encryptBase64, KeyType.PrivateKey);
    }
}

九、JSON 处理:JSONUtil 的简洁之道

9.1 对象与 JSON 互转

import cn.hutool.json.JSONUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONArray;

public class JsonDemo {

    public static void main(String[] args) {
        User user = new User(1L, "张三", 25);

        // 1. 对象转 JSON 字符串
        String jsonStr = JSONUtil.toJsonStr(user);
        // {"id":1,"name":"张三","age":25}

        // 2. JSON 字符串转对象
        User parsedUser = JSONUtil.toBean(jsonStr, User.class);

        // 3. 美化输出
        String prettyJson = JSONUtil.toJsonPrettyStr(user);

        // 4. 转 JSONObject
        JSONObject jsonObject = JSONUtil.parseObj(user);
        String name = jsonObject.getStr("name");
    }
}

9.2 复杂 JSON 处理

public class ComplexJsonDemo {

    public static void main(String[] args) {
        String jsonStr = "{\n" +
            "  \"code\": 200,\n" +
            "  \"data\": {\n" +
            "    \"users\": [\n" +
            "      {\"id\": 1, \"name\": \"张三\"},\n" +
            "      {\"id\": 2, \"name\": \"李四\"}\n" +
            "    ],\n" +
            "    \"total\": 100\n" +
            "  }\n" +
            "}";

        // 1. 解析 JSON
        JSONObject root = JSONUtil.parseObj(jsonStr);

        // 2. 获取嵌套字段
        int code = root.getInt("code");
        JSONObject data = root.getJSONObject("data");
        JSONArray users = data.getJSONArray("users");

        // 3. 遍历数组
        for (int i = 0; i < users.size(); i++) {
            JSONObject user = users.getJSONObject(i);
            System.out.println(user.getStr("name"));
        }

        // 4. 数组转 List
        List<User> userList = users.toList(User.class);
    }
}

实战场景:处理第三方 API 响应

@Service
public class WeatherService {

    public WeatherInfo getWeather(String city) {
        String url = "https://api.weather.com/v1/current?city=" + city;
        String response = HttpUtil.get(url);

        // 解析响应
        JSONObject json = JSONUtil.parseObj(response);

        if (json.getInt("code") != 200) {
            throw new RuntimeException("获取天气失败:" + json.getStr("message"));
        }

        JSONObject data = json.getJSONObject("data");

        WeatherInfo info = new WeatherInfo();
        info.setCity(city);
        info.setTemperature(data.getStr("temperature"));
        info.setWeather(data.getStr("weather"));
        info.setHumidity(data.getStr("humidity"));

        return info;
    }
}

十、其他实用工具类

10.1 随机工具 RandomUtil

import cn.hutool.core.util.RandomUtil;

public class RandomDemo {

    public static void main(String[] args) {
        // 1. 随机整数
        int num = RandomUtil.randomInt(1, 100);  // 1-99

        // 2. 随机字符串
        String str = RandomUtil.randomString(6);     // "aB3dEf"
        String numbers = RandomUtil.randomNumbers(6); // "123456"

        // 3. 从列表随机取一个
        List<String> list = Arrays.asList("A", "B", "C");
        String randomItem = RandomUtil.randomEle(list);

        // 4. 随机打乱列表
        List<String> shuffled = RandomUtil.randomEleList(list, list.size());

        // 5. 生成 UUID(简化版)
        String uuid = RandomUtil.randomString(32);
    }
}

实战场景:生成验证码

@Service
public class VerifyCodeService {

    public String generateCode(String phone) {
        // 生成6位数字验证码
        String code = RandomUtil.randomNumbers(6);

        // 存入 Redis,5分钟过期
        redisTemplate.opsForValue().set(
            "verify:code:" + phone,
            code,
            5,
            TimeUnit.MINUTES
        );

        return code;
    }

    public boolean verifyCode(String phone, String code) {
        String key = "verify:code:" + phone;
        String storedCode = redisTemplate.opsForValue().get(key);

        if (storedCode != null && storedCode.equals(code)) {
            redisTemplate.delete(key);
            return true;
        }
        return false;
    }
}

10.2 唯一 ID 生成 IdUtil

import cn.hutool.core.util.IdUtil;

public class IdDemo {

    public static void main(String[] args) {
        // 1. UUID(带横线)
        String uuid = IdUtil.randomUUID();
        // "a1b2c3d4-e5f6-7890-abcd-ef1234567890"

        // 2. 简化 UUID(无横线)
        String simpleUuid = IdUtil.simpleUUID();
        // "a1b2c3d4e5f67890abcdef1234567890"

        // 3. ObjectId(MongoDB 风格)
        String objectId = IdUtil.objectId();
        // "65a1b2c3d4e5f67890abcdef"

        // 4. Snowflake ID(分布式唯一 ID)
        long snowflakeId = IdUtil.getSnowflakeNextId();
        // 1293847129384712938
    }
}

实战场景:分布式系统 ID 生成

@Service
public class OrderService {

    public String createOrder(Long userId) {
        // 使用雪花算法生成唯一订单号
        long orderId = IdUtil.getSnowflakeNextId();

        // 或者自定义格式
        String orderNo = DateUtil.format(new Date(), "yyyyMMdd") +
                            IdUtil.getSnowflakeNextIdStr().substring(0, 10);

        Order order = new Order();
        order.setOrderNo(orderNo);
        order.setUserId(userId);
        order.setStatus(OrderStatus.CREATED);
        orderMapper.insert(order);

        return orderNo;
    }
}

10.3 缓存工具 CacheUtil

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;

public class CacheDemo {

    public static void main(String[] args) {
        // 1. 创建定时缓存(5秒过期)
        TimedCache<String, String> cache = CacheUtil.newTimedCache(5000);

        // 2. 放入缓存
        cache.put("key1", "value1");
        cache.put("key2", "value2", 3000);  // 自定义过期时间3秒

        // 3. 获取缓存
        String value = cache.get("key1");

        // 4. 启动定时清理(可选)
        cache.schedulePrune(1000);  // 每秒清理一次过期项

        // 5. 停止清理
        cache.cancelPruneSchedule();
    }
}

十一、性能优化与最佳实践

11.1 Hutool 使用建议

场景 推荐做法 避免做法
字符串处理 使用 StrUtil 重复造轮子
日期计算 使用 DateUtil 使用 Calendar
HTTP 请求 简单场景用 Hutool 复杂场景用 OkHttp
JSON 处理 简单场景用 Hutool 复杂场景用 Jackson
数据库操作 简单场景用 Hutool 生产环境用 MyBatis

11.2 依赖管理建议

<!-- 开发环境:使用全部模块 -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.23</version>
    <scope>compile</scope>
</dependency>

<!-- 生产环境:按需引入 -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-core</artifactId>
    <version>5.8.23</version>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-crypto</artifactId>
    <version>5.8.23</version>
</dependency>

11.3 版本升级注意事项

// 5.8.0+ 版本推荐使用新的 API
// 旧的 API
DateUtil.format(now, DatePattern.NORM_DATETIME_PATTERN);

// 新的 API(更简洁)
DateUtil.formatDateTime(now);

十二、总结

12.1 本文核心内容回顾

  1. StrUtil:字符串判空、截取、格式化、脱敏
  2. DateUtil:日期格式化、计算、间隔、年龄星座
  3. CollUtil/BeanUtil:集合操作、Bean 拷贝
  4. HttpUtil:HTTP 请求、文件上传下载
  5. FileUtil:文件读写、目录操作
  6. SecureUtil:MD5、AES、RSA 加密
  7. JSONUtil:JSON 转换与处理
  8. RandomUtil/IdUtil:随机数、唯一 ID

Hutool 作为一款优秀的开源实战工具,其丰富的 API 和简洁的设计哲学,确实能极大提升我们的日常开发效率。希望本文的 20 个实战技巧能帮助你在项目中更优雅地使用它。

12.2 学习资源

  • Hutool 官方文档
  • Hutool GitHub 仓库

附录:常用 API 速查表

工具类 常用方法 功能描述
StrUtil isBlank/isNotBlank 字符串判空
StrUtil format 字符串格式化
StrUtil split/splitTrim 字符串切割
DateUtil formatDateTime 日期格式化
DateUtil offsetDay/offsetMonth 日期偏移
DateUtil betweenDay 计算日期间隔
CollUtil isEmpty/isNotEmpty 集合判空
CollUtil groupByKey 按字段分组
BeanUtil copyProperties Bean 属性拷贝
BeanUtil beanToMap/mapToBean Bean 与 Map 互转
HttpUtil get/post HTTP 请求
FileUtil readUtf8String 读取文件
FileUtil writeUtf8String 写入文件
SecureUtil md5/sha256 摘要算法
SecureUtil aes/rsa 对称/非对称加密
JSONUtil toJsonStr/toBean JSON 转换
IdUtil simpleUUID/getSnowflakeNextId 唯一 ID



上一篇:深入解析RDMA GID:工作原理与NCCL实战配置指南
下一篇:OpenClaw跑Skill总罢工?避开agentskills.io三大心智陷阱,掌握高阶编写心法
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-17 00:29 , Processed in 0.668889 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表