Apache Commons Lang 3 简介
上次更新:2024 年 1 月 8 日
1. 概述
Apache Commons Lang 3 库 是一个流行且功能齐全的实用类包,旨在扩展 Java API 的功能。
该库的功能非常丰富,涵盖字符串、数组和数字操作、反射和并发,以及对成对和三元组等多个有序数据结构的实现(通常称为 元组)。
在本教程中,我们将深入研究该库中最有用的实用类。
2. Maven 依赖
像往常一样,要开始使用 Apache Commons Lang 3,我们首先需要添加 Maven 依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
</dependency>
3. StringUtils 类
我们将在此介绍中介绍的第一个实用类是 StringUtils.
顾名思义,StringUtils 允许我们执行一系列空安全字符串操作,这些操作补充/扩展了java.lang.String开箱即用的操作。
让我们开始展示一组用于对给定字符串执行各种检查的实用方法,例如确定字符串是否为空白、为空、小写、大写、字母数字等
@Test
public void whenCalledisBlank_thenCorrect() {
assertThat(StringUtils.isBlank(" ")).isTrue();
}
@Test
public void whenCalledisEmpty_thenCorrect() {
assertThat(StringUtils.isEmpty("")).isTrue();
}
@Test
public void whenCalledisAllLowerCase_thenCorrect() {
assertThat(StringUtils.isAllLowerCase("abd")).isTrue();
}
@Test
public void whenCalledisAllUpperCase_thenCorrect() {
assertThat(StringUtils.isAllUpperCase("ABC")).isTrue();
}
@Test
public void whenCalledisMixedCase_thenCorrect() {
assertThat(StringUtils.isMixedCase("abC")).isTrue();
}
@Test
public void whenCalledisAlpha_thenCorrect() {
assertThat(StringUtils.isAlpha("abc")).isTrue();
}
@Test
public void whenCalledisAlphanumeric_thenCorrect() {
assertThat(StringUtils.isAlphanumeric("abc123")).isTrue();
}
当然,StringUtils 类实现了许多其他方法,为了简单起见,我们在此省略了它们。
对于检查或对给定字符串应用某种转换算法的其他一些附加方法,请 查看本教程。
上面介绍的方法非常简单明了,因此单元测试应该不言自明。
4. ArrayUtils 类
ArrayUtils 类实现了一批实用方法,允许我们以各种不同的方式处理和检查数组.
让我们从toString() 的两个重载实现开始,它返回给定数组的字符串表示形式,并在数组为 null 时返回一个特定的字符串
@Test
public void whenCalledtoString_thenCorrect() {
String[] array = {"a", "b", "c"};
assertThat(ArrayUtils.toString(array))
.isEqualTo("{a,b,c}");
}
@Test
public void whenCalledtoStringIfArrayisNull_thenCorrect() {
assertThat(ArrayUtils.toString(null, "Array is null"))
.isEqualTo("Array is null");
}
接下来,我们有hashCode() 和 toMap() 方法。
前者为数组生成自定义 hashCode 实现,后者将数组转换为Map
@Test
public void whenCalledhashCode_thenCorrect() {
String[] array = {"a", "b", "c"};
assertThat(ArrayUtils.hashCode(array))
.isEqualTo(997619);
}
@Test
public void whenCalledtoMap_thenCorrect() {
String[][] array = {{"1", "one", }, {"2", "two", }, {"3", "three"}};
Map map = new HashMap();
map.put("1", "one");
map.put("2", "two");
map.put("3", "three");
assertThat(ArrayUtils.toMap(array))
.isEqualTo(map);
}
最后,让我们看一下isSameLength() 和indexOf() 方法。
前者用于检查两个数组是否具有相同的长度,后者用于获取给定元素的索引
@Test
public void whenCalledisSameLength_thenCorrect() {
int[] array1 = {1, 2, 3};
int[] array2 = {1, 2, 3};
assertThat(ArrayUtils.isSameLength(array1, array2))
.isTrue();
}
@Test
public void whenCalledIndexOf_thenCorrect() {
int[] array = {1, 2, 3};
assertThat(ArrayUtils.indexOf(array, 1, 0))
.isEqualTo(0);
}
与StringUtils 类一样,ArrayUtils 实现更多其他方法。您可以在 本教程 中了解更多信息。
在这种情况下,我们仅展示了最具代表性的方法。
5. NumberUtils 类
Apache Commons Lang 3 的另一个关键组件是 NumberUtils 类。
如预期的那样,该类提供了大量的实用方法,旨在处理和操作数值类型。
让我们看一下compare() 的重载实现,它比较不同的原始类型(例如int 和long)的相等性
@Test
public void whenCalledcompareWithIntegers_thenCorrect() {
assertThat(NumberUtils.compare(1, 1))
.isEqualTo(0);
}
@Test
public void whenCalledcompareWithLongs_thenCorrect() {
assertThat(NumberUtils.compare(1L, 1L))
.isEqualTo(0);
}
此外,还存在对byte和short类型操作的compare()实现,其工作方式与上述示例非常相似。
接下来在本综述中是createNumber()和isDigit()方法。
第一个允许我们创建一个string的数字表示,而第二个检查一个string是否仅由数字组成
@Test
public void whenCalledcreateNumber_thenCorrect() {
assertThat(NumberUtils.createNumber("123456"))
.isEqualTo(123456);
}
@Test
public void whenCalledisDigits_thenCorrect() {
assertThat(NumberUtils.isDigits("123456")).isTrue();
}
在寻找给定数组的最小值和最大值时,NumberUtils类通过min()和max()方法的重载实现为此类操作提供了强大的支持
@Test
public void whenCalledmaxwithIntegerArray_thenCorrect() {
int[] array = {1, 2, 3, 4, 5, 6};
assertThat(NumberUtils.max(array))
.isEqualTo(6);
}
@Test
public void whenCalledminwithIntegerArray_thenCorrect() {
int[] array = {1, 2, 3, 4, 5, 6};
assertThat(NumberUtils.min(array)).isEqualTo(1);
}
@Test
public void whenCalledminwithByteArray_thenCorrect() {
byte[] array = {1, 2, 3, 4, 5, 6};
assertThat(NumberUtils.min(array))
.isEqualTo((byte) 1);
}
6. Fraction 类
使用笔和纸处理分数一切都很好。 但是,在编写代码时,我们是否需要经历这个过程的复杂性? 实际上不是。
Fraction 类使加、减和乘分数变得轻而易举:
@Test
public void whenCalledgetFraction_thenCorrect() {
assertThat(Fraction.getFraction(5, 6)).isInstanceOf(Fraction.class);
}
@Test
public void givenTwoFractionInstances_whenCalledadd_thenCorrect() {
Fraction fraction1 = Fraction.getFraction(1, 4);
Fraction fraction2 = Fraction.getFraction(3, 4);
assertThat(fraction1.add(fraction2).toString()).isEqualTo("1/1");
}
@Test
public void givenTwoFractionInstances_whenCalledsubstract_thenCorrect() {
Fraction fraction1 = Fraction.getFraction(3, 4);
Fraction fraction2 = Fraction.getFraction(1, 4);
assertThat(fraction1.subtract(fraction2).toString()).isEqualTo("1/2");
}
@Test
public void givenTwoFractionInstances_whenCalledmultiply_thenCorrect() {
Fraction fraction1 = Fraction.getFraction(3, 4);
Fraction fraction2 = Fraction.getFraction(1, 4);
assertThat(fraction1.multiplyBy(fraction2).toString()).isEqualTo("3/16");
}
虽然分数运算肯定不是我们在日常开发工作中需要处理的最常见任务,但Fraction 类为以直接的方式执行这些操作提供了有价值的支持。
7. SystemUtils 类
有时,我们需要获取有关底层 Java 平台或操作系统的不同属性和变量的一些动态信息。
Apache Commons Lang 3 提供了SystemUtils 类,可以以一种无痛的方式完成此操作.
例如,考虑getJavaHome()、getUserHome() 和 isJavaVersionAtLeast() 方法
@Test
public void whenCalledgetJavaHome_thenCorrect() {
assertThat(SystemUtils.getJavaHome())
.isEqualTo(new File("path/to/java/jdk"));
}
@Test
public void whenCalledgetUserHome_thenCorrect() {
assertThat(SystemUtils.getUserHome())
.isEqualTo(new File("path/to/user/home"));
}
@Test
public void whenCalledisJavaVersionAtLeast_thenCorrect() {
assertThat(SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_RECENT)).isTrue();
}
SystemUtils 类实现了一些其他的实用方法。 我们省略了它们以保持示例的简短。
8. 延迟初始化和 Builder 类
Apache Commons Lang 3 最吸引人的方面之一是实现了某些众所周知的设计模式,包括延迟初始化和建造者模式.
例如,假设我们创建了一个昂贵的User类(为简洁起见未显示),并希望将其实例化推迟到真正需要时。
在这种情况下,我们需要做的就是扩展参数化LazyInitializer抽象类并覆盖其initialize()方法
public class UserInitializer extends LazyInitializer<User> {
@Override
protected User initialize() {
return new User("John", "[email protected]");
}
}
现在,如果我们需要在需要时获取我们的高成本User对象,我们只需调用UserInitializer的get()方法
@Test
public void whenCalledget_thenCorrect()
throws ConcurrentException {
UserInitializer userInitializer = new UserInitializer();
assertThat(userInitializer.get()).isInstanceOf(User.class);
}
get() 方法是实例字段的双重检查习语(线程安全)的实现,如Joshua Bloch 的“Effective Java”第 71 条
private volatile User instance;
User get() {
if (instance == null) {
synchronized(this) {
if (instance == null)
instance = new User("John", "[email protected]");
}
}
}
return instance;
}
此外,Apache Commons Lang 3 实现了 HashCodeBuilder 类,它允许我们通过向 builder 提供不同的参数(基于典型的流利 API)来生成hashCode() 实现
@Test
public void whenCalledtoHashCode_thenCorrect() {
int hashcode = new HashCodeBuilder(17, 37)
.append("John")
.append("[email protected]")
.toHashCode();
assertThat(hashcode).isEqualTo(1269178828);
}
我们可以对 BasicThreadFactory 类进行类似的操作,并创建具有命名模式和优先级的守护线程
@Test
public void whenCalledBuilder_thenCorrect() {
BasicThreadFactory factory = new BasicThreadFactory.Builder()
.namingPattern("workerthread-%d")
.daemon(true)
.priority(Thread.MAX_PRIORITY)
.build();
assertThat(factory).isInstanceOf(BasicThreadFactory.class);
}
9. ConstructorUtils 类
反射在 Apache Commons Lang 3 中是一等公民。
该库包含几个反射类,允许我们以反射方式访问和操作类字段和方法。
例如,假设我们实现了一个简单的 User 领域类
public class User {
private String name;
private String email;
// standard constructors / getters / setters / toString
}
假设它的参数化构造函数是 public,我们可以使用 ConstructorUtils 类轻松访问它
@Test
public void whenCalledgetAccessibleConstructor_thenCorrect() {
assertThat(ConstructorUtils
.getAccessibleConstructor(User.class, String.class, String.class))
.isInstanceOf(Constructor.class);
}
除了通过构造函数进行标准的类实例化外,我们可以通过调用 invokeConstructor() 和 invokeExactConstructor() 方法以反射方式创建 User 实例
@Test
public void whenCalledinvokeConstructor_thenCorrect()
throws Exception {
assertThat(ConstructorUtils.invokeConstructor(User.class, "name", "email"))
.isInstanceOf(User.class);
}
@Test
public void whenCalledinvokeExactConstructor_thenCorrect()
throws Exception {
String[] args = {"name", "email"};
Class[] parameterTypes= {String.class, String.class};
assertThat(ConstructorUtils.invokeExactConstructor(User.class, args, parameterTypes))
.isInstanceOf(User.class);
}
10. FieldUtils 类
同样,我们可以使用 FieldUtils 类的这些方法以反射方式读取/写入类字段。
假设我们想要获取 User 类的某个字段,或者最终获取该类从超类继承的某个字段。
在这种情况下,我们可以调用 getField() 方法
@Test
public void whenCalledgetField_thenCorrect() {
assertThat(FieldUtils.getField(User.class, "name", true).getName())
.isEqualTo("name");
}
或者,如果我们想要使用更严格的反射范围,并且只获取 User 类中声明的字段,而不从超类继承,我们将只使用 getDeclaredField() 方法
@Test
public void whenCalledgetDeclaredFieldForceAccess_thenCorrect() {
assertThat(FieldUtils.getDeclaredField(User.class, "name", true).getName())
.isEqualTo("name");
}
此外,我们可以使用 getAllFields() 方法获取反射类的字段数量,并使用 writeField() 和 writeDeclaredField() 方法将值写入声明的字段或层次结构中定义的字段
@Test
public void whenCalledgetAllFields_thenCorrect() {
assertThat(FieldUtils.getAllFields(User.class).length)
.isEqualTo(2);
}
@Test
public void whenCalledwriteField_thenCorrect()
throws IllegalAccessException {
FieldUtils.writeField(user, "name", "Julie", true);
assertThat(FieldUtils.readField(user, "name", true))
.isEqualTo("Julie");
}
@Test
public void givenFieldUtilsClass_whenCalledwriteDeclaredField_thenCorrect() throws IllegalAccessException {
FieldUtils.writeDeclaredField(user, "name", "Julie", true);
assertThat(FieldUtils.readField(user, "name", true))
.isEqualTo("Julie");
}
11. MethodUtils 类
同样,我们可以使用 MethodUtils 类对类方法进行反射。
在这种情况下,User 类的 getName() 方法的可见性是 public。因此,我们可以使用 getAccessibleMethod() 方法访问它
@Test
public void whenCalledgetAccessibleMethod_thenCorrect() {
assertThat(MethodUtils.getAccessibleMethod(User.class, "getName"))
.isInstanceOf(Method.class);
}
在以反射方式调用方法时,我们可以使用 invokeExactMethod() 和 invokeMethod() 方法
@Test
public
void whenCalledinvokeExactMethod_thenCorrect()
throws Exception {
assertThat(MethodUtils.invokeExactMethod(new User("John", "[email protected]"), "getName"))
.isEqualTo("John");
}
@Test
public void whenCalledinvokeMethod_thenCorrect()
throws Exception {
User user = new User("John", "[email protected]");
Object method = MethodUtils.invokeMethod(user, true, "setName", "John");
assertThat(user.getName()).isEqualTo("John");
}
12. MutableObject 类
虽然不变性是良好面向对象软件的关键特性,我们应该在每种可能的情况下都将其作为默认设置,但不幸的是,有时我们需要处理可变对象。
此外,创建可变类需要大量的样板代码,这可以通过大多数 IDE 通过自动生成的 setter 生成。
为此,Apache Commons Lang 3 提供了 MutableObject 类,一个用于创建可变对象的简单包装类,无需繁琐的操作
@BeforeClass
public static void setUpMutableObject() {
mutableObject = new MutableObject("Initial value");
}
@Test
public void whenCalledgetValue_thenCorrect() {
assertThat(mutableObject.getValue()).isInstanceOf(String.class);
}
@Test
public void whenCalledsetValue_thenCorrect() {
mutableObject.setValue("Another value");
assertThat(mutableObject.getValue()).isEqualTo("Another value");
}
@Test
public void whenCalledtoString_thenCorrect() {
assertThat(mutableObject.toString()).isEqualTo("Another value");
}
当然,这只是如何使用 MutableObject 类的一个例子。
作为经验法则,我们应该始终努力创建不可变类,或者在最坏的情况下,只提供所需的变异级别。
13. MutablePair 类
有趣的是,Apache Commons Lang 3 以配对和三元组的形式为元组提供了强大的支持。
所以,假设我们需要创建一个可变的有序元素对。
在这种情况下,我们将使用 MutablePair 类
private static MutablePair<String, String> mutablePair;
@BeforeClass
public static void setUpMutablePairInstance() {
mutablePair = new MutablePair<>("leftElement", "rightElement");
}
@Test
public void whenCalledgetLeft_thenCorrect() {
assertThat(mutablePair.getLeft()).isEqualTo("leftElement");
}
@Test
public void whenCalledgetRight_thenCorrect() {
assertThat(mutablePair.getRight()).isEqualTo("rightElement");
}
@Test
public void whenCalledsetLeft_thenCorrect() {
mutablePair.setLeft("newLeftElement");
assertThat(mutablePair.getLeft()).isEqualTo("newLeftElement");
}
这里值得强调的最相关的细节是该类的简洁 API。
它允许我们通过标准的 setter/getter 设置和访问配对封装的左右对象。
14. ImmutablePair 类
毫不奇怪,MutablePair 类也有一个不可变对应的实现,称为 ImmutablePair
private static ImmutablePair<String, String> immutablePair = new ImmutablePair<>("leftElement", "rightElement");
@Test
public void whenCalledgetLeft_thenCorrect() {
assertThat(immutablePair.getLeft()).isEqualTo("leftElement");
}
@Test
public void whenCalledgetRight_thenCorrect() {
assertThat(immutablePair.getRight()).isEqualTo("rightElement");
}
@Test
public void whenCalledof_thenCorrect() {
assertThat(ImmutablePair.of("leftElement", "rightElement"))
.isInstanceOf(ImmutablePair.class);
}
@Test(expected = UnsupportedOperationException.class)
public void whenCalledSetValue_thenThrowUnsupportedOperationException() {
immutablePair.setValue("newValue");
}
正如我们可能期望的那样,任何尝试通过setValue()方法更改配对的内部状态的行为都将导致抛出UnsupportedOperationException异常。
15. Triple类
我们将要查看的最后一个工具类是Triple。
由于该类是抽象的,我们可以使用of()静态工厂方法来创建Triple实例
@BeforeClass
public static void setUpTripleInstance() {
triple = Triple.of("leftElement", "middleElement", "rightElement");
}
@Test
public void whenCalledgetLeft_thenCorrect() {
assertThat(triple.getLeft()).isEqualTo("leftElement");
}
@Test
public void whenCalledgetMiddle_thenCorrect() {
assertThat(triple.getMiddle()).isEqualTo("middleElement");
}
@Test
public void whenCalledgetRight_thenCorrect() {
assertThat(triple.getRight()).isEqualTo("rightElement");
}
通过MutableTriple和ImmutableTriple类,也存在可变和不可变的 triple 的具体实现。
我们可以通过参数化构造函数创建它们的实例,而不是使用静态工厂方法。
在这种情况下,我们将跳过它们,因为它们的 API 非常类似于MutablePair和ImmutablePair类的 API。
16. 结论
在本教程中,我们深入了解了Apache Commons Lang 3提供的一些最有用的现成的工具.
该库实现了许多其他值得一看的工具类。这里,我们只是根据一个相当主观的标准展示了最有用的工具类。
有关完整的库API,请查看官方Javadoc。
支持本文的代码可在 GitHub 上获取。 一旦你以 Baeldung Pro 会员 身份登录,就开始学习并在项目上进行编码。















