JavaRush /Java 博客 /Random-ZH /让我们分解一下 StringUtils 类
Roman Beekeeper
第 35 级

让我们分解一下 StringUtils 类

已在 Random-ZH 群组中发布
大家好,我亲爱的读者。我尝试写下我真正感兴趣的事情以及我目前担心的事情。因此,今天将有一些轻读,对您以后作为参考很有用:我们来谈谈StringUtils让我们分解一下 StringUtils 类 - 1恰巧我有一次绕过了Apache Commons Lang 3库。这是一个带有辅助类的库,用于处理不同的对象。这是用于处理字符串、集合等的有用方法的集合。在当前的一个项目中,我必须更详细地处理字符串,将 25 年前的业务逻辑(从 COBOL 转换为 Java),结果证明我对StringUtils 类没有足够深入的了解。所以我必须自己创造一切。我的意思是说?事实上,您不必自己编写涉及字符串操作的某些任务,而是使用现成的解决方案。自己写有什么问题吗?至少这是很久以前就已经编写的更多代码。同样紧迫的是测试额外编写的代码的问题。当我们使用一个已经证明自己很好的库时,我们期望它已经经过测试,并且我们不需要编写一堆测试用例来测试它。碰巧的是,Java 中处理字符串的方法集并不是那么大。对工作有用的方法确实不多。创建此类也是为了提供对 NullPointerException 的检查。我们文章的大纲如下:
  1. 如何连接?
  2. 我的工作示例:在不了解如此有用的课程的情况下,我如何创建了我的自行车拐杖。
  3. 让我们看看我觉得有趣的其他方法。
  4. 我们来总结一下。
所有案例都将添加到GitHub 上 Javarush 社区组织的单独存储库中。他们将会有单独的例子和测试。

0. 如何连接

与我携手同行的人都或多或少地熟悉了 Git 和 Maven,因此我将进一步依赖这些知识,不再重复。对于那些错过了我之前的文章或刚刚开始阅读的人,这里有关于MavenGit 的材料。当然,如果没有构建系统(Maven、Gredl),您也可以手动连接所有内容,但现在这很疯狂,您绝对不需要这样做:最好立即学习如何正确地完成所有操作。因此,要使用 Maven,我们首先添加适当的依赖项:
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-lang3</artifactId>
   <version>${apache.common.version}</version>
</dependency>
其中${apache.common.version}是该库的版本。接下来,要导入某个类,请添加导入:
import org.apache.commons.lang3.StringUtils;
就这样,一切都在袋子里))

1. 实际项目示例

  • 左垫法

第一个例子现在看起来很愚蠢,我的同事知道StringUtils.leftPad并告诉我,这很好。任务是什么:代码的构建方式是,如果数据未完全正确到达,则必须转换数据。预计字符串字段应仅包含数字,即 如果它的长度是3并且它的值是1,则该条目应该是“001”。也就是说,首先需要删除所有空格,然后用零覆盖它。更多示例可以让任务的本质变得清晰:从“12” -> “012” 从“1” -> “001” 等等。我做了什么?在LeftPadExample类中对此进行了描述。我写了一个方法来完成这一切:
public static String ownLeftPad(String value) {
   String trimmedValue = value.trim();

   if(trimmedValue.length() == value.length()) {
       return value;
   }

   StringBuilder newValue = new StringBuilder(trimmedValue);

   IntStream.rangeClosed(1, value.length() - trimmedValue.length())
           .forEach(it -> newValue.insert(0, "0"));
   return newValue.toString();
}
作为基础,我的想法是,我们可以简单地获得原始值和修剪值之间的差异,并在前面用零填充。为此,我使用IntStream执行相同的操作 n 次。这绝对需要测试。如果我事先知道StringUtils.leftPad方法,我可以做以下事情:
public static String apacheCommonLeftPad(String value) {
   return StringUtils.leftPad(value.trim(), value.length(), "0");
}
可以看到,代码少了很多,而且还使用了大家都确认的库。为此,我在LeftPadExampleTest类中创建了两个测试(通常当他们计划测试一个类时,他们会在同一个包中创建一个同名的类 + Test,仅在 src/test/java 中)。这些测试检查一种方法以确保它正确转换值,然后检查另一种方法。当然,需要编写更多的测试,但测试不是我们案例的主要主题:
package com.github.javarushcommunity.stringutilsdemo;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

@DisplayName("Unit-level testing for LeftPadExample")
class LeftPadExampleTest {

   @DisplayName("Should transform by using ownLeftPad method as expected")
   @Test
   public void shouldTransformOwnLeftPadAsExpected() {
       //given
       String value = "1   ";
       String expectedTransformedValue = "0001";

       //when
       String transformedValue = LeftPadExample.ownLeftPad(value);

       //then
       Assertions.assertEquals(expectedTransformedValue, transformedValue);
   }

   @DisplayName("Should transform by using StringUtils method as expected")
   @Test
   public void shouldTransformStringUtilsLeftPadAsExpected() {
       //given
       String value = "1   ";
       String expectedTransformedValue = "0001";

       //when
       String transformedValue = LeftPadExample.apacheCommonLeftPad(value);

       //then
       Assertions.assertEquals(expectedTransformedValue, transformedValue);
   }

}
我现在可以对测试发表一些评论。它们是使用 JUnit 5 编写的:
  1. 如果测试具有适当的注释 - @Test,则该测试将被视为测试。
  2. 如果名称难以描述测试的操作或者描述较长不方便阅读,可以添加@DisplayName注解,使其成为运行测试时可见的普通描述。
  3. 在编写测试时,我使用 BDD 方法,将测试分为逻辑部分:
    1. //给定-测试前的数据设置块;
    2. //我们正在测试的代码部分何时启动;
    3. //then是一个块,其中检查when块的结果。
如果您运行它们,它们将确认一切都按预期运行。

  • stripStart方法

在这里,我需要解决开头可能有空格和逗号的行的问题。改造后,它们不应该有新的含义。问题陈述比以往更加清晰。几个例子将加深我们的理解: “, , books” -> “books” “,,, books” -> “books” b , books” -> “b , books” 与 leftPad 的情况一样,我添加了StrimStartExample类,其中有两个方法。一 - 有自己的解决方案:
public static String ownStripStart(String value) {
   int index = 0;
   List commaSpace = asList(" ", ",");
   for (int i = 0; i < value.length(); i++) {
       if (commaSpace.contains(String.valueOf(value.charAt(i)))) {
           index++;
       } else {
           break;
       }
   }
   return value.substring(index);
}
这里的想法是找到从没有更多空格或逗号开始的索引。如果它们一开始就不存在,那么索引将为零。第二个 - 通过StringUtils解决方案:
public static String apacheCommonLeftPad(String value) {
   return StringUtils.stripStart(value, StringUtils.SPACE + COMMA);
}
在这里,我们传递第一个参数有关我们正在使用哪个字符串的信息,在第二个参数中,我们传递一个由需要跳过的字符组成的字符串。我们以相同的方式创建StripStartExampleTest类:
package com.github.javarushcommunity.stringutilsdemo;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@DisplayName("Unit-level testing for StripStartExample")
class StripStartExampleTest {

   @DisplayName("Should transform by using stripStart method as expected")
   @Test
   public void shouldTransformOwnStripStartAsExpected() {
       //given
       String value = ", , books";
       String expectedTransformedValue = "books";

       //when
       String transformedValue = StripStartExample.ownStripStart(value);

       //then
       Assertions.assertEquals(expectedTransformedValue, transformedValue);
   }

   @DisplayName("Should transform by using StringUtils method as expected")
   @Test
   public void shouldTransformStringUtilsStripStartAsExpected() {
       //given
       String value = ", , books";
       String expectedTransformedValue = "books";

       //when
       String transformedValue = StripStartExample.apacheCommonLeftPad(value);

       //then
       Assertions.assertEquals(expectedTransformedValue, transformedValue);
   }
}

  • isEmpty方法

当然,这种方法要简单得多,但这并没有降低它的用处。它扩展了String.isEmpty()方法的功能,该方法还添加了对 null 的检查。为了什么?避免 NullPointerException,即避免对null变量调用方法。因此,为了不写:
if(value != null && value.isEmpty()) {
   //doing something
}
你可以简单地这样做:
if(StringUtils.isEmpty(value)) {
   //doing something
}
这种方法的优点是立即清楚哪里使用了哪种方法。

2、StringUtils类的其他方法分析

现在我们来谈谈那些我认为也值得关注的方法。一般来说,关于StringUtils,值得一提的是,它提供了与String类中的方法类似的 null 安全方法(就像isEmpty方法的情况一样)。让我们来看看它们:

  • 比较法

String中存在这样的方法,如果在比较两个字符串时其中一个为 null,则会抛出 NullPointerException。为了避免代码中出现丑陋的检查,我们可以使用StringUtils.compare(String str1, String str2)方法:它返回一个 int 作为比较结果。这些值是什么意思?如果它们相同(或均为 null),则 int = 0。如果 str1 小于 str2,则 int < 0。如果 str1 大于 str2,则 int > 0。另外,如果您查看他们的文档,该方法的 Javadoc 会呈现以下场景:
StringUtils.compare(null, null)   = 0
StringUtils.compare(null , "a")   < 0
StringUtils.compare("a", null)    > 0
StringUtils.compare("abc", "abc") = 0
StringUtils.compare("a", "b")     < 0
StringUtils.compare("b", "a")     > 0
StringUtils.compare("a", "B")     > 0
StringUtils.compare("ab", "abc")  < 0

  • 包含...方法

公用事业开发商在这里玩得很开心。你想要什么方法都有。我决定将它们放在一起:
  1. contains是一种检查预期字符串是否在另一个字符串内部的方法。这有什么用?如果需要确定文本中是否存在某个单词,可以使用此方法。

    例子:

    StringUtils.contains(null, *)     = false
    StringUtils.contains(*, null)     = false
    StringUtils.contains("", "")      = true
    StringUtils.contains("abc", "")   = true
    StringUtils.contains("abc", "a")  = true
    StringUtils.contains("abc", "z")  = false

    再次强调,NPE(空指针异常)安全性是存在的。

  2. containsAny是一种检查字符串中是否存在任何字符的方法。还有一件有用的事情:你经常必须这样做。

    文档中的示例:

    StringUtils.containsAny(null, *)                  = false
    StringUtils.containsAny("", *)                    = false
    StringUtils.containsAny(*, null)                  = false
    StringUtils.containsAny(*, [])                    = false
    StringUtils.containsAny("zzabyycdxx", ['z', 'a']) = true
    StringUtils.containsAny("zzabyycdxx", ['b', 'y']) = true
    StringUtils.containsAny("zzabyycdxx", ['z', 'y']) = true
    StringUtils.containsAny("aba", ['z'])             = false

  3. containsIgnoreCase是contains方法的有用扩展。事实上,要在没有这种方法的情况下检查这种情况,您将必须经历几个选项。所以只有一种方法会被和谐地使用。

  4. 文档中的一些示例:

    StringUtils.containsIgnoreCase(null, *) = false
    StringUtils.containsIgnoreCase(*, null) = false
    StringUtils.containsIgnoreCase("", "") = true
    StringUtils.containsIgnoreCase("abc", "") = true
    StringUtils.containsIgnoreCase("abc", "a") = true
    StringUtils.containsIgnoreCase("abc", "z") = false
    StringUtils.containsIgnoreCase("abc", "A") = true
    StringUtils.containsIgnoreCase("abc", "Z") = false

  5. containsNone - 从名称来看,您已经可以了解正在检查的内容。里面不应该有任何线条。绝对是有用的东西。快速搜索一些不需要的字符;)。在我们的电报机器人中,我们将过滤淫秽内容,并且不会忽略这些有趣的方法。

    举个例子,如果没有它们我们会怎样:

    StringUtils.containsNone(null, *)       = true
    StringUtils.containsNone(*, null)       = true
    StringUtils.containsNone("", *)         = true
    StringUtils.containsNone("ab", '')      = true
    StringUtils.containsNone("abab", 'xyz') = true
    StringUtils.containsNone("ab1", 'xyz')  = true
    StringUtils.containsNone("abz", 'xyz')  = false

  • 默认字符串方法

一系列方法有助于避免在字符串为空并且需要设置一些默认值时添加额外信息。有多种选择可满足各种口味。其中最主要的是StringUtils.defaultString(final String str, Final String defaultStr) - 如果 str 为 null,我们将简单地将值传递给defaultStr。文档中的示例:
StringUtils.defaultString(null, "NULL")  = "NULL"
StringUtils.defaultString("", "NULL")    = ""
StringUtils.defaultString("bat", "NULL") = "bat"
当你创建带有数据的POJO类时使用起来非常方便。

  • 删除空白方法

这是一个有趣的方法,尽管其应用选项并不多。同时,如果出现这样的情况,这个方法肯定会非常有用。它删除字符串中的所有空格。无论这个间隙在哪里,都不会留下任何痕迹)))文档中的示例:
StringUtils.deleteWhitespace(null)         = null
StringUtils.deleteWhitespace("")           = ""
StringUtils.deleteWhitespace("abc")        = "abc"
StringUtils.deleteWhitespace("   ab  c  ") = "abc"

  • 结束于方法

不言而喻。这是一个非常有用的方法:它检查字符串是否以建议的字符串结尾。这通常是必要的。当然,你可以自己写支票,但使用现成的方法显然更方便、更好。例子:
StringUtils.endsWith(null, null)      = true
StringUtils.endsWith(null, "def")     = false
StringUtils.endsWith("abcdef", null)  = false
StringUtils.endsWith("abcdef", "def") = true
StringUtils.endsWith("ABCDEF", "def") = false
StringUtils.endsWith("ABCDEF", "cde") = false
StringUtils.endsWith("ABCDEF", "")    = true
正如你所看到的,一切都以空行结束)))我认为这个例子(StringUtils.endsWith(“ABCDEF”,“”)= true)只是一个奖励,因为这是荒谬的)还有一种方法忽略大小写。

  • 等于方法

比较两个字符串的空安全方法的一个很好的例子。无论我们输入什么,答案都会在那里,而且不会有错误。例子:
StringUtils.equals(null, null)   = true
StringUtils.equals(null, "abc")  = false
StringUtils.equals("abc", null)  = false
StringUtils.equals("abc", "abc") = true
StringUtils.equals("abc", "ABC") = false
当然,还有equalsIgnoreCase - 一切都以完全相同的方式完成,只是我们忽略大小写。让我们来看看?
StringUtils.equalsIgnoreCase(null, null)   = true
StringUtils.equalsIgnoreCase(null, "abc")  = false
StringUtils.equalsIgnoreCase("abc", null)  = false
StringUtils.equalsIgnoreCase("abc", "abc") = true
StringUtils.equalsIgnoreCase("abc", "ABC") = true

  • 等于任何方法

让我们继续扩展equals方法。假设我们不想执行多次相等性检查,而是执行一次。为此,我们可以传递一个字符串,将与一组字符串进行比较;如果其中任何一个与建议的字符串相同,则为 TRUE。我们传递一个字符串和一个字符串集合来将它们相互比较(第一个字符串与集合中的字符串)。难的?以下是文档中的示例,可帮助您理解其含义:
StringUtils.equalsAny(null, (CharSequence[]) null) = false
StringUtils.equalsAny(null, null, null)    = true
StringUtils.equalsAny(null, "abc", "def")  = false
StringUtils.equalsAny("abc", null, "def")  = false
StringUtils.equalsAny("abc", "abc", "def") = true
StringUtils.equalsAny("abc", "ABC", "DEF") = false
还有equalsAnyIgnoreCase。以及它的例子:
StringUtils.equalsAnyIgnoreCase(null, (CharSequence[]) null) = false
StringUtils.equalsAnyIgnoreCase(null, null, null)    = true
StringUtils.equalsAnyIgnoreCase(null, "abc", "def")  = false
StringUtils.equalsAnyIgnoreCase("abc", null, "def")  = false
StringUtils.equalsAnyIgnoreCase("abc", "abc", "def") = true
StringUtils.equalsAnyIgnoreCase("abc", "ABC", "DEF") = true

底线

因此,我们了解了StringUtils是什么以及它有哪些有用的方法。好吧,意识到有如此有用的东西,并且无需每次都用拐杖在可以借助现成解决方案解决问题的地方进行围栏。总的来说,我们只分析了部分方法。如果你愿意,我可以继续:这样的人还有很多,而且确实值得关注。如果您对如何呈现此内容有任何想法,请写信 - 我总是乐于接受新想法。这些方法的文档写得很好,添加了带有结果的测试示例,这有助于更好地理解该方法的操作。因此,我们不会回避阅读文档:它将消除您对该实用程序功能的疑虑。为了获得新的编码经验,我建议您了解实用程序类是如何制作和编写的。这在将来会很有用,因为通常每个项目都有自己的废料类,编写它们的经验会派上用场。传统上,我建议您在 Github 上订阅我的帐户)对于那些不了解我的电报机器人项目的人,这里是第一篇文章的链接。感谢大家的阅读。我在下面添加了一些有用的链接。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION