Java String
上次修改时间:2024 年 7 月 4 日
在本文中,我们将展示如何使用 String 和 StringBuilder 在 Java 中处理字符串。
定义
在 Java 中,字符串是 Unicode 字符序列。字符串是对象。有两个基本的类用于处理字符串
StringStringBuilder
String 是一个不可变的字符序列。StringBuilder 是一个可变的字符序列。(还有一个 StringBuffer 类,可以被多个线程使用。如果我们不处理线程,我们使用 StringBuilder。)
字符串字面量 是源代码中用双引号括起来的一系列字符。例如,“Java” 是一个字符串字面量。每当 Java 编译器在代码中遇到字符串字面量时,它都会创建一个具有其值的 String 对象。
String lang = "Java"; // same as String lang = new String("Java");
字符串字面量被许多编程语言使用。这是一种既定的约定,并且还可以节省打字时间。
初始化字符串
有多种创建字符串的方法,包括不可变和可变字符串。我们将展示其中的一些方法。
void main() {
char[] cdb = {'M', 'y', 'S', 'Q', 'L'};
String lang = "Java";
String ide = new String("NetBeans");
String db = new String(cdb);
System.out.println(lang);
System.out.println(ide);
System.out.println(db);
StringBuilder sb1 = new StringBuilder(lang);
StringBuilder sb2 = new StringBuilder();
sb2.append("Fields");
sb2.append(" of ");
sb2.append("glory");
System.out.println(sb1);
System.out.println(sb2);
}
该示例展示了几种创建 String 和 StringBuilder 对象的方法。
String lang = "Java";
最常见的方法是从字符串字面量创建一个字符串对象。
String ide = new String("NetBeans");
在这行代码中,我们使用通常构建对象的方式,使用 new 关键字创建一个字符串。
String db = new String(cdb);
这里我们从字符数组创建一个字符串对象。
StringBuilder sb1 = new StringBuilder(lang);
StringBuilder 对象是从 String 创建的。
StringBuilder sb2 = new StringBuilder();
sb2.append("Fields");
sb2.append(" of ");
sb2.append("glory");
我们创建一个空的 StringBuilder 对象。我们将三个字符串附加到该对象中。
$ java Main.java Java NetBeans MySQL Java Fields of glory
String 是一个对象
字符串是对象;它们不是原始数据类型。字符串是 String 或 StringBuilder 类的实例。由于它们是对象,因此它们有多种方法可用于执行各种工作。
void main() {
String lang = "Java";
String bclass = lang.getClass().toString();
System.out.println(bclass);
String sup = lang.getClass().getSuperclass().toString();
System.out.println(sup);
if (lang.isEmpty()) {
System.out.println("The string is empty");
} else {
System.out.println("The string is not empty");
}
int n = lang.length();
System.out.println("The string has " + n + " characters");
}
在这个程序中,我们演示了字符串是对象。对象必须有一个类名、一个父类,并且它们还必须有一些我们可以调用的方法。
String lang = "Java";
创建了一个 String 类型的对象。
String bclass = lang.getClass().toString();
我们确定了 lang 变量引用的对象的类名。
String sup = lang.getClass().getSuperclass().toString();
接收到我们对象的父类。所有对象至少有一个父类 — Object。
if (lang.isEmpty()) {
System.out.println("The string is empty");
} else {
System.out.println("The string is not empty");
}
对象有各种方法。一个有用的字符串方法是 isEmpty 方法,它确定字符串是否为空。
int n = lang.length();
length 方法返回字符串的大小。
$ java Main.java class java.lang.String class java.lang.Object The string is not empty The string has 4 characters
我们的字符串对象是 String 类的实例。它具有 Object 父类。该对象不为空,并且包含四个字符。
字符串长度
确定 Unicode 字符串的长度并不容易。 length 方法仅适用于某些 Unicode 字符。
import java.text.BreakIterator;
void main() {
var text1 = "falcon";
var n1 = text1.length();
System.out.printf("%s has %d characters%n", text1, n1);
System.out.println("----------------------------");
var text2 = "вишня";
var n2 = text2.length();
System.out.printf("%s has %d characters%n", text2, n2);
System.out.println("----------------------------");
var text3 = "🐺🦊🦝";
var n3 = text3.length();
System.out.printf("%s has %d characters%n", text3, n3);
var n3_ = graphemeLength(text3);
System.out.printf("%s has %d characters%n", text3, n3_);
System.out.println("----------------------------");
var text4 = "नमस्ते";
var n4 = text4.length();
System.out.printf("%s has %d characters%n", text4, n4);
var n4_ = graphemeLength(text4);
System.out.printf("%s has %d characters%n", text4, n4_);
}
int graphemeLength(String text) {
BreakIterator it = BreakIterator.getCharacterInstance();
it.setText(text);
int count = 0;
while (it.next() != BreakIterator.DONE) {
count++;
}
return count;
}
在该示例中,我们尝试确定各种文本的长度。
var text1 = "falcon"; var n1 = text1.length();
对于基本拉丁字符,length 方法工作正常。
var text2 = "вишня"; var n2 = text2.length();
对于此西里尔文本,它也工作正常。
var text3 = "🐺🦊🦝"; var n3 = text3.length();
对于表情符号,我们使用 length 得到了错误的结果。
var n3_ = graphemeLength(text3);
通过在 graphemeLength 中使用的 BreakIterator,我们获得了表情符号的正确结果。
var text4 = "नमस्ते";
var n4 = text4.length();
System.out.printf("%s has %d characters%n", text4, n4);
var n4_ = graphemeLength(text4);
System.out.printf("%s has %d characters%n", text4, n4_);
在 Java 20 中,BreakIterator 得到了增强,可以返回梵文的正确值。(适当的值是四个字符。)
$ java Main.java falcon has 6 characters ---------------------------- вишня has 5 characters ---------------------------- 🐺🦊🦝 has 6 characters 🐺🦊🦝 has 3 characters ---------------------------- नमस्ते has 6 characters नमस्ते has 4 characters
可变 & 不可变字符串
String 是一个不可变字符序列,而 StringBuilder 是一个可变字符序列。 下一个示例将显示差异。
void main() {
String name = "Jane";
String name2 = name.replace('J', 'K');
String name3 = name2.replace('n', 't');
System.out.println(name);
System.out.println(name3);
StringBuilder sb = new StringBuilder("Jane");
System.out.println(sb);
sb.setCharAt(0, 'K');
sb.setCharAt(2, 't');
System.out.println(sb);
}
这两个对象都有替换字符串中字符的方法。
String name = "Jane";
String name2 = name.replace('J', 'K');
String name3 = name2.replace('n', 't');
在 String 上调用 replace 方法会导致返回一个新的修改后的字符串。原始字符串未更改。
sb.setCharAt(0, 'K'); sb.setCharAt(2, 't');
StringBuilder 的 setCharAt 方法会将给定索引处的字符替换为一个新字符。原始字符串被修改。
$ java Main.java Jane Kate Jane Kate
isBlank 方法
如果字符串为空或仅包含空格,则 isBlank 方法返回 true。
import java.util.List;
void main() {
var data = List.of("sky", "\n\n", " ", "blue", "\t\t", "", "sky");
for (int i=0; i<data.size(); i++) {
var e = data.get(i);
if (e.isBlank()) {
System.out.printf("element with index %d is blank%n", i);
} else {
System.out.println(data.get(i));
}
}
}
我们遍历字符串列表并打印所有空白元素。
$ java Main.java sky element with index 1 is blank element with index 2 is blank blue element with index 4 is blank element with index 5 is blank sky
连接字符串
可以使用 + 运算符或 concat 方法添加不可变字符串。它们将形成一个新的字符串,该字符串是所有连接字符串的链。可变字符串具有 append 方法,该方法可以从任意数量的其他字符串构建字符串。
void main() {
System.out.println("Return" + " of " + "the king.");
System.out.println("Return".concat(" of ").concat("the king."));
StringBuilder sb = new StringBuilder();
sb.append("Return");
sb.append(" of ");
sb.append("the king.");
System.out.println(sb);
}
该示例通过添加字符串来创建三个句子。
System.out.println("Return" + " of " + "the king.");
通过使用 + 运算符来形成新的字符串。
System.out.println("Return".concat(" of ").concat("the king."));
concat 方法返回一个字符串,该字符串表示此对象的字符与字符串参数的字符的连接。
StringBuilder sb = new StringBuilder();
sb.append("Return");
sb.append(" of ");
sb.append("the king.");
通过三次调用 append 方法来创建一个 StringBuilder 类型的可变对象。
$ java Main.java Return of the king. Return of the king. Return of the king.
使用引号
在某些情况下,例如使用直接引语时,必须转义内部引号。
void main() {
System.out.println("There are may stars");
System.out.println("He said: \"Which one are you looking at?\"");
}
我们使用 \ 字符来转义额外的引号。
$ java Main.java There are may stars He said: "Which one are you looking at?"
多行字符串
文本块允许定义多行字符串。要创建多行字符串,我们使用三引号。
String lyrics = """
I cheated myself
like I knew I would
I told ya, I was trouble
you know that I'm no good""";
void main() {
System.out.println(lyrics);
}
我们有一个跨越四行的诗节。
$ java Main.java I cheated myself like I knew I would I told ya, I was trouble you know that I'm no good
在引入文本块之前,我们必须进行连接操作并使用 \n 字符。
String lyrics = "I cheated myself\n" +
"like I knew I would\n" +
"I told ya, I was trouble\n" +
"you know that I'm no good";
void main() {
System.out.println(lyrics);
}
这四个字符串与 + 运算符连接。
字符串元素
字符串是字符序列。字符是字符串的基本元素。以下两个示例显示了一些处理字符串字符的方法。
void main() {
char[] crs = {'Z', 'e', 't', 'C', 'o', 'd', 'e' };
String s = new String(crs);
char c1 = s.charAt(0);
char c2 = s.charAt(s.length()-1);
System.out.println(c1);
System.out.println(c2);
int i1 = s.indexOf('e');
int i2 = s.lastIndexOf('e');
System.out.println("The first index of character e is " + i1);
System.out.println("The last index of character e is " + i2);
System.out.println(s.contains("t"));
System.out.println(s.contains("f"));
char[] elements = s.toCharArray();
for (char el : elements) {
System.out.println(el);
}
}
在第一个示例中,我们将使用不可变字符串。
char[] crs = {'Z', 'e', 't', 'C', 'o', 'd', 'e' };
String s = new String(crs);
从字符数组形成一个新的不可变字符串。
char c1 = s.charAt(0); char c2 = s.charAt(s.length()-1);
使用 charAt 方法,我们获取字符串的第一个和最后一个 char 值。
int i1 = s.indexOf('e');
int i2 = s.lastIndexOf('e');
使用 indexOf 和 lastIndexOf 方法,我们获取字符 'e' 的第一个和最后一个出现的位置。
System.out.println(s.contains("t"));
使用 contains 方法,我们检查字符串是否包含 t 字符。该方法返回一个布尔值。
char[] elements = s.toCharArray();
for (char el : elements) {
System.out.println(el);
}
toCharArray 方法从字符串创建一个字符数组。我们遍历数组并打印每个字符。
$ java Main.java Z e The first index of character e is 1 The last index of character e is 6 true false Z e t C o d e
在第二个示例中,我们使用 StringBuilder 类的元素。
void main() {
StringBuilder sb = new StringBuilder("Misty mountains");
System.out.println(sb);
sb.deleteCharAt(sb.length()-1);
System.out.println(sb);
sb.append('s');
System.out.println(sb);
sb.insert(0, 'T');
sb.insert(1, 'h');
sb.insert(2, 'e');
sb.insert(3, ' ');
System.out.println(sb);
sb.setCharAt(4, 'm');
System.out.println(sb);
}
形成一个可变字符串。我们通过删除、附加、插入和替换字符来修改字符串的内容。
sb.deleteCharAt(sb.length()-1);
此行删除最后一个字符。
sb.append('s');
已删除的字符被附加回字符串。
sb.insert(0, 'T'); sb.insert(1, 'h'); sb.insert(2, 'e'); sb.insert(3, ' ');
我们在字符串的开头插入四个字符。
sb.setCharAt(4, 'm');
最后,我们替换索引 4 处的字符。
$ java Main.java Misty mountains Misty mountain Misty mountains The Misty mountains The misty mountains
repeat 方法
repeat 方法返回一个重复 n 次的字符串。
void main() {
var word = "falcon ";
var output = word.repeat(5);
System.out.println(output);
}
在该示例中,我们将单词重复五次。
$ java Main.java falcon falcon falcon falcon falcon
lines 方法
lines 方法返回一个流,其中包含从字符串中提取的行,由行终止符分隔。
void main() {
var words = """
club
sky
blue
cup
coin
new
cent
owl
falcon
brave
war
ice
paint
water
""";
var wstream = words.lines();
wstream.forEach(word -> {
if (word.length() == 3) {
System.out.println(word);
}
});
}
文本块中有十四个单词。
var wstream = words.lines();
使用 lines 方法,我们创建了这些单词的流。
wstream.forEach(word -> {
if (word.length() == 3) {
System.out.println(word);
}
});
我们使用 forEach 遍历流并打印所有长度为三个字母的单词。
$ java Main.java sky cup new owl war ice
startsWith 方法
startsWith 检查字符串是否以给定的前缀开头。
void main() {
var words = "club\nsky\nblue\ncup\ncoin\nnew\ncent\nowl\nfalcon\nwar\nice";
var wstream = words.lines();
wstream.forEach(word -> {
if (word.startsWith("c")) {
System.out.println(word);
}
});
}
字符串中有几个单词。我们打印所有以字母 c 开头的单词。
$ java Main.java club cup coin cent
endsWith 方法
endsWith 方法检查字符串是否以指定的后缀结尾。
void main() {
var words = "club\nsky\nblue\ncup\ncoin\nnew\ncent\nowl\nfalcon\nwar\nice";
var wstream = words.lines();
wstream.forEach(word -> {
if (word.endsWith("n") || word.endsWith("y")) {
System.out.println(word);
}
});
}
在该示例中,我们打印所有以 n 或 y 结尾的单词。
$ java Main.java sky coin falcon
toUpperCase/toLowerCase 方法
toUpperCase 方法将字符串的所有字符转换为大写。 toLowerCase 方法将字符串的所有字符转换为小写。
void main() {
var word1 = "Cherry";
var u_word1 = word1.toUpperCase();
var l_word1 = u_word1.toLowerCase();
System.out.println(u_word1);
System.out.println(l_word1);
var word2 = "Čerešňa";
var u_word2 = word2.toUpperCase();
var l_word2 = u_word2.toLowerCase();
System.out.println(u_word2);
System.out.println(l_word2);
}
我们修改两个单词的大小写。
$ java Main.java CHERRY cherry ČEREŠŇA čerešňa
matches 方法
matches 方法告诉字符串是否与给定的正则表达式匹配。
void main() {
var words = """
book
bookshelf
bookworm
bookcase
bookish
bookkeeper
booklet
bookmark
""";
var wstream = words.lines();
wstream.forEach(word -> {
if (word.matches("book(worm|mark|keeper)?")) {
System.out.println(word);
}
});
}
在该示例中,我们打印所有满足指定子模式的单词。
$ java Main.java book bookworm bookkeeper bookmark
回文示例
回文是指一个单词、数字、短语或其他字符序列,正着读和反着读都一样,例如 madam 或 racecar。有很多方法可以检查一个字符串是否是回文。以下示例是可能的解决方案之一。
// A palindrome is a word, number, phrase, or other sequence of characters
// which reads the same backward as forward, such as madam or racecar
void main() {
System.out.println(isPalindrome("radar"));
System.out.println(isPalindrome("kayak"));
System.out.println(isPalindrome("forest"));
}
boolean isPalindrome(String original) {
char[] data = original.toCharArray();
int i = 0;
int j = data.length - 1;
while (j > i) {
if (data[i] != data[j]) {
return false;
}
++i;
--j;
}
return true;
}
我们有 isPalindrome 方法的实现。
boolean isPalindrome(String original) {
char[] data = original.toCharArray();
...
}
我们将字符串转换为字符数组。
int i = 0;
int j = data.length - 1;
while (j > i) {
if (data[i] != data[j]) {
return false;
}
++i;
--j;
}
return true;
我们遍历数组并将左侧字符与右侧对应的字符进行比较。如果全部匹配,我们返回 true,否则我们返回 false。
$ java Main.java true true false
子字符串
substring 方法返回字符串的一部分。起始索引是包含的,结束索引是不包含的。起始索引从零开始。
void main() {
String str = "bookcase";
System.out.println(str.substring(0, 4));
System.out.println(str.substring(4, str.length()));
}
该示例使用 substring 方法创建两个子字符串。
System.out.println(str.substring(0, 4));
这里我们得到“book”子字符串。零表示字符串的第一个字符。
System.out.println(str.substring(4, str.length()));
这里打印“case”子字符串。
$ java Main.java book case
拆分字符串
split 方法将字符串分割成几部分;它接受一个分隔正则表达式作为参数。
void main() {
String s = "Today is a beautiful day.";
String[] words = s.split(" ");
for (String word : words) {
System.out.println(word);
}
}
该示例将一个句子拆分为单词。
String s = "Today is a beautiful day.";
这是一个要拆分的句子。单词由空格字符分隔。
String[] words = s.split(" ");
使用 split 方法,我们将句子分割成单词。空格字符用作分隔符。该方法返回一个字符串数组。
for (String word : words) {
System.out.println(word);
}
我们遍历数组并打印其内容。
$ java Main.java Today is a beautiful day.
删除字符串字符
当我们把字符串拆分成单词时,有些单词会有起始或结尾的字符,例如逗号或句号。在接下来的示例中,我们将展示如何删除这些字符。
void main() {
String str = "Did you go there? We did, but we had a \"great\" service there.";
String[] parts = str.split(" ");
for (String part: parts) {
String word = removeChars(part);
System.out.println(word);
}
}
String removeChars(String part) {
String word = part;
if (part.endsWith(".") || part.endsWith("?") || part.endsWith(",")
|| part.endsWith("\"")) {
word = part.substring(0, part.length()-1);
}
if (part.startsWith("\"")) {
word = word.substring(1, part.length()-1);
}
return word;
}
该示例将字符串拆分为单词并删除潜在的逗号、句点、问号或双引号。
String str = "Did you go there? We did, but we had a \"great\" service there.";
在这个字符串中,我们在单词中附加了问号、逗号、引号和句点。
String removeChars(String part) {
...
}
在这个自定义方法中,我们从单词中删除这些字符。
if (part.endsWith(".") || part.endsWith("?") || part.endsWith(",")
|| part.endsWith("\"")) {
word = part.substring(0, part.length()-1);
}
在此 if 语句中,我们删除结尾字符。我们使用 endsWith 方法来识别我们要删除的字符。 substring 方法返回字符串的一部分,不包含该字符。
if (part.startsWith("\"")) {
word = word.substring(1, part.length()-1);
}
此外,我们删除起始字符。使用 startsWith 方法检查起始字符。
$ java Main.java Did you go there We did but we had a great service there
连接字符串
有一个 join 方法来连接字符串。请参阅 Java StringJoiner,以了解有关在 Java 中连接字符串的更多信息。
void main() {
String joined = String.join(" ", "Today", "is", "Sunday");
System.out.println(joined);
}
在该示例中,我们将三个字符串连接成一个最终字符串。
String joined = String.join(" ", "Today", "is", "Sunday");
join 方法的第一个参数是一个分隔符,它将分隔最终字符串中的每个字符串。其余参数是要连接的字符串。
比较字符串
有两种基本方法可以比较字符串。 equals 方法比较两个字符串的内容并返回一个布尔值,指示字符串是否相等。 equalsIgnoreCase 执行相同的操作,只是它忽略大小写。
void main() {
String a = "book";
String b = "Book";
System.out.println(a.equals(b));
System.out.println(a.equalsIgnoreCase(b));
}
我们使用上述方法比较两个字符串。
String a = "book"; String b = "Book";
我们定义两个要比较的字符串。
System.out.println(a.equals(b));
equals 方法返回 false。两个字符串的第一个字符不同。
System.out.println(a.equalsIgnoreCase(b));
当我们忽略大小写时,字符串相等:equalsIgnoreCase 方法返回 true。
$ java Main.java false true
如果我们正在将变量与字符串进行比较,请务必记住,字符串应位于比较方法的左侧。否则,我们可能会得到 NullPointerException。
import java.util.Random;
String readString() {
Random r = new Random();
boolean b = r.nextBoolean();
if (b == true) {
return "ZetCode";
} else {
return null;
}
}
void main() {
String d = readString();
if ("ZetCode".equals(d)) {
System.out.println("Strings are equal");
} else {
System.out.println("Strings are not equal");
}
}
在代码示例中,我们正确地比较字符串,避免了可能的 NullPointerException。
String readString() {
Random r = new Random();
boolean b = r.nextBoolean();
if (b == true) {
return "ZetCode";
} else {
return null;
}
readString 方法模拟了方法调用可能导致空值的情况。例如,如果我们尝试从数据库中读取值,可能会发生这种情况。
String d = readString();
d 变量可以包含空值。
if ("ZetCode".equals(d)) {
上面的行是比较两个字符串的正确方法,其中一个字符串是已知的文字。如果我们将 d 变量放在左侧,如果 d 变量包含空值,这将导致 NullPointerException。
equals 方法比较两个字符串的字符。 == 运算符测试引用相等性。所有字符串文字在 Java 中都会自动驻留。它们放置在字符串池中。这发生在编译时。如果两个变量包含两个相等的字符串文字,则它们实际上是指字符串池中的同一字符串对象。
void main() {
boolean a = "ZetCode" == "ZetCode";
boolean b = "ZetCode" == new String("ZetCode");
boolean c = "ZetCode" == "Zet" + "Code";
boolean d = "ZetCode" == new String("ZetCode").intern();
boolean e = "ZetCode" == " ZetCode ".trim();
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
System.out.println(e);
}
在此代码示例中,我们使用 == 运算符比较字符串对象。
boolean a = "ZetCode" == "ZetCode";
这些字符串文字是驻留的。因此,身份比较运算符返回 true。
boolean b = "ZetCode" == new String("ZetCode");
使用 new 运算符创建的字符串不会被驻留。比较运算符会导致 false 值。
boolean c = "ZetCode" == "Zet" + "Code";
字符串在编译时连接。字符串文字导致相同的对象。结果为 true。
boolean d = "ZetCode" == new String("ZetCode").intern();
intern 对象将右侧的字符串对象放入池中。因此,d 变量保存一个布尔值 true。
boolean e = "ZetCode" == " ZetCode ".trim();
trim 方法在运行时调用,生成一个不同的对象。 e 变量保存一个布尔值 false。
$ java Main.java true false true true false
格式化字符串
我们有三种基本方法可以在 Java 中格式化字符串。有关更多详细信息,请参阅 Java String format。
void main() {
String name = "John Doe";
String occupation = "gardener";
String txt = "%s is a %s";
String msg = txt.formatted(name, occupation);
System.out.println(msg);
System.out.format("%s is a %s\n", name, occupation);
System.out.printf("%s is a %s%n", name, occupation);
}
我们构建了相同的三次字符串。
String name = "John Doe"; String occupation = "gardener"; String txt = "%s is a %s"; String msg = txt.formatted(name, occupation);
formatted 方法是一个实例方法。 %s 是一个字符串格式说明符,它被实际值替换。
System.out.format("%s is a %s\n", name, occupation);
System.out.printf("%s is a %s%n", name, occupation);
format 和 printf 方法是静态的。
$ java Main.java John Doe is a gardener John Doe is a gardener John Doe is a gardener
来源
在本文中,我们使用了 Java 中的字符串。
作者
列出所有Java教程。