Apache Commons 是 Apache 软件基金会提供的一系列 Java 工具库,它们是 Java 生态中最成熟、使用最广泛的基础设施之一。本文介绍几个最常用的 Commons 库。
概述
Apache Commons 项目起源于 Jakarta 项目,目的是提供可复用的 Java 组件。这些库填补了 JDK 标准库的空白,提供了大量实用工具方法。
核心价值:减少样板代码、处理边界情况、提供经过生产验证的实现。
Commons Lang3
最基础也最常用的库,扩展了 java.lang 包的功能。
Maven 依赖
1 2 3 4 5
| <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.20.0</version> </dependency>
|
字符串工具 - StringUtils
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import org.apache.commons.lang3.StringUtils;
String name = null; StringUtils.isEmpty(name); StringUtils.isBlank(name); StringUtils.defaultIfBlank(name, "default");
String text = " Hello World "; StringUtils.trim(text); StringUtils.substringBetween(text, "He", "o"); StringUtils.split("a,b,c", ",");
StringUtils.join(["a", "b", "c"], "-"); String[] array = {"Hello", null, "World"}; StringUtils.join(array, " ");
StringUtils.upperCase("hello"); StringUtils.capitalize("hello"); StringUtils.uncapitalize("Hello");
StringUtils.isNumeric("123"); StringUtils.isAlpha("abc"); StringUtils.startsWithIgnoreCase("Hello", "he");
|
对象工具 - ObjectUtils
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import org.apache.commons.lang3.ObjectUtils;
ObjectUtils.firstNonNull(null, null, "value", "other"); ObjectUtils.defaultIfNull(null, "default");
ObjectUtils.compare(1, 2); ObjectUtils.max(1, 2, 3); ObjectUtils.min(1, 2, 3);
ObjectUtils.notEqual(null, "value"); ObjectUtils.allNull(null, null); ObjectUtils.anyNull(null, "value");
|
数组工具 - ArrayUtils
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import org.apache.commons.lang3.ArrayUtils;
int[] nums = {1, 2, 3};
ArrayUtils.add(nums, 4); ArrayUtils.insert(nums, 1, 99);
ArrayUtils.remove(nums, 0); ArrayUtils.removeAll(nums, 0, 1);
ArrayUtils.contains(nums, 2); ArrayUtils.indexOf(nums, 2);
ArrayUtils.reverse(nums); ArrayUtils.subarray(nums, 0, 2);
ArrayUtils.isEmpty(nums); ArrayUtils.nullToEmpty((int[]) null);
|
日期工具 - DateUtils
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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); DateUtils.truncatedEquals(now, tomorrow, Calendar.DATE);
String formatted = DateFormatUtils.format(now, "yyyy-MM-dd HH:mm:ss"); FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM-dd");
|
随机字符串 - RandomStringUtils
1 2 3 4 5 6 7 8
| import org.apache.commons.lang3.RandomStringUtils;
RandomStringUtils.random(10); RandomStringUtils.randomAlphabetic(10); RandomStringUtils.randomNumeric(10); RandomStringUtils.randomAlphanumeric(10); RandomStringUtils.random(10, "abc123");
|
Commons IO
处理文件和 IO 操作的利器。
Maven 依赖
1 2 3 4 5
| <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.21.0</version> </dependency>
|
IOUtils - 流操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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 - 文件操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| 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 - 文件名处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import org.apache.commons.io.FilenameUtils;
String path = "/home/user/docs/readme.txt";
FilenameUtils.getExtension(path); FilenameUtils.getBaseName(path); FilenameUtils.getName(path); FilenameUtils.getPath(path); FilenameUtils.getFullPath(path);
FilenameUtils.normalize("/home/../user"); FilenameUtils.separatorsToUnix("C:\\Users");
FilenameUtils.equals(path1, path2);
|
IOFileFilter - 文件过滤
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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), new SuffixFileFilter(".log") );
Collection<File> files = FileUtils.listFiles(dir, filter, null);
|
Commons Collections4
增强 Java 集合框架。
Maven 依赖
1 2 3 4 5
| <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.5.0</version> </dependency>
|
CollectionUtils
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 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); CollectionUtils.isNotEmpty(list1); CollectionUtils.containsAny(list1, list2);
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);
|
MapUtils
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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);
MapUtils.safeAddToMap(map, "key", "value");
MapUtils.verbosePrint(System.out, "Map: ", map);
|
MultiValuedMap - 一键多值
1 2 3 4 5 6 7 8
| 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");
|
BidiMap - 双向映射
1 2 3 4 5 6 7 8 9 10 11 12
| 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); Integer value = bidiMap.get("one");
BidiMap<Integer, String> inverse = bidiMap.inverseBidiMap();
|
Commons Codec
编码解码工具。
Maven 依赖
1 2 3 4 5
| <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.21.0</version> </dependency>
|
Base64
1 2 3 4 5 6 7 8 9
| import org.apache.commons.codec.binary.Base64;
String encoded = Base64.encodeBase64String("Hello".getBytes());
byte[] decoded = Base64.decodeBase64(encoded);
String urlSafe = Base64.encodeBase64URLSafeString(bytes);
|
Hex
1 2 3 4 5
| import org.apache.commons.codec.binary.Hex;
String hex = Hex.encodeHexString(bytes); byte[] bytes = Hex.decodeHex(hex);
|
哈希算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import org.apache.commons.codec.digest.DigestUtils;
String data = "password";
String md5 = DigestUtils.md5Hex(data);
String sha256 = DigestUtils.sha256Hex(data); String sha384 = DigestUtils.sha384Hex(data); String sha512 = DigestUtils.sha512Hex(data);
String fileHash = DigestUtils.sha256Hex(new FileInputStream(file));
|
URL 编码
1 2 3 4 5
| import org.apache.commons.codec.net.URLCodec;
URLCodec codec = new URLCodec(); String encoded = codec.encode("Hello World"); String decoded = codec.decode(encoded);
|
Commons Text
文本处理增强工具。
Maven 依赖
1 2 3 4 5
| <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-text</artifactId> <version>1.15.0</version> </dependency>
|
字符串替换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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}!");
TextStringBuilder builder = new TextStringBuilder(); builder.append("Hello").append(" ").append("World"); builder.appendln("!"); String result = builder.toString();
|
字符串相似度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import org.apache.commons.text.similarity.LevenshteinDistance; import org.apache.commons.text.similarity.JaccardSimilarity; import org.apache.commons.text.similarity.FuzzyScore;
LevenshteinDistance levenshtein = new LevenshteinDistance(); int distance = levenshtein.apply("kitten", "sitting");
JaccardSimilarity jaccard = new JaccardSimilarity(); double similarity = jaccard.apply("hello", "hallo");
FuzzyScore fuzzy = new FuzzyScore(Locale.ENGLISH); int score = fuzzy.fuzzyScore("hello", "hallo");
|
WordUtils
1 2 3 4 5 6 7 8 9 10 11
| import org.apache.commons.text.WordUtils;
WordUtils.capitalize("hello world"); WordUtils.capitalizeFully("HELLO WORLD");
WordUtils.abbreviate("Hello World", 8);
String wrapped = WordUtils.wrap("Long text here", 10);
|
Commons CLI
命令行参数解析。
Maven 依赖
1 2 3 4 5
| <dependency> <groupId>commons-cli</groupId> <artifactId>commons-cli</artifactId> <version>1.11.0</version> </dependency>
|
使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| 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 依赖
1 2 3 4 5
| <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.13.1</version> </dependency>
|
对象池示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| 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+ 特性结合
1 2 3 4 5 6 7 8 9 10
| List<String> filtered = list.stream() .filter(StringUtils::isNotBlank) .map(String::trim) .collect(Collectors.toList());
Optional.ofNullable(value) .filter(StringUtils::isNotBlank) .orElseGet(() -> defaultValue);
|
3. 注意事项
- 线程安全:大多数工具类是线程安全的,但要注意实例方法
- 空值处理:Commons 工具方法通常对 null 友好,但阅读文档确认
- 性能考量:对于高频调用,考虑直接使用 JDK 方法或缓存结果
- 版本兼容:Lang3 与 Lang2 不兼容,使用
lang3 版本
总结
Apache Commons 系列库是 Java 开发的基础设施,它们:
- 减少样板代码:一行代码完成复杂操作
- 处理边界情况:null 安全、空集合处理
- 经过生产验证:数百万项目的依赖
- 持续维护:活跃的社区支持
建议:在项目中优先使用这些成熟工具,而非重复造轮子。但也要注意不要过度依赖,对于简单场景 JDK 原生方法可能更合适。
参考