Hutool 3.9 Upd
They decompiled the Hutool 3.9 JAR. Inside StrUtil.class, they found the culprit:
public static String blankToDefault(CharSequence str, String defaultStr)
if (blank(str)) // <-- This method had changed
return defaultStr;
return str.toString();
The blank() method in 3.9 had been "optimized" to handle certain Unicode spaces more aggressively. But in doing so, it now called toString() on a CharSequence before checking if the input was null. If the input was null, it exploded.
The old version had been graceful. The new version was technically correct but practically disastrous for legacy code that relied on null-safety at the deepest level.
"What do we do?" Lina whispered. "Downgrade?"
Old Kai shook his head. "Half the city already upgraded. Two dozen services depend on 3.9's new features. We can't roll back. We have to patch forward." Hutool 3.9 UPD
The FileUtil and WatchUtil were practically rewritten. In 3.9 UPD, you can now watch a directory for changes without endless loops.
WatchUtil.createModify(myPath, (event) ->
System.out.println("File modified: " + event.context());
).start();
This single feature replaced hundreds of lines of JDK WatchService boilerplate.
Twenty-seven minutes later, Looly released Hutool 3.9.1. The changelog contained exactly one line:
Fix null pointer exception in StrUtil.blankToDefault when input is null (issue #I23K7F). They decompiled the Hutool 3
The city updated. The Java agent was removed. And a new rule was written in the Developer's Codex:
Every utility method that accepts a CharSequence must explicitly guard against null, even if the spec says 'never null'. Because reality is never the spec.
import cn.hutool.core.util.*; import cn.hutool.crypto.SecureUtil; import cn.hutool.http.HttpUtil;public class Hutool39Showcase public static void main(String[] args) // 1. Crypto - SM4 encryption String plain = "Hutool 3.9 UPD rocks!"; byte[] key = SecureUtil.generateKey(SecureUtil.SM4).getEncoded(); String encrypted = SecureUtil.sm4(key).encryptBase64(plain);
// 2. Network - Fetch with retry String result = HttpUtil.createGet("https://api.ipify.org") .timeout(5000) .execute() .body(); // 3. Image - QR code generation QrCodeUtil.generate("https://hutool.cn", 300, 300, FileUtil.file("qr.png")); // 4. Reflection - Deep copy with ignore User user = new User("John"); User copy = BeanUtil.copyProperties(user, User.class, "id"); // 5. Number - Radix conversion String binary = NumberUtil.toBinaryString(255); // "11111111" String hex = NumberUtil.toHexString(255); // "ff"
Maven:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>3.9.0</version>
</dependency>
Gradle:
implementation 'cn.hutool:hutool-all:3.9.0'
Regardless of the version, Hutool is designed to simplify standard Java operations. Key modules include: The blank() method in 3