Java-字符串
java.lang.String
Java 各个版本中 String 的差别
Java 6 中 String 对象有以下成员:value:char[], hash:int, count:int, offset:int,是对 char 数组的封装。String 对象是通过 offset 和 count 两个属性来定位 char[] 数组,获取字符串。这么做可以高效、快速地共享数组对象,同时节省内存空间,但这种方式很有可能会导致内存泄漏。
Java 7-8 版本中,String 类中不再有 offset 和 count 两个变量,占用内存少了些,同时 String.substring 方法也不再共享 char[],从而解决了使用该方法可能导致的内存泄漏问题。
Java 9 开始,将 char[] 数组改为了 byte[] 数组,因为两个字节的 char 存储单字节字符时会浪费空间。
在 Java9 维护了一个新的属性 coder,它是编码格式的标识,在计算字符串长度或者调用 indexOf 函数时,需要根据这个字段,判断如何计算字符串长度。coder 属性默认有 0 和 1 两个值, 0 代表Latin-1(单字节编码),1 代表 UTF-16 编码。如果 String判断字符串只包含了 Latin-1,则 coder 属性值为 0 ,反之则为 1。
String
java.lang.String
public final class String
extends Object
implements Serializable, Comparable<String>, CharSequence {}
String 类代表字符串。Java 程序中的所有字符串字面值(如 “abc” )都作为此类的实例实现(所以字符串常量值上也是可以使用String类的成员方法的)。
字符串是常量;它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的,所以可以共享。例如:
String str = "abc";
等效于:
char data[] = {'a', 'b', 'c'};
String str = new String(data);
下面给出了一些如何使用字符串的更多示例:
System.out.println("abc");
String cde = "cde";
System.out.println("abc" + cde);
String c = "abc".substring(2,3);
String d = cde.substring(1, 2);
String 类包括的方法可用于检查序列的单个字符、比较字符串、搜索字符串、提取子字符串、创建字符串副本并将所有字符全部转换为大写或小写。大小写映射基于 Character 类指定的 Unicode 标准版。
Java 语言提供对字符串串联符号(”+”)以及将其他对象转换为字符串的特殊支持。字符串串联是通过 StringBuilder(或 StringBuffer)类及其 append 方法实现的。字符串转换是通过 toString 方法实现的,该方法由 Object 类定义,并可被 Java 中的所有类继承。
除非另行说明,否则将 null 参数传递给此类中的构造方法或方法将抛出 NullPointerException。
String 表示一个 UTF-16 格式的字符串,其中的增补字符 由代理项对 表示(有关详细信息,请参阅 Character 类中的 Unicode 字符表示形式)。索引值是指 char 代码单元,因此增补字符在 String 中占用两个位置(无论中文英文,java中一个字符占2个字节)。
valueOf()
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
注意 valueOf()
参数为 null 时会返回字符串 null
, 曾经在这点儿上出过bug
如果想参数为 null 时返回也是 null,可以改用 Optional 处理下。
length()
int length()
字符串长度,无论字母、数字、汉字,长度都是1
charAt()
char charAt(int index)
返回指定索引处的 char 值。索引范围为从 0 到 length() - 1。序列的第一个 char 值位于索引 0 处,第二个位于索引 1 处,依此类推,这类似于数组索引。
substring() 左闭右开
String substring(int beginIndex, int endIndex)
返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的 beginIndex 处开始,直到索引 endIndex - 1 处的字符。因此,该子字符串的长度为 endIndex-beginIndex。
contains(CharSequence s)
public boolean contains(CharSequence s) {
return indexOf(s.toString()) > -1;
}
当且仅当此字符串包含指定的 char 值序列时,返回 true。
- 参数:s - 要搜索的序列
- 返回:如果此字符串包含 s,则返回 true,否则返回 false
- 抛出: NullPointerException - 如果 s 为 null
indexOf() 第一次出现的下标
int indexOf(String str)
返回指定子字符串在此字符串中第一次出现处的索引。返回的整数是 this.startsWith(str, k)为 true 的最小 k 值。
返回:如果字符串参数作为一个子字符串在此对象中出现,则返回第一个这种子字符串的第一个字符的索引;如果它不作为一个子字符串出现,则返回 -1。
indexOf() 结果要和 -1 比较
如果目标子串出现在开头,indexOf(pattern) 返回值是 0。
所有判断 pattern 在 str 中是否存在时,要使用 if (Str.indexOf(pattern) > -1)
,如果用大于 0 做条件,pattern 出现在开始位置就被漏掉了。
此外,indexOf() > -1
和 contains()
方法是同义的,替换为 contains()
方法更易读。
endsWith()
boolean endsWith(String suffix)
测试此字符串是否以指定的后缀结束。
join(“,”, Array)
public static String join(CharSequence delimiter, CharSequence... elements)
返回以指定分隔符连接的elements元素串联起来组成的String字符串
注意:如果某个元素为null,则”null”字符串会被加入。
- 参数:
- delimiter:分隔符
- elements:要串接的元素
- 抛出:NullPointerException:如果delimiter或elements为空
- 从以下版本开始: 1.8
join(“,”, Collection)
public static String join(CharSequence delimiter, Iterable<? extends CharSequence> elements)
返回以指定分隔符连接的elements元素串联起来组成的String字符串,例如:
List<String> strings = List.of("Java", "is", "cool");
String message = String.join(" ", strings);
//message returned is: "Java is cool"
Set<String> strings = new LinkedHashSet<>(List.of("Java", "is", "very", "cool"));
String message = String.join("-", strings);
//message returned is: "Java-is-very-cool"
注意:如果某个元素为null,则”null”字符串会被加入。
- 参数:
- delimiter:分隔符
- elements:Iterable结构的要串接的元素集合
- 抛出:NullPointerException:如果delimiter或elements为空
- 从以下版本开始: 1.8
toLowerCase()
String toLowerCase()
使用默认语言环境的规则将此 String 中的所有字符都转换为小写。
trim()
String trim()
返回此字符串移除了前导和尾部空白的副本;如果没有前导和尾部空白,则返回此字符串。
replace()
replaceAll() 正则替换
String replaceAll(String regex, String replacement)
使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
例如 a.replaceAll(“ +”,” “);//将字符串a中的所有一个或多个连续空格替换为单个空格
String.replaceAll 会重复编译正则有性能问题
删除非字母数字字符
replaceAll("[^A-Za-z0-9]", "")
删除非字母数字字符
删除非数字字符
replaceAll("[\\D]", "")
或 replaceAll("[^0-9]", "")
删除非数字字符
@Test
public void testReplace() {
String str = "345hf969bdf974";
String str1 = str.replaceAll("\\D", ""); // 删除非数字字符
String str2 = RegExUtils.removeAll(str, "\\D");
System.out.println(str);
System.out.println(str1);
System.out.println(str2);
}
将日期格式替换为只包含数字的字符串,不使用 replaceAll, replaceAll 会重复编译正则有性能问题
// 只留下数字,replaceAll("[\\D]", "") 重复编译正则有性能问题
String str = "2023-01-11 12:19:22";
System.out.println(StringUtils.replaceChars(str, "- :", ""));
split(String regex, int limit)
String[] split(String regex, int limit)
根据匹配给定的正则表达式来拆分此字符串。
此方法返回的数组包含此字符串的子字符串,每个子字符串都由另一个匹配给定表达式的子字符串终止,或者由此字符串末尾终止。数组中的子字符串按它们在此字符串中出现的顺序排列。如果表达式不匹配输入的任何部分,那么所得数组只具有一个元素,即此字符串。
- 参数:
- regex:定界正则表达式
- limit:结果阈值,如上所述
- 返回:字符串数组,它是根据给定正则表达式的匹配拆分此字符串确定的
- 抛出:PatternSyntaxException - 如果正则表达式的语法无效
- 从以下版本开始: 1.4
limit 参数控制模式应用的次数,因此影响所得数组的长度。如果该限制 n 大于 0,则模式将被最多应用 n - 1 次,数组的长度将不会大于 n,而且数组的最后一项将包含所有超出最后匹配的定界符的输入。如果 n 为非正,那么模式将被应用尽可能多的次数,而且数组可以是任何长度。如果 n 为 0,那么模式将被应用尽可能多的次数,数组可以是任何长度,并且结尾空字符串将被丢弃。
例如:字符串 “boo:and:foo” 使用这些参数可生成以下结果:
Regex | Limit | 结果 |
---|---|---|
: | 2 | { “boo”, “and:foo” } |
: | 5 | { “boo”, “and”, “foo” } |
: | -2 | { “boo”, “and”, “foo” } |
o | 5 | { “b”, “”, “:and:f”, “”, “” } |
o | -2 | { “b”, “”, “:and:f”, “”, “” } |
o | 0 | { “b”, “”, “:and:f” } |
调用此方法的 str.split(regex, n)
形式与以下表达式产生的结果完全相同:Pattern.compile(regex).split(str, n)
split(String regex)
String[] split(String regex)
根据给定正则表达式的匹配拆分此字符串。
该方法的作用就像是使用给定的表达式和限制参数 0 来调用两参数 split 方法。因此,所得数组中不包括结尾空字符串。
- 参数:regex,定界正则表达式
- 返回:字符串数组,它是根据给定正则表达式的匹配拆分此字符串确定的
- 抛出:PatternSyntaxException - 如果正则表达式的语法无效
- 从以下版本开始: 1.4
例如:字符串 “boo:and:foo” 使用这些表达式可生成以下结果:
Regex | 结果 |
---|---|
: | { “boo”, “and”, “foo” } |
o | { “b”, “”, “:and:f” } |
注意转义字符.
和|
注意:“.”和“|”都是转义字符,必须得加”\“。同理:*
和+
也是如此
如果用“.”作为分隔的话,必须是如下写法:String.split("\\.")
,这样才能正确的分隔开,不能用String.split(".")
如果在一个字符串中有多个分隔符,可以用“|”作为连字符,比如:“acountId=? and act_id =? or extra=?”,想and和or都做分隔符,可以用String.split(“and|or”);
字符串拆分示例
拆分斜杠分隔字符串并存入列表
String orgcabinStr="W/C/Y/F";
String orgCabinArray[] = orgcabinStr.split("/");
List<String> orgCabinList = Arrays.asList(orgCabinArray);
int i=0;
while(i < orgCabinList.size() ){
System.out.println(orgCabinArray[i++]);
}
正则表达式用于String.split()分隔字符串
用空格来分隔单词时,可能单词间有多个空格,直接使用正则表达式 \\s+
或 +
匹配一个或多个空格即可。
@Test
public void testSplit() {
String input = "hello world, i am Java.";
String[] words = input.split("\\s+");
System.out.print(String.join(" ", words));
}
结果
hello world, i am Java.
equals()
boolean equals(Object anObject)
将此字符串与指定的对象比较。当且仅当该参数不为 null,并且是与此对象表示相同字符序列的 String 对象时,结果才为 true。
字符串相等判断
equals()方法比较的是对象的内容(区分字母的大小写格式) ,但是如果使用==
双等号操作符比较两个对象时, 比较的是两个对象的内存地址, 所以它们不相等 (即使内容相同, 不同对象的内存地址也是不相同的)
equalsIgnoreCase()
boolean equalsIgnoreCase(String anotherString)
将此 String 与另一个 String 比较,不考虑大小写。如果两个字符串的长度相同,并且其中的相应字符都相等(忽略大小写),则认为这两个字符串是相等的。
formatted()(Java15+)
public String formatted(Object... args) {
return new Formatter().format(this, args).toString();
}
更方便的格式化,例如:
String vectorFieldName = "content_vector";
String scriptSource = """
if (doc['%s'].size() == 0) {
return 0.0;
} else {
return cosineSimilarity(params.queryVector, '%s')+1;
}
""".formatted(vectorFieldName, vectorFieldName);
format()
String sf1=String.format("name is %s",name);
String sf2=String.format("value is %f",32.33434);
String sf3=String.format("value is %32.12f",32.33434);
转换符 | 说明 | 示例 |
---|---|---|
%s | 字符串类型 | github |
%c | 字符类型 | c |
%b | 布尔类型 | true |
%d | 整数类型(十进制) | 1024 |
%x | 整数类型(十六进制) | FF |
%o | 整数类型(八进制) | 77 |
%f | 浮点类型 | 99.66 |
%a | 十六进制浮点类型 | FF.35AE |
%e | 指数类型 | 9.38e+5 |
%g | 通用浮点类型(f和e类型中较短的 | — |
%h | 散列码 | — |
%% | % | — |
%n | 换行符 | — |
%tx | 日期与时间类型 | x代表不同的日期与时间转换符 |
%%转义
如果想输出 %
,直接 %%
就行,比如拼 sql 中的 %name% 时
String.format(“%%%s%%”, name)
isEmpty()
boolean isEmpty()
当且仅当 length() 为 0 时返回 true。
返回:如果 length() 为 0,则返回 true;否则返回 false。
字符串判空
以下是Java 判断字符串是否为空的四种方法:
- 方法一: 最多人使用的一个方法, 直观, 方便, 但效率很低:
if(s == null ||"".equals(s));
- 方法二: 比较字符串长度, 效率高, 是我知道的最好一个方法:
if(s == null || s.length() <= 0);
- 方法三: Java SE 6.0 才开始提供的方法, 效率和方法二几乎相等, 但出于兼容性考虑, 推荐使用方法二.
if(s == null || s.isEmpty());
- 方法四: 这是一种比较直观,简便的方法,而且效率也非常的高,与方法二、三的效率差不多:
if (s == null || s == "");
注意:s == null 是有必要存在的。如果 String 类型为null, 而去进行 equals(String) 或 length() 等操作会抛出java.lang.NullPointerException. 并且s==null 的顺序必须出现在前面,不然同样会抛出java.lang.NullPointerException.
如下Java代码会抛出空指针异常:String str = null; if(str.equals("") || str= == null){//会抛出异常 System.out.println("success"); }
StringBuffer
java.lang.StringBuffer
public final class StringBuffer
extends Object
implements Serializable, CharSequence
线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。
可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。
StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符添加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
例如,如果 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用 z.append(“le”) 会使字符串缓冲区包含“startle”,而 z.insert(4, “le”) 将更改字符串缓冲区,使之包含“starlet”。
通常,如果 sb 引用 StringBuilder 的一个实例,则 sb.append(x) 和 sb.insert(sb.length(), x) 具有相同的效果。
只要发生有关源序列(如在源序列中添加或插入)的操作,该类就只在执行此操作的字符串缓冲区上而不是在源上实现同步。
每个字符串缓冲区都有一定的容量。只要字符串缓冲区所包含的字符序列的长度没有超出此容量,就无需分配新的内部缓冲区数组。如果内部缓冲区溢出,则此容量自动增大。从 JDK 5 开始,为该类补充了一个单个线程使用的等价类,即 StringBuilder。与该类相比,通常应该优先使用 StringBuilder 类,因为它支持所有相同的操作,但由于它不执行同步,所以速度更快。
String 的值是不可变的,每次对String的操作都会生成新的String对象,不仅效率低,而且耗费大量内存空间。
StringBuffer 默认分配16字节长度的缓冲区,当字符串超过该大小时,会自动增加缓冲区长度,而不是生成新的对象。
StringBuffer只能通过 new 来创建对象。
StringBuffer多用于需要对一个字符串进行频繁的修改,例如追加、插入、删除等。
reverse()
public StringBuffer reverse()
字符串逆置,设n为字符串长度,则新串中k位置的元素等于原串中n-k-1位置的元素
append()
StringBuffer append(String str)
将指定的 String 添加到此序列中。
按顺序将 String 参数中的字符添加到此 StringBuffer 中,并使 StringBuffer 在长度上增加该参数的长度。如果 str 为 null,则将 4 个 “null” 字符添加到此 StringBuffer 中。
假设执行 append 方法前的原有字符序列的长度为 n,则在添加后的新字符序列中,如果k小于n,则新字符序列中索引k处的字符等于原有字符序列中索引k处的字符;如果k大于n,则它等于参数str中索引 k-n 处的字符。
该方法在 this(目标)对象上实现同步,但不在源上(sb)实现同步。
deleteCharAt()
StringBuffer deleteCharAt(int index)
删除指定位置的字符
insert(int offset, String str)
StringBuffer insert(int offset, String str)
在指定位置插入字符串
setCharAt(int index, char ch)
void setCharAt(int index, char ch)
修改指定位置的字符
StringBuilder
java.lang.StringBuilder
public final class StringBuilder
extends Object
implements Serializable, CharSequence
一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。
在 StringBuilder 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符添加或插入到字符串生成器中。append 方法始终将这些字符添加到生成器的末端;而 insert 方法则在指定的点添加字符。
例如,如果 z 引用一个当前内容为“start”的字符串生成器对象,则该方法调用 z.append(“le”) 将使字符串生成器包含“startle”,而 z.insert(4, “le”) 将更改字符串生成器,使之包含“starlet”。
通常,如果 sb 引用 StringBuilder 的实例,则 sb.append(x) 和 sb.insert(sb.length(), x) 具有相同的效果。
每个字符串生成器都有一定的容量。只要字符串生成器所包含的字符序列的长度没有超出此容量,就无需分配新的内部缓冲区。如果内部缓冲区溢出,则此容量自动增大。
将 StringBuilder 的实例用于多个线程是不安全的。如果需要这样的同步,则建议使用 StringBuffer。
append()
StringBuilder append(String str)
将指定的 String 添加到此序列中。
按顺序将 String 参数中的字符添加到此 StringBuilder 中,并使 StringBuilder 在长度上增加该参数的长度。如果 str 为 null,则将 4 个 “null” 字符添加到此 StringBuilder 中。
假设执行 append 方法前的原有字符序列的长度为 n,则在添加后的新字符序列中,如果k小于n,则新字符序列中索引k处的字符等于原有字符序列中索引k处的字符;如果k大于n,则它等于参数str中索引 k-n 处的字符。
toString()
String toString()
转换为String类型
总结
String,StringBuffer和StringBuilder
一般情况下,速度从快到慢为 StringBuilder > StringBuffer > String
- 操作少量的数据使用 String;
- 单线程操作大量数据使用 StringBuilder;
- 多线程操作大量数据使用 StringBuffer。
StringBuffer与StringBuilder对比
StringBuffer类的方法是多线程安全的,而StringBuilder不是线程安全的,相比而言,StringBuilder类会略微快一点。
StringBuffer与String对比
字符串的”+“操作实际上也是先创建一个StringBuffer对象,然后调用append()方法将字符串片段拼接起来,最后调用toString()方法转换为字符串。
对于长度较小的字符串,String的“+”操作更加直观,更具可读性,有些时候可以稍微牺牲一下效率。
但当在涉及大量字符串的频繁操作时,StringBuffer的效率优势会明显显现出来。
StringBuilder是否相等比较
StringBuilder与StringBuffer中没有覆盖Object的equals()方法,所以想要比较两个字符串是否相等,要将其转换为Strng类型。
字符串整型互相转换
字符串转整型
int Integer.parseInt(String s)
字符串转整型Integer Integer.valueOf(String s)
字符串转Integer类
整型转字符串
String s = String.valueOf(i);
String s = Integer.toString(i);
String s = "" + i;
字符串串接符号+
Java 语言提供对字符串串联符号(”+”)以及将其他对象转换为字符串的特殊支持。
字符串串联是通过 StringBuilder(或 StringBuffer)类及其 append 方法实现的。
字符串转换是通过 toString 方法实现的,该方法由 Object 类定义,并可被 Java 中的所有类继承。
Java除了String重载了+之外,不支持其他操作符的重载。严格来说,+也不算运算符重载,只是JVM做的语法糖,因为其内部时通过StringBuilder的append方法实现的。
上一篇 Spring-概述
下一篇 Java-基础
页面信息
location:
protocol
: host
: hostname
: origin
: pathname
: href
: document:
referrer
: navigator:
platform
: userAgent
: