Apache Commons Lang 3 教程

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 提供的最常用工具类。它提供很多有用的类和方法,值得我们进一步探索。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值