核心亮点:本文将深入剖析 Hutool 5.x 的核心工具类,通过 20 个高频实战场景,展示如何用一行代码替代百行原生 Java 代码。涵盖字符串处理、日期计算、加密解密、HTTP 请求、文件操作等开发必备技能,让你的 Java 代码更简洁、更优雅。
在日常 Java 开发中,你是否经常需要编写大量重复、繁琐的工具代码?处理日期、校验字符串、发送 HTTP 请求……这些看似简单的任务,用原生 JDK 写起来却异常冗长。
1.1 原生 Java 开发的常见痛点
| 痛点 |
真实场景 |
代码复杂度 |
| 日期计算繁琐 |
计算两个日期相差天数、获取本月第一天 |
10+ 行代码,涉及 Calendar、SimpleDateFormat |
| 字符串处理重复 |
判空、去空格、截取、替换 |
需要 Apache Commons Lang 或手写工具类 |
| HTTP 请求冗长 |
发起一个 POST 请求 |
需要 HttpClient/RestTemplate,配置复杂 |
| 文件操作麻烦 |
读取文件、复制文件、计算 MD5 |
需要处理流、关闭资源、异常处理 |
| JSON 转换繁琐 |
对象转 JSON、JSON 转对象 |
需要引入 Jackson/Gson,配置繁多 |
Hutool(谐音“糊涂”,寓意“难得糊涂”)是一个小而全的 Java 工具类库,旨在减少代码搜索成本,提高开发效率。
Hutool = Hu(糊涂)+ Tool(工具)
核心优势:
- 零依赖:核心模块不依赖任何第三方库(JDK 8+)
- 功能全面:覆盖 Java 开发 90% 的常用工具需求
- 中文文档:全中文 API 文档,学习成本低
- 持续更新:社区活跃,Bug 修复及时
- 企业级应用:已在阿里巴巴、腾讯、字节等大厂广泛使用
作为一个功能强大的Java工具库,它解决了我们日常开发中的诸多痛点。
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();
}
}
十一、性能优化与最佳实践
| 场景 |
推荐做法 |
避免做法 |
| 字符串处理 |
使用 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 本文核心内容回顾
- StrUtil:字符串判空、截取、格式化、脱敏
- DateUtil:日期格式化、计算、间隔、年龄星座
- CollUtil/BeanUtil:集合操作、Bean 拷贝
- HttpUtil:HTTP 请求、文件上传下载
- FileUtil:文件读写、目录操作
- SecureUtil:MD5、AES、RSA 加密
- JSONUtil:JSON 转换与处理
- 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 |