String类字符串常量池
在 Java 中,字符串常量池(String Pool)是一个特殊的存储区域,用于存储字符串字面量(literal strings),以节省内存和提高性能。字符串常量池的概念在 Java 7 及以后的版本中有所变化,但基本原理相同。
字符串常量池的基本概念:
-
存储位置:在 Java 7 及之前,字符串常量池位于永久代(PermGen)中;从 Java 7u20 开始,字符串常量池被移动到了堆内存中。在 Java 8 及以后的版本中,永久代被元空间(Metaspace)取代。
-
字符串字面量:当程序中出现双引号括起来的字符串时,如
String s = "Hello";
,这个字符串 "Hello" 会被存储在字符串常量池中。如果字符串常量池中已经存在相同的字符串,那么就会直接引用已有的字符串,而不是创建一个新的字符串实例。 -
String s1 = "Hello"; String s2 = "Hello";
上述代码中,
s1
和s2
都指向字符串常量池中的同一个 "Hello"。 -
字符串的不可变性:由于字符串是不可变的,因此,任何对字符串的修改都会创建一个新的字符串对象,而不是修改原有的字符串常量池中的字符串。
-
字符串常量池的回收:由于字符串常量池存储的是字符串字面量,当字符串不再被引用时,它们可以被垃圾回收器回收。
字符串常量池的实现细节:
-
字符串字面量和 new 关键字:使用
new
关键字创建的字符串,如String s = new String("Hello");
,不会存储在字符串常量池中,而是在堆内存中创建一个新的字符串对象。 -
intern() 方法:
intern()
方法可以将一个字符串对象放入字符串常量池中。如果常量池中已经存在相同的字符串,则返回常量池中的字符串引用;如果不存在,则在常量池中创建一个新的字符串,并返回这个新字符串的引用。String s1 = "Hello"; // 存储在字符串常量池 String s2 = new String("Hello").intern(); // 使用 intern() 方法将新创建的字符串放入常量池 System.out.println(s1 == s2); // 输出 true,因为 s1 和 s2 都指向常量池中的同一个 "Hello"
-
性能考虑:字符串常量池可以减少内存使用,但过度使用
intern()
方法可能会导致性能问题,因为它需要在常量池中搜索或创建字符串。
String类方法
Java 中的 String
类是 java.lang
包的一部分,它用于表示和操作字符串。String
类是不可变的,这意味着一旦一个 String
对象被创建,它的内容就不能被改变。这个特性使得 String
对象在多线程环境中是线程安全的。
-
构造函数:
String()
: 创建一个空的字符串。String(char data[])
: 创建一个新的字符串,从字符数组data
。
-
字符串比较:
equals(Object another)
: 测试两个字符串是否相等。equalsIgnoreCase(String another)
: 测试两个字符串是否相等,忽略大小写。
-
字符串长度:
length()
: 返回字符串的长度。
-
字符访问:
charAt(int index)
: 返回指定索引处的字符。
-
字符串连接:
concat(String str)
: 将指定字符串连接到此字符串的结尾。
-
子字符串:
substring(int beginIndex)
: 返回一个新字符串,它是此字符串从beginIndex
开始的子字符串。substring(int beginIndex, int endIndex)
: 返回一个新字符串,它是此字符串从beginIndex
到endIndex - 1
的子字符串。
-
字符串分割:
split(String regex)
: 根据匹配给定正则表达式的模式来拆分此字符串。
-
大小写转换:
toLowerCase()
: 将字符串转换为小写。toUpperCase()
: 将字符串转换为大写。
-
字符串搜索:
indexOf(int ch)
: 返回字符ch
在此字符串中第一次出现处的索引。indexOf(String str)
: 返回子字符串str
在此字符串中第一次出现处的索引。
-
字符串替换:
replace(char oldChar, char newChar)
: 返回一个新字符串,它是将此字符串中所有出现的oldChar
替换为newChar
。
-
字符串修剪:
trim()
: 返回字符串的副本,忽略前导空白和尾部空白。
-
字符串格式化:
format(String format, Object... args)
: 将一个格式化字符串按照给定的格式和参数转换为新的字符串。
下面是一个使用 String
类的示例代码:
public class StringExample {
public static void main(String[] args) {
String greeting = "Hello, World!";
System.out.println("Original String: " + greeting);
// 字符串长度
System.out.println("Length of String: " + greeting.length());
// 访问特定位置的字符
System.out.println("Character at index 7: " + greeting.charAt(7));
// 子字符串
String sub = greeting.substring(7, 12);
System.out.println("Substring: " + sub);
// 大小写转换
String lowerCase = greeting.toLowerCase();
String upperCase = greeting.toUpperCase();
System.out.println("Lower Case: " + lowerCase);
System.out.println("Upper Case: " + upperCase);
// 字符串搜索
int index = greeting.indexOf("World");
System.out.println("Index of 'World': " + index);
// 字符串替换
String replaced = greeting.replace("World", "Java");
System.out.println("Replaced String: " + replaced);
// 字符串格式化
String formatted = String.format("The value is: %d", 42);
System.out.println(formatted);
}
}
String
类提供了丰富的方法来处理字符串的各种操作,包括但不限于比较、搜索、替换、拆分和格式化。由于 String
的不可变性,每次对 String
进行修改操作时,实际上都会创建一个新的 String
对象。
正则表达式
正则表达式(Regular Expression,简称 Regex)是一种强大的文本处理工具,用于匹配字符串中的字符组合。它使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。在编程语言和文本编辑器中,正则表达式通常用于搜索、替换、分割和验证文本。
基本组成:
- 字符:正则表达式中的普通字符(例如,
abc
)直接表示它自己。 - 特殊字符:有一些特殊字符具有特定的含义,如
.
表示任意单个字符,*
表示前面的元素可以出现零次或多次。 - 字符类:
[abc]
表示任何在括号内的字符(在这个例子中是 a、b 或 c)。 - 选择:
(x|y)
表示 x 或 y。 - 量词:
*
:零次或多次。+
:一次或多次。?
:零次或一次。{n}
:恰好 n 次。{n,}
:至少 n 次。{n,m}
:从 n 到 m 次。
正则表达式示例:
.*
:匹配任何内容。^hello
:从字符串开始匹配 "hello"。world$
:匹配 "world" 结束的字符串。[a-zA-Z]
:匹配任何单个字母。\d
:匹配任何数字,等同于[0-9]
。\s
:匹配任何空白字符(空格、制表符、换行符等)。abc|xyz
:匹配 "abc" 或 "xyz"。a{3}
:匹配三个连续的 "a"。a{3,}
:至少匹配三个 "a"。a{3,5}
:匹配 3 到 5 个 "a"。
正则表达式在编程中的应用:
在 Java 中,正则表达式是通过 java.util.regex
包中的类来实现的,主要包括:
Pattern
:编译后的正则表达式,用于后续的匹配操作。Matcher
:用于执行正则表达式匹配操作的对象。
以下是一个简单的 Java 示例,演示如何使用正则表达式来检查字符串是否为有效的电子邮件地址:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class RegexExample {
public static void main(String[] args) {
String emailRegex = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@" +
"(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$";
String email = "[email protected]";
Pattern pattern = Pattern.compile(emailRegex);
Matcher matcher = pattern.matcher(email);
if (matcher.matches()) {
System.out.println("这是一个有效的电子邮件地址。");
} else {
System.out.println("这不是一个有效的电子邮件地址。");
}
}
}
字符通配符
字符通配符通常在文件系统和编程语言中使用,用于匹配一个或多个字符。它们在不同的上下文中有不同的含义和用途
- 星号(*):
- 在文件系统中,
*
匹配任意数量的字符,包括零个字符。例如,*.txt
匹配所有扩展名为.txt
的文件。 - 在正则表达式中,
*
是一个量词,表示前面的元素可以出现零次或多次。
- 在文件系统中,
- 问号(?):
- 在文件系统中,
?
匹配单个字符。例如,file?.txt
可以匹配file1.txt
、file2.txt
等,但不能匹配file.txt
或file12.txt
。 - 在正则表达式中,
?
也是一个量词,表示前面的元素可以出现零次或一次。
- 在文件系统中,
- 方括号([ ]):
- 在文件系统中,通常不使用方括号作为通配符。
- 在正则表达式中,
[ ]
定义了一个字符集,可以匹配方括号内的任意一个字符。例如,[abc]
可以匹配 'a'、'b' 或 'c'。
- 花括号({ }):
- 在文件系统中,花括号没有特殊含义。
- 在正则表达式中,
{n}
、{n,}
和{n,m}
用于指定量词的范围。例如,a{3}
表示 'a' 恰好出现三次,a{3,}
表示 'a' 至少出现三次,a{3,5}
表示 'a' 出现三到五次。
- 管道符号(|):
- 在文件系统中,管道符号通常不作为通配符使用。
- 在正则表达式中,
|
表示选择,用来匹配两个或多个选项中的一个。例如,cat|dog
可以匹配 "cat" 或 "dog"。
- 点号(.):
- 在文件系统中,点号通常用来表示文件扩展名的开始,如
file.
。 - 在正则表达式中,
.
表示任意单个字符(除了换行符)。
- 在文件系统中,点号通常用来表示文件扩展名的开始,如
- 反斜杠(\):
- 在文件系统中,反斜杠通常用来指定路径,如
C:\Users\Username
。 - 在正则表达式中,反斜杠用作转义字符,用于转义那些具有特殊含义的字符,使其失去特殊含义,或者引入特殊字符。例如,
\.
匹配一个点号,\d
匹配一个数字。
- 在文件系统中,反斜杠通常用来指定路径,如
次数通配符
次数通配符用于指定正则表达式中模式出现的次数。以下是一些常见的次数通配符:
-
*
(星号):- 表示前面的元素可以出现零次或多次。例如,
a*
可以匹配 "Bob"、"Alice",也可以匹配空字符串。
- 表示前面的元素可以出现零次或多次。例如,
-
+
(加号):- 表示前面的元素至少出现一次或多次。例如,
a+
可以匹配 "apple" 中的 "ap",但不能匹配空字符串。
- 表示前面的元素至少出现一次或多次。例如,
-
?
(问号):- 表示前面的元素可以出现零次或一次。例如,
a?
可以匹配 "Bob" 中的 "o",也可以匹配 "apple" 中的 "a" 或空字符串。
- 表示前面的元素可以出现零次或一次。例如,
-
{n}
:- 表示前面的元素恰好出现
n
次。例如,a{3}
可以匹配 "banana" 中的 "aaa"。
- 表示前面的元素恰好出现
-
{n,}
:- 表示前面的元素至少出现
n
次,可以出现无限次。例如,a{2,}
可以匹配 "aaaa" 中的 "aa","aaa","aaaaa" 等。
- 表示前面的元素至少出现
-
{n,m}
:- 表示前面的元素至少出现
n
次,但不超过m
次。例如,a{2,3}
可以匹配 "banana" 中的 "aa" 或 "aaa",但不能匹配 "a" 或 "aaaa"。
- 表示前面的元素至少出现
-
*?
(非贪婪匹配):*
默认是贪婪的,它会尽可能多地匹配字符。使用?
可以使其变为非贪婪模式,尽可能少地匹配字符。例如,a*?
与 "aaaa" 匹配时,会匹配一个 "a" 而不是四个。
-
+?
和??
:- 类似于
*?
,+?
表示至少一次但尽可能少的匹配,??
表示零次或一次但尽可能少的匹配。
- 类似于
-
{n,m}?
:- 这表示非贪婪模式的
{n,m}
,匹配次数在n
到m
次之间,但尽可能少。
- 这表示非贪婪模式的
其他通配符
除了次数通配符,正则表达式中还有一些其他的通配符,用于匹配特定的字符或模式:
-
.
(点):- 匹配除换行符之外的任何单个字符。
-
^
(脱字符号):- 匹配输入字符串的开始位置。如果设置为多行模式,
^
匹配每一行的开始。
- 匹配输入字符串的开始位置。如果设置为多行模式,
-
$
(美元符号):- 匹配输入字符串的结束位置。如果设置为多行模式,
$
匹配每一行的结束。
- 匹配输入字符串的结束位置。如果设置为多行模式,
-
|
(管道符号):- 逻辑或操作符,用于匹配两个模式中的一个。例如,
cat|dog
匹配 "cat" 或 "dog"。
- 逻辑或操作符,用于匹配两个模式中的一个。例如,
-
[]
(方括号):- 字符集,匹配方括号内的任意一个字符。例如,
[abc]
匹配 "a"、"b" 或 "c"。
- 字符集,匹配方括号内的任意一个字符。例如,
-
[^...]
(否定字符集):- 匹配不在方括号内的任意字符。例如,
[^abc]
匹配除了 "a"、"b"、"c" 之外的任何字符。
- 匹配不在方括号内的任意字符。例如,
-
\s
:- 匹配任何空白字符,包括空格、制表符、换行符等。
-
\S
:- 匹配任何非空白字符。
-
\d
:- 匹配任何数字,等同于
[0-9]
。
- 匹配任何数字,等同于
-
\D
:- 匹配任何非数字字符。
-
\w
:- 匹配任何字母数字字符,包括下划线。等同于
[a-zA-Z0-9_]
。
- 匹配任何字母数字字符,包括下划线。等同于
-
\W
:- 匹配任何非字母数字字符。
-
\b
:- 匹配一个单词边界,即单词和空格之间的位置。
-
\B
:- 匹配非单词边界。
-
(x)
:- 括号用于创建一个分组,捕获匹配的文本。括号中的内容可以被其他量词量化。
-
(?:x)
:- 非捕获组,用于组织模式,但不捕获文本。
-
(?=x)
:- 正向前瞻,匹配
x
前面的文本,但x
不会成为匹配的一部分。
- 正向前瞻,匹配
-
(?!x)
:- 负向前瞻,匹配除了
x
前面的所有文本。
- 负向前瞻,匹配除了
-
(?<=x)
:- 正向后瞻,匹配
x
后面的文本,但x
不会成为匹配的一部分。
- 正向后瞻,匹配
-
(?<!x)
:- 负向后瞻,匹配除了
x
后面的所有文本。
- 负向后瞻,匹配除了
-
\
(反斜杠)**:- 转义特殊字符,使其失去特殊含义,或者表示特殊序列,如
\t
(制表符)。
- 转义特殊字符,使其失去特殊含义,或者表示特殊序列,如
-
\1
到\9
:- 反向引用,用于引用之前捕获的分组内容。