Apache Commons 是 Apache 软件基金会提供的一系列 Java 工具库,它们是 Java 生态中最成熟、使用最广泛的基础设施之一。本文介绍几个最常用的 Commons 库。

概述

Apache Commons 项目起源于 Jakarta 项目,目的是提供可复用的 Java 组件。这些库填补了 JDK 标准库的空白,提供了大量实用工具方法。

核心价值:减少样板代码、处理边界情况、提供经过生产验证的实现。

Commons Lang3

最基础也最常用的库,扩展了 java.lang 包的功能。

Maven 依赖

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.17.0</version>
</dependency>

字符串工具 - StringUtils

import org.apache.commons.lang3.StringUtils;

// 空值处理 - 避免 NPE
String name = null;
StringUtils.isEmpty(name);        // true
StringUtils.isBlank(name);         // true (空白也视为空)
StringUtils.defaultIfBlank(name, "default");  // "default"

// 截取与分割
String text = "  Hello World  ";
StringUtils.trim(text);                    // "Hello World"
StringUtils.substringBetween(text, "He", "o");  // "ll"
StringUtils.split("a,b,c", ",");            // ["a", "b", "c"]

// 拼接
StringUtils.join(["a", "b", "c"], "-");     // "a-b-c"
String[] array = {"Hello", null, "World"};
StringUtils.join(array, " ");               // "Hello  World" (跳过 null)

// 大小写转换
StringUtils.upperCase("hello");             // "HELLO"
StringUtils.capitalize("hello");            // "Hello"
StringUtils.uncapitalize("Hello");          // "hello"

// 判断
StringUtils.isNumeric("123");               // true
StringUtils.isAlpha("abc");                 // true
StringUtils.startsWithIgnoreCase("Hello", "he");  // true

对象工具 - ObjectUtils

import org.apache.commons.lang3.ObjectUtils;

// 空值处理
ObjectUtils.firstNonNull(null, null, "value", "other");  // "value"
ObjectUtils.defaultIfNull(null, "default");               // "default"

// 比较
ObjectUtils.compare(1, 2);                    // -1
ObjectUtils.max(1, 2, 3);                     // 3
ObjectUtils.min(1, 2, 3);                     // 1

// 判等(null 安全)
ObjectUtils.notEqual(null, "value");          // true
ObjectUtils.allNull(null, null);              // true
ObjectUtils.anyNull(null, "value");           // true

数组工具 - ArrayUtils

import org.apache.commons.lang3.ArrayUtils;

int[] nums = {1, 2, 3};

// 添加元素(返回新数组)
ArrayUtils.add(nums, 4);              // [1, 2, 3, 4]
ArrayUtils.insert(nums, 1, 99);       // [1, 99, 2, 3]

// 删除元素
ArrayUtils.remove(nums, 0);           // [2, 3]
ArrayUtils.removeAll(nums, 0, 1);     // [3]

// 查找
ArrayUtils.contains(nums, 2);         // true
ArrayUtils.indexOf(nums, 2);          // 1

// 反转与子数组
ArrayUtils.reverse(nums);             // [3, 2, 1]
ArrayUtils.subarray(nums, 0, 2);      // [3, 2]

// 空值处理
ArrayUtils.isEmpty(nums);             // false
ArrayUtils.nullToEmpty((int[]) null); // []

日期工具 - DateUtils

import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.FastDateFormat;

Date now = new Date();

// 日期计算
Date tomorrow = DateUtils.addDays(now, 1);
Date nextMonth = DateUtils.addMonths(now, 1);
Date rounded = DateUtils.round(now, Calendar.HOUR);

// 日期比较
DateUtils.isSameDay(now, tomorrow);   // false
DateUtils.truncatedEquals(now, tomorrow, Calendar.DATE);  // false

// 格式化(线程安全)
String formatted = DateFormatUtils.format(now, "yyyy-MM-dd HH:mm:ss");
FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM-dd");

随机字符串 - RandomStringUtils

import org.apache.commons.lang3.RandomStringUtils;

// 随机生成
RandomStringUtils.random(10);              // 10位随机字符
RandomStringUtils.randomAlphabetic(10);    // 10位字母
RandomStringUtils.randomNumeric(10);        // 10位数字
RandomStringUtils.randomAlphanumeric(10);   // 字母+数字
RandomStringUtils.random(10, "abc123");     // 从指定字符集生成

Commons IO

处理文件和 IO 操作的利器。

Maven 依赖

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.18.0</version>
</dependency>

IOUtils - 流操作

import org.apache.commons.io.IOUtils;
import java.io.*;

// 流转字符串
String content = IOUtils.toString(inputStream, StandardCharsets.UTF_8);

// 流转字节数组
byte[] bytes = IOUtils.toByteArray(inputStream);

// 复制流
IOUtils.copy(inputStream, outputStream);
IOUtils.copyLarge(inputStream, outputStream);  // 大文件

// 关闭流(静默关闭,不抛异常)
IOUtils.closeQuietly(inputStream);

// 比较流内容
boolean same = IOUtils.contentEquals(stream1, stream2);

FileUtils - 文件操作

import org.apache.commons.io.FileUtils;
import java.io.File;

File file = new File("test.txt");
File dir = new File("output");

// 读取文件
String content = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
List<String> lines = FileUtils.readLines(file, StandardCharsets.UTF_8);
byte[] bytes = FileUtils.readFileToByteArray(file);

// 写入文件
FileUtils.writeStringToFile(file, "content", StandardCharsets.UTF_8);
FileUtils.writeLines(file, Arrays.asList("line1", "line2"));
FileUtils.writeByteArrayToFile(file, bytes);

// 复制文件
FileUtils.copyFile(srcFile, destFile);
FileUtils.copyDirectory(srcDir, destDir);
FileUtils.copyFileToDirectory(srcFile, destDir);

// 删除
FileUtils.deleteQuietly(file);           // 静默删除
FileUtils.forceDelete(dir);              // 强制删除
FileUtils.cleanDirectory(dir);           // 清空目录内容

// 创建
FileUtils.forceMkdir(dir);               // 创建目录
FileUtils.touch(file);                    // 创建空文件(如不存在)

// 文件信息
long size = FileUtils.sizeOf(file);
long dirSize = FileUtils.sizeOfDirectory(dir);
FileUtils.isFileNewer(file, referenceFile);
FileUtils.isFileOlder(file, referenceFile);

// 遍历文件
Collection<File> files = FileUtils.listFiles(dir, new String[]{"java"}, true);

FilenameUtils - 文件名处理

import org.apache.commons.io.FilenameUtils;

String path = "/home/user/docs/readme.txt";

// 获取各部分
FilenameUtils.getExtension(path);        // "txt"
FilenameUtils.getBaseName(path);         // "readme"
FilenameUtils.getName(path);             // "readme.txt"
FilenameUtils.getPath(path);             // "/home/user/docs/"
FilenameUtils.getFullPath(path);         // "/home/user/docs/"

// 路径规范化
FilenameUtils.normalize("/home/../user");  // "/user"
FilenameUtils.separatorsToUnix("C:\\Users"); // "C:/Users"

// 路径比较
FilenameUtils.equals(path1, path2);     // 标准化后比较

IOFileFilter - 文件过滤

import org.apache.commons.io.filefilter.*;

// 常用过滤器
FileFilter txtFilter = new SuffixFileFilter(".txt");
FileFilter dirFilter = DirectoryFileFilter.INSTANCE;
FileFilter fileFilter = FileFileFilter.INSTANCE;

// 组合过滤器
IOFileFilter filter = FileFilterUtils.and(
    FileFilterUtils.sizeFileFilter(1024 * 1024),  // 大于 1MB
    new SuffixFileFilter(".log")
);

// 使用过滤器遍历
Collection<File> files = FileUtils.listFiles(dir, filter, null);

Commons Collections4

增强 Java 集合框架。

Maven 依赖

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.5.0-M2</version>
</dependency>

CollectionUtils

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;

List<String> list1 = Arrays.asList("a", "b", "c");
List<String> list2 = Arrays.asList("b", "c", "d");

// 集合操作
Collection<String> union = CollectionUtils.union(list1, list2);       // 并集
Collection<String> intersection = CollectionUtils.intersection(list1, list2);  // 交集
Collection<String> subtract = CollectionUtils.subtract(list1, list2);  // 差集

// 判断
CollectionUtils.isEmpty(list1);              // false
CollectionUtils.isNotEmpty(list1);           // true
CollectionUtils.containsAny(list1, list2);   // true

// 过滤
CollectionUtils.filter(list, e -> e.length() > 1);  // 原地过滤
CollectionUtils.select(list, predicate);            // 返回新集合

// 转换
CollectionUtils.collect(list, transformer);

// 查找
String found = CollectionUtils.find(list, e -> e.startsWith("a"));

// 分区
List<List<String>> partitions = ListUtils.partition(list1, 2);  // 每组2个元素

MapUtils

import org.apache.commons.collections4.MapUtils;

Map<String, Object> map = new HashMap<>();

// 安全获取值
MapUtils.getString(map, "key", "default");
MapUtils.getInteger(map, "key", 0);
MapUtils.getBoolean(map, "key", false);

// 判断
MapUtils.isEmpty(map);
MapUtils.isNotEmpty(map);

// 安全 put
MapUtils.safeAddToMap(map, "key", "value");

// 调试输出
MapUtils.verbosePrint(System.out, "Map: ", map);

MultiValuedMap - 一键多值

import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;

MultiValuedMap<String, String> map = new ArrayListValuedHashMap<>();
map.put("key", "value1");
map.put("key", "value2");

Collection<String> values = map.get("key");  // [value1, value2]

BidiMap - 双向映射

import org.apache.commons.collections4.BidiMap;
import org.apache.commons.collections4.bidimap.TreeBidiMap;

BidiMap<String, Integer> bidiMap = new TreeBidiMap<>();
bidiMap.put("one", 1);
bidiMap.put("two", 2);

String key = bidiMap.getKey(1);      // "one"
Integer value = bidiMap.get("one");  // 1

// 反转
BidiMap<Integer, String> inverse = bidiMap.inverseBidiMap();

Commons Codec

编码解码工具。

Maven 依赖

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.17.1</version>
</dependency>

Base64

import org.apache.commons.codec.binary.Base64;

// 编码
String encoded = Base64.encodeBase64String("Hello".getBytes());
// 解码
byte[] decoded = Base64.decodeBase64(encoded);

// URL 安全的 Base64
String urlSafe = Base64.encodeBase64URLSafeString(bytes);

Hex

import org.apache.commons.codec.binary.Hex;

// 十六进制编码
String hex = Hex.encodeHexString(bytes);
byte[] bytes = Hex.decodeHex(hex);

哈希算法

import org.apache.commons.codec.digest.DigestUtils;

String data = "password";

// MD5(不推荐用于密码存储)
String md5 = DigestUtils.md5Hex(data);

// SHA 系列
String sha256 = DigestUtils.sha256Hex(data);
String sha384 = DigestUtils.sha384Hex(data);
String sha512 = DigestUtils.sha512Hex(data);

// 文件哈希
String fileHash = DigestUtils.sha256Hex(new FileInputStream(file));

URL 编码

import org.apache.commons.codec.net.URLCodec;

URLCodec codec = new URLCodec();
String encoded = codec.encode("Hello World");  // "Hello+World"
String decoded = codec.decode(encoded);

Commons Text

文本处理增强工具。

Maven 依赖

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-text</artifactId>
    <version>1.12.0</version>
</dependency>

字符串替换

import org.apache.commons.text.StringSubstitutor;
import org.apache.commons.text.StringTokenizer;
import org.apache.commons.text.TextStringBuilder;

// 变量替换
Map<String, String> values = new HashMap<>();
values.put("name", "World");
StringSubstitutor sub = new StringSubstitutor(values);
String result = sub.replace("Hello ${name}!");  // "Hello World!"

// 字符串构建器
TextStringBuilder builder = new TextStringBuilder();
builder.append("Hello").append(" ").append("World");
builder.appendln("!");
String result = builder.toString();

字符串相似度

import org.apache.commons.text.similarity.LevenshteinDistance;
import org.apache.commons.text.similarity.JaccardSimilarity;
import org.apache.commons.text.similarity.FuzzyScore;

// Levenshtein 距离(编辑距离)
LevenshteinDistance levenshtein = new LevenshteinDistance();
int distance = levenshtein.apply("kitten", "sitting");  // 3

// Jaccard 相似度
JaccardSimilarity jaccard = new JaccardSimilarity();
double similarity = jaccard.apply("hello", "hallo");  // 0.75

// 模糊匹配分数
FuzzyScore fuzzy = new FuzzyScore(Locale.ENGLISH);
int score = fuzzy.fuzzyScore("hello", "hallo");

WordUtils

import org.apache.commons.text.WordUtils;

// 首字母大写
WordUtils.capitalize("hello world");       // "Hello World"
WordUtils.capitalizeFully("HELLO WORLD");  // "Hello World"

// 缩写
WordUtils.abbreviate("Hello World", 8);    // "Hello..."

// 换行
String wrapped = WordUtils.wrap("Long text here", 10);

Commons CLI

命令行参数解析。

Maven 依赖

<dependency>
    <groupId>commons-cli</groupId>
    <artifactId>commons-cli</artifactId>
    <version>1.9.0</version>
</dependency>

使用示例

import org.apache.commons.cli.*;

// 定义选项
Options options = new Options();
options.addOption("h", "help", false, "显示帮助信息");
options.addOption("f", "file", true, "输入文件路径");
options.addOption("v", "verbose", false, "详细输出");
options.addOption(Option.builder("o")
    .longOpt("output")
    .hasArg()
    .argName("FILE")
    .desc("输出文件路径")
    .build());

// 解析参数
CommandLineParser parser = new DefaultParser();
try {
    CommandLine cmd = parser.parse(options, args);
    
    if (cmd.hasOption("h")) {
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp("myapp", options);
        return;
    }
    
    String inputFile = cmd.getOptionValue("f");
    String outputFile = cmd.getOptionValue("o", "output.txt");
    boolean verbose = cmd.hasOption("v");
    
} catch (ParseException e) {
    System.err.println("参数解析失败: " + e.getMessage());
}

Commons Pool

对象池实现。

Maven 依赖

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.12.1</version>
</dependency>

对象池示例

import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;

// 定义对象工厂
class ExpensiveObjectFactory extends BasePooledObjectFactory<ExpensiveObject> {
    
    @Override
    public ExpensiveObject create() {
        return new ExpensiveObject();  // 创建昂贵对象
    }
    
    @Override
    public PooledObject<ExpensiveObject> wrap(ExpensiveObject obj) {
        return new DefaultPooledObject<>(obj);
    }
    
    @Override
    public void destroyObject(PooledObject<ExpensiveObject> p) {
        p.getObject().cleanup();  // 清理资源
    }
}

// 使用对象池
GenericObjectPool<ExpensiveObject> pool = new GenericObjectPool<>(new ExpensiveObjectFactory());
pool.setMaxTotal(10);
pool.setMaxIdle(5);

try {
    ExpensiveObject obj = pool.borrowObject();
    try {
        obj.doSomething();
    } finally {
        pool.returnObject(obj);  // 归还对象
    }
} catch (Exception e) {
    // 处理异常
}

最佳实践

1. 选择正确的库

需求推荐库
字符串/对象工具Commons Lang3
文件/IO 操作Commons IO
集合增强Commons Collections4
编码解码Commons Codec
文本处理Commons Text
命令行解析Commons CLI
对象池Commons Pool2

2. 与 Java 8+ 特性结合

// 结合 Stream API
List<String> filtered = list.stream()
    .filter(StringUtils::isNotBlank)
    .map(String::trim)
    .collect(Collectors.toList());

// 结合 Optional
Optional.ofNullable(value)
    .filter(StringUtils::isNotBlank)
    .orElseGet(() -> defaultValue);

3. 注意事项

  • 线程安全:大多数工具类是线程安全的,但要注意实例方法
  • 空值处理:Commons 工具方法通常对 null 友好,但阅读文档确认
  • 性能考量:对于高频调用,考虑直接使用 JDK 方法或缓存结果
  • 版本兼容:Lang3 与 Lang2 不兼容,使用 lang3 版本

总结

Apache Commons 系列库是 Java 开发的基础设施,它们:

  1. 减少样板代码:一行代码完成复杂操作
  2. 处理边界情况:null 安全、空集合处理
  3. 经过生产验证:数百万项目的依赖
  4. 持续维护:活跃的社区支持

建议:在项目中优先使用这些成熟工具,而非重复造轮子。但也要注意不要过度依赖,对于简单场景 JDK 原生方法可能更合适。

参考