Skip to main content
 首页 » 编程设计

Apache Commons Lang 3 教程

2022年07月19日124daizhj

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
阅读延展