ZetCode

Java String

上次修改时间:2024 年 7 月 4 日

在本文中,我们将展示如何使用 String 和 StringBuilder 在 Java 中处理字符串。

定义

在 Java 中,字符串是 Unicode 字符序列。字符串是对象。有两个基本的类用于处理字符串

String 是一个不可变的字符序列。StringBuilder 是一个可变的字符序列。(还有一个 StringBuffer 类,可以被多个线程使用。如果我们不处理线程,我们使用 StringBuilder。)

字符串字面量 是源代码中用双引号括起来的一系列字符。例如,“Java” 是一个字符串字面量。每当 Java 编译器在代码中遇到字符串字面量时,它都会创建一个具有其值的 String 对象。

String lang = "Java"; // same as String lang = new String("Java");

字符串字面量被许多编程语言使用。这是一种既定的约定,并且还可以节省打字时间。

初始化字符串

有多种创建字符串的方法,包括不可变和可变字符串。我们将展示其中的一些方法。

Main.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);
}

该示例展示了几种创建 StringStringBuilder 对象的方法。

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 是一个对象

字符串是对象;它们不是原始数据类型。字符串是 StringStringBuilder 类的实例。由于它们是对象,因此它们有多种方法可用于执行各种工作。

Main.java
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 字符。

Main.java
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 是一个可变字符序列。 下一个示例将显示差异。

Main.java
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');

StringBuildersetCharAt 方法会将给定索引处的字符替换为一个新字符。原始字符串被修改。

$ java Main.java
Jane
Kate
Jane
Kate

isBlank 方法

如果字符串为空或仅包含空格,则 isBlank 方法返回 true。

Main.java
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 方法,该方法可以从任意数量的其他字符串构建字符串。

Main.java
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.

使用引号

在某些情况下,例如使用直接引语时,必须转义内部引号。

Main.java
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?"

多行字符串

文本块允许定义多行字符串。要创建多行字符串,我们使用三引号。

Main.java
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 字符。

Main.java
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);
}

这四个字符串与 + 运算符连接。

字符串元素

字符串是字符序列。字符是字符串的基本元素。以下两个示例显示了一些处理字符串字符的方法。

Main.java
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');

使用 indexOflastIndexOf 方法,我们获取字符 '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 类的元素。

Main.java
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 次的字符串。

Main.java
void main() {

    var word = "falcon ";

    var output = word.repeat(5);
    System.out.println(output);
}

在该示例中,我们将单词重复五次。

$ java Main.java
falcon falcon falcon falcon falcon

lines 方法

lines 方法返回一个流,其中包含从字符串中提取的行,由行终止符分隔。

Main.java
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 检查字符串是否以给定的前缀开头。

Main.java
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 方法检查字符串是否以指定的后缀结尾。

Main.java
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);
        }
    });
}

在该示例中,我们打印所有以 ny 结尾的单词。

$ java Main.java
sky
coin
falcon

toUpperCase/toLowerCase 方法

toUpperCase 方法将字符串的所有字符转换为大写。 toLowerCase 方法将字符串的所有字符转换为小写。

Main.java
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 方法告诉字符串是否与给定的正则表达式匹配。

Main.java
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。有很多方法可以检查一个字符串是否是回文。以下示例是可能的解决方案之一。

Main.java
// 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 方法返回字符串的一部分。起始索引是包含的,结束索引是不包含的。起始索引从零开始。

Main.java
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 方法将字符串分割成几部分;它接受一个分隔正则表达式作为参数。

Main.java
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.

删除字符串字符

当我们把字符串拆分成单词时,有些单词会有起始或结尾的字符,例如逗号或句号。在接下来的示例中,我们将展示如何删除这些字符。

Main.java
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 中连接字符串的更多信息。

Main.java
void main() {

    String joined = String.join(" ", "Today", "is", "Sunday");
    System.out.println(joined);
}

在该示例中,我们将三个字符串连接成一个最终字符串。

String joined = String.join(" ", "Today", "is", "Sunday");

join 方法的第一个参数是一个分隔符,它将分隔最终字符串中的每个字符串。其余参数是要连接的字符串。

比较字符串

有两种基本方法可以比较字符串。 equals 方法比较两个字符串的内容并返回一个布尔值,指示字符串是否相等。 equalsIgnoreCase 执行相同的操作,只是它忽略大小写。

Main.java
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

Main.java
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 中都会自动驻留。它们放置在字符串池中。这发生在编译时。如果两个变量包含两个相等的字符串文字,则它们实际上是指字符串池中的同一字符串对象。

Main.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

Main.java
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);

formatprintf 方法是静态的。

$ java Main.java
John Doe is a gardener
John Doe is a gardener
John Doe is a gardener

来源

Java 字符串 - 语言参考

在本文中,我们使用了 Java 中的字符串。

作者

我的名字是 Jan Bodnar,我是一位充满热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我已经撰写了超过 1400 篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

列出所有Java教程