初衷
我们平常业务上需要对字符串进行很多处理,而jdk自带的处理又不能满足全部的需求,所以一般会用上StringUtils来处理。我们就剖析下这个轮子的方法,让我们可以高效的处理字符串。
简略查询版
只需要当文档看的可以直接在里面查。
| 方法 | 作用 |
|---|---|
| IsEmpty/IsBlank | 检查字符串是否包含文本 |
| Trim/Strip | 删除前导和尾随”空格“ |
| Equals/Compare | 比较两个字符串空安全 |
| startsWith | 检查字符串是否以空安全前缀开头 |
| EndsWith | 检查字符串是否以空安全后缀结尾 |
| IndexOfAny/LastIndexOfAny/IndexOfAnyBut/LastIndexOfAnyBut | 任何一组字符串的索引 |
| containsOnly/ContainsNone/ContainsAny | 字符串是否只包含/无/任何这些字符 |
| Substring/Left/Right/Mid | 空安全子串提取 |
| SubstringBefore/SubstringAfter/SubstringBetween | 相对于其他字符串的子字符串提取 |
| Split/Join | 将字符串拆分为子字符串数组,反之亦然 |
| Remove/Delete | 删除字符串的一部分 |
| Replace/Overlay | 搜索字符串并将一个字符串替换为另一个 |
| Chomp/Chop | 删除字符串的最后一部分 |
| AppendIfMissing | 如果不存在,则在字符串的末尾附加一个后缀 |
| PrependIfMissing | 如果不存在,则在字符串的开头添加前缀 |
| LeftPad/RightPad/Center/Repeat | 填充一个字符串 |
| UpperCase/LowerCase/SwapCase/Capitalize/Uncapitalize | 改变字符串的大小写 |
| CountMatches | 计算一个字符串在另一个字符串中出现的次数 |
| IsAlpha/IsNumeric/IsWhitespace/IsAsciiPrintable | 检查字符串中的字符 |
| DefaultString | 防止空输入字符串 |
| Rotate | 旋转一个字符串 |
| Reverse/ReverseDelimited | 反转字符串 |
| Abbreviate | 使用省略号或另一个给定的字符串来缩写字符串 |
| Difference | 比较字符串并报告它们的差异 |
| LevenshteinDistance | 一个字符串更改为另一个字符串所需的更改次数 |
| IndexOf/LastIndexOf/Contains | 空安全索引检查 |
前提
详解方法之前先声明下Java里对于一些名词的含义,也就是字符串状态的一些概念,这个是很重要且容易发生混淆的。
- null
- empty
- space
- whitespace
- trim
null
就是本质的null了,例如
String s = null;
System.out.println(s.length());
复制代码
对他们操作会直接报空指针异常
Exception in thread "main" java.lang.NullPointerException
复制代码
这中空指针是我们比较难预知的和容易触发的,也算是Java的一个痛点了。这也是我们使用StringUtils的一个很重要的原因,就是判断null;
empty
String s = "";
复制代码
很简单,就是创建了字符串对象,但是长度为0;
space
空格字符
char c = ' ';
复制代码
whitespace
重点来了!!!
什么是whitespace呢,jdk给出的定义就是满足于Character.isWhitespace()为true,即如下
It is '\t', U+0009 HORIZONTAL TABULATION.
It is '\n', U+000A LINE FEED.
It is '\u000B', U+000B VERTICAL TABULATION.
It is '\f', U+000C FORM FEED.
It is '\r', U+000D CARRIAGE RETURN.
It is '\u001C', U+001C FILE SEPARATOR.
It is '\u001D', U+001D GROUP SEPARATOR.
It is '\u001E', U+001E RECORD SEPARATOR.
It is '\u001F', U+001F UNIT SEPARATOR.
像空格、制表符、回车符等这种在页面里显示为空白的字符称为whitespace
trim
首先聊下jdk里的trim()方法。
很多人认为就是简单的把字符串前后的空格删掉了,其实他删掉的是unicode小于等于32的所有字符,包括whitespace这些小于32的字符。
unicode
所以我们需要谈下Unicode编码。
unicode编码是16进制,等于32也就是**\u0020**就是空格(space)。
小于32的被称为c0控制符,是不会显示出来的,例如whitespace里的各种换行符、回车符之类的。
小结
所以可以简单的认为whitespace是trim的子集,trim代表的字符会更多一些。
详细剖析
先说下StringUtils的意义
Operations on String that are null safe.
主要有两点,一是丰富处理字符串业务的轮子,二是防止出现String空指针的情况。所以StringUtils的每个方法都会对null字符串进行特殊处理或者返回出来,不会出现空指针异常。
虽然说处理字符串这方面相比于jdk原生是方便了许多,但是遇到复杂的判断、处理情况,还是要使用正则表达式。
isEmpty/isBlank
他们都是用来判断字符串是否为”空“,常见于代码里对字符串进行校验
public Api get(String s1){
if(StringUtils.isEmpty(s1)){
return Api.error();
}
return Api.success(s1);
}
复制代码
那他俩的区别是什么呢
isEmpty 是当null和empty(”“) 时才会为true,但是如果” “这种情况是false的,因为里面有了个空格,他底层源码是通过判断字符串长度决定的
isBlank 相比于上面还包含了whitespace,也就是说” “这种情况也是true的。
Not/All/Any/None
分别代表
- 不为空
- 字符串数组或参数全部为空
- 字符串数组或参数其中有一个为空
- 字符串数组或参数全部为不空
相比于!isEmpty()更容易看清逻辑,也是符合Java倡导的代码规范的。
Trim/Strip
trim()简单来说就是jdk的trim(), 只是多了一个null安全判断。把首尾的小于等于unicode32的全部删掉。
StringUtils.trim(null) = null
StringUtils.trim("") = ""
StringUtils.trim(" ") = ""
StringUtils.trim("abc") = "abc"
StringUtils.trim(" abc ") = "abc"
strip()直接使用就跟trim是一样的,但是可以通过多一个参数来控制要删除的字符,不单单指trim状态的字符。
StringUtils.strip(null, *) = null
StringUtils.strip("", *) = ""
StringUtils.strip("abc", null) = "abc"
StringUtils.strip(" abc", null) = "abc"
StringUtils.strip("abc ", null) = "abc"
StringUtils.strip(" abc ", null) = "abc"
StringUtils.strip(" abcyx", "xyz") = " abc"
ToNull/ToEmpty
正常的trim、strip如果字符串为null或被全部删除了,会返回一个null或”“字符串。
- ToNull 表示如果字符串为null或被全部删除了,统一返回一个null
- ToEmpty 表示如果字符串为null或被全部删除了,统一返回一个
”“
equals/compare
对于equals我要进行一些扩展了。
比较两个字符串是否相同,最简单的方式就是调用jdk String自带的equals方法。
String s1 = "qwe";
String s2 = "qwe";
System.out.println(s1.equals(s2));
复制代码
但其实这样非常容易出现null指针报错
String s1 = null;
String s2 = "qwe";
//直接报错
System.out.println(s1.equals(s2));
复制代码
所以我们推荐一般把字符串常量放在最前面,保证不会出现null异常
String s1 = null;
final String S2 = "qwe"
//返回false
System.out.println(s2.equals(s1));
复制代码
或者采取阿里最新的规范处理
String s1 = null;
String s2 = "qwe";
//里面做了null的处理,所以s1,s2为null都不会报错
System.out.println(Objects.equals(s1,s2));
复制代码
除了使用Objects的equals,还可以使用StringUtils.equals,底层都是一样的
对于compare也是做了null的比较处理,我就不说了,因为平常业务基本用不到。
startsWith/endsWith
没什么可讲的,跳过
IndexOf/LastIndexOf/Contains
- indexof 从前面找到第一个字符索引位置
- LastIndexOf 从后面找到第一个字符索引位置
- contains 判断是否存在
IndexOfAny/LastIndexOfAny/IndexOfAnyBut/LastIndexOfAnyBut
any表示的是,给定任意字符后,找到的第一个索引位置
but表示的是,给定任意字符后,找到的第一个不是对应字符的位置,例如
StringUtils.indexOfAnyBut("zzabyycdxx", new char[] {'z', 'a'} ) = 3
StringUtils.indexOfAnyBut("aba", new char[] {'z'} ) = 0
StringUtils.indexOfAnyBut("aba", new char[] {'a', 'b'} ) = -1
大家把这个例子看懂,就能理解anybut了。
substring
SubstringBefore/SubstringAfter/SubstringBetween
根据给定的字符串,切割字符串
StringUtils.substringAfter("abcba", "b") = "cba"
StringUtils.substringBefore("abc", "c") = "ab"
StringUtils.substringBetween("yabcz", "y", "z") = "abc"
Split/Join
split 将字符串拆分为子字符串数组,分隔符不包含在返回的 String 数组中。 相邻的分隔符被视为一个分隔符。如果没有传入分隔符,就通过whitespace分割。
join 将字符串数组,拼接成字符串,具体可以看下我的文章,Java字符串拼接的优雅方式
剩下的有点多,还不是很常见,等我以后再慢慢补吧。。。