Apache Commons Lang 3 是非常流行的库,包括很多功能齐全的工具类,可以扩展Java的能力。其中内容相当丰富,包括字符串、数组以及数值操作,反射和并发,以及几个顺序数据结构实现(pair和triple)。本文带你学习这些工具类的应用。
引入依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>
引入maven依赖,读者可以选择合适的版本。
StringUtils 类
第一个工具类是StringUtils,如其名称所表示的,用于处理一组null安全检查的字符串处理操作,其补充并继承了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();
}
上面单元测试比较直接,几乎不用解释。为了简化,这里仅列举了部分方法,还有很多其他方法读者可自己探索。
ArrayUtils 类
ArrayUtils类实现了一组处理或检查数组的实用方法。
下面展示数组的toString方法,返回给定数组的字符串表示,对于空数组可返回特定字符:
@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");
}
接着是hasCode() 和 toMap() 方法,前者生成自定义hash code,后者转换数组为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 也实现了很多其他方法,读者可进一步查阅官方文档。
NumberUtils 类
另一个重要工具是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类型的实现,与上面示例非常类似。下面看createNumber() 和 isDigit() 方法。第一个方法使用字符串创建数值,第二个检查字符串是否有数字组成:
@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);
}
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类以一种简单方式提供了有价值的支持。
SystemUtils 类
有时需要从不同属性文件、java平台或操作的变量获取动态信息。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();
}
还有其他方法这里不再赘述。
懒初始化和构建类
Apache Commons Lang 3最吸引人的方面是实现了一些设计模式,包括懒初始化和构建模式。
下面示例假设创建昂贵的User类,因此想推迟到实际使用才初始化。
我们需要做的是扩展参数化LazyInitializer抽象类,并重写initialize方法:
public class UserInitializer extends LazyInitializer<User> {
@Override
protected User initialize() {
return new User("John", "john@domain.com");
}
}
下面测试需要使用User类对象,通过get方法:
@Test
public void whenCalledget_thenCorrect()
throws ConcurrentException {
UserInitializer userInitializer = new UserInitializer();
assertThat(userInitializer.get()).isInstanceOf(User.class);
}
get方法实现了实例属性的双检查方式:
private volatile User instance;
User get() {
if (instance == null) {
synchronized(this) {
if (instance == null)
instance = new User("John", "john@domain.com");
}
}
}
return instance;
}
另外Apache Commons Lang 3实现HashCodeBuilder类,用于使用不同参数构建生成hashCode:
@Test
public void whenCalledtoHashCode_thenCorrect() {
int hashcode = new HashCodeBuilder(17, 37)
.append("John")
.append("john@domain.com")
.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);
}
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);
}
FieldUtils 类
FieldUtils 类的方法使用反射方式读写类属性。
如果需要获取User类的属性,甚至从父类继承来的属性,可以使用getField方法:
@Test
public void whenCalledgetField_thenCorrect() {
assertThat(FieldUtils.getField(User.class, "name", true).getName())
.isEqualTo("name");
}
如果需要访问更严格的反射范围,仅需要获取属性声明(不是继承属性),然后使用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");
}
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", "john@domain.com"), "getName"))
.isEqualTo("John");
}
@Test
public void whenCalledinvokeMethod_thenCorrect()
throws Exception {
User user = new User("John", "john@domain.com");
Object method = MethodUtils.invokeMethod(user, true, "setName", "John");
assertThat(user.getName()).isEqualTo("John");
}
MutableObject 类
尽管不可变性是面向对象的关键特性,在大多情况下我们都应该默认这种特性,但不有时需要处理可变对象。创建可变对象需要大量模板代码,一般可以通过IDE自动生成。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类的示例。
MutablePair 类
有趣的是,Apache Commons Lang 3提供对tuple的强大支持,包括pair和triple形式。下面创建有序元素的集合:
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");
}
值得提醒的是该类提供了简单set和get方法,用于访问左右值对象。
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");
}
有时需要不可变类,任何尝试修改其内部状态都会抛出UnsupportedOperationException 异常。
Triple 类
最后一个实用类是Triple,它是抽象类,可以通过of方法进行实例化:
@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 类。
我们可以通过带参数的构造方法进行实例化,而不是静态工厂类。方法类似,这里不再演示。
总结
本文学习了Apache Commons Lang 3 提供的最常用工具类。它提供很多有用的类和方法,值得我们进一步探索。
本文参考链接:https://blog.csdn.net/neweastsun/article/details/115312960