Java字符串
Java String数据类型可以包含一个字符序列(字符串)。
Java中的字符串在内部使用字节表示,编码为UTF-16。 UTF-16使用2个字节来表示单个字符。 UTF是一种字符编码,可以表示来自许多不同语言(字母)的字符。
创建字符串
Java中的字符串是对象。因此,你需要使用new运算符来创建一个新的Java String对象。示例如下:
String myString = new String("Hello World");
引号内的文本是String对象将包含的文本。
Java字符串字面量
Java可以用一个更简单的方式来创建一个新的String:
String myString = "Hello World";
不需要将文本“Hello World”作为参数传递给String构造函数,而只需将文本本身写在双引号里面就行了。这被称为一个字符串字面量。 Java编译器将在内部构建出新的Java String对象。
转义字符
Java字符串字面量可以接受一组转义字符,在创建的字符串时会将其转换为特殊字符。转义字符是如下:
转义字符 | 描述 |
---|---|
\\ | 在字符串中转换成单个\字符 |
\t | 转换成单个制表符 |
\r | 转换成单个回车符 |
\n | 转换成单个换行符 |
以下是使用转义字符创建Java字符串的示例:
String text = "\tThis text is one tab in.\r\n";
此字符串字面量将生成一个字符串,以制表符开头,并以回车符和换行符结束。
字符串字面量作为常量或单例
如果在其他字符串变量声明中使用相同的字符串(例如“Hello World”),那么Java虚拟机可能在内存中创建一个String实例。因此字符串字面量成为事实上的常量或单例。初始化相同常量字符串的各种不同变量将指向内存中相同的String实例。示例如下:
String myString1 = "Hello World";
String myString2 = "Hello World";
在本例中,Java虚拟机会让 myString1和 myString2指向相同的String对象。
更确切地说,表示Java字符串字面量的对象是从Java虚拟机内部保存的常量字符串池中获取的。这意味着即使是分别编译的不同项目的类,但在同一个应用程序中使用的类也可以共享常量的String对象。共享是在运行时发生的,而不是在编译时。
如果你想确保两个字符串变量分别指向不同的字符串对象,请使用 new运算符:
String myString1 = new String("Hello World");
String myString2 = new String("Hello World");
即使要创建的两个Java字符串的值(文本)是相同的,Java虚拟机也将在内存中创建两个不同的对象来表示它们。
字符串连接
字符串连接是指将一个字符串附加到另一个字符串后面。 Java中的字符串是不可改变的,意味着一旦创建就不能改变。因此,当将两个Java String对象连接起来时,实际上是将连接后的字符串放入第三个String对象中。
下面是一个Java字符串连接的例子:
String one = "Hello";
String two = "World";
String three = one + " " + two;
变量 three引用的String的内容是Hello World;另外两个字符串对象并不会发生变化。
字符串连接性能
字符串连接时,必须注意可能出现的性能问题。在Java中连接两个字符串将被Java编译器翻译成如下所示:
String one = "Hello";
String two = " World";
String three = new StringBuilder(one).append(two).toString();
正如你所看到的,在最后调用toString()方法之前,会创建一个新的StringBuilder,将第一个String传递给它的构造函数,将第二个String传递给它的append()方法。这段代码实际上创建了两个对象:一个StringBuilder实例和一个从toString()方法返回的新的String实例。
当它自己作为一个单独的语句执行时,这个额外的对象创建开销是微不足道的。然而,在循环内部执行时,情况就不一样了。
下面是在循环中进行字符串连接的示例:
String[] strings = new String[]{"one", "two", "three", "four", "five" };
String result = null;
for(String string : strings) {
result = result + string;
}
这段代码会被翻译成类似下面这样:
String[] strings = new String[]{"one", "two", "three", "four", "five" };
String result = null;
for(String string : strings) {
result = new StringBuilder(result).append(string).toString();
}
现在,在这个循环中的每次迭代都会创建一个新的StringBuilder。另外,同时也会一个toString()方法创建一个新的String对象。这个循环导致每次迭代的对象实例化开销并不大:一个StringBuilder对象和一个String对象。其本身也并不是真正的性能杀手,而与创建这些对象有关的其他东西才是。
每次执行 new StringBuilder(result))代码时,StringBuilder构造函数都将 result中的所有字符复制到StringBuilder中。循环越多,result就越大。 result越大,将其中的字符复制到新的StringBuilder所需的时间就越长,并再将字符从StringBuilder复制到由toString()方法创建的临时String中所需的时间也越长。换句话说,迭代越多,每次迭代会越慢。
字符串连接的最快方法是只创建StringBuilder一次,并在循环内重用相同的实例。示例如下:
String[] strings = new String[]{"one", "two", "three", "four", "five" };
StringBuilder temp = new StringBuilder();
for(String string : strings) {
temp.append(string);
}
String result = temp.toString();
这段代码避免了循环内部的StringBuilder和String对象实例化,因此也避免了两次复制字符,首先复制进StringBuilder,然后再复制进临时字符串。
字符串长度
你可以使用length()方法获取String的长度。字符串的长度是字符串包含的字符数,而不是用来表示字符串的字节数。示例如下:
String string = "Hello World";
int length = string.length();
子字符串
你可以复制出一个字符串的部分,这部分字符串称为子字符串(子串)。你可以通过String类中的 substring()方法来实现。示例如下:
String string1 = "Hello World";
String substring = string1.substring(0,5);
在执行这段代码之后,substring变量将包含字符串Hello。
substring()方法有两个参数。第一个是要截取字符串的开始索引。第二个是截取字符串的结束索引。请记住,截取的子串包含开始索引的字符,而不包含结束索引的字符。
字符串中的第一个字符索引为0,第二个字符索引为1,以此类推。字符串中的最后一个字符索引为String.length() - 1。
使用indexOf()搜索字符串
你可以使用 indexOf()方法在字符串搜索给定子串的位置。示例如下:
String string1 = "Hello World";
int index = string1.indexOf("World");
该代码执行后,index变量值为6。 indexOf()方法返回找到第一个匹配子字符串中第一个字符的位置的索引。在本例中,要匹配的子字符串World的W位于字符串的索引6处。
如果在字符串中找不到子字符串,则indexOf()方法返回-1;
indexOf()方法有另一个版本,可以接受开始搜索位置的索引。这样,你可以搜索字符串中同一子串多次出现的位置。示例如下:
String theString = "is this good or is this bad?";
String substring = "is";
int index = theString.indexOf(substring);
while(index != -1) {
System.out.println(index);
index = theString.indexOf(substring, index + 1);
}
这段代码将会在字符串“is this good or is this bad?”中搜索子字符串“is”的出现位置。它使用了 indexOf(substring,index)方法。参数 index告诉字符串中开始搜索的起始位置。在这个例子中,开始搜索的起始索引是上一次找到位置索引加1,这样可以确保你不会找到相同的位置。
这段代码输出如下:
0
5
16
21
子串“is”被所搜到4次,两次是单词“is”处搜到,两次是在“this”处搜到。
Java String类还有一个lastIndexOf()方法,用于查找最后一次出现的子串。示例如下:
String theString = "is this good or is this bad?";
String substring = "is";
int index = theString.lastIndexOf(substring);
System.out.println(index);
从这个代码将打印输出子串“is”最后出现的索引21
使用matches()方法和正则表达式匹配字符串
Java String matches()方法将正则表达式作为参数,如果正则表达式匹配到字符串,则返回true,否则返回false。
示例如下:
String text = "one two three two one";
boolean matches = text.matches(".*two.*");
字符串比较
Java中有一组用于字符串比较的方法:
- equals()
- equalsIgoreCase()
- startsWith()
- endsWith()
- compareTo()
equals()
equals()方法测试两个字符串是否完全相等。如果是,则返回true。如果不是,则返回false。示例如下:
String one = "abc";
String two = "def";
String three = "abc";
String four = "ABC";
System.out.println( one.equals(two) );
System.out.println( one.equals(three) );
System.out.println( one.equals(four) );
两个字符串 one和 three是相等的,但和 two、four不等。字符串相等必须是包含的字符完全匹配,所以小写字符不等于大写字符。
输出如下:
false
true
false
equalsIgnoreCase()
String类还有一个名为equalsIgnoreCase()的方法,它比较两个字符串是否相等时,但忽略字符的大小写。也就是说,大写字符认为是等于它们的小写字母。
startsWith() 和 endsWith()
startsWith()和endsWith()方法检查字符串是否以某个子字符串开头或者结尾。示例如下:
String one = "This is a good day to code";
System.out.println( one.startsWith("This") );
System.out.println( one.startsWith("This", 5) );
System.out.println( one.endsWith ("code") );
System.out.println( one.endsWith ("shower") );
这个例子创建一个String并检查它是否以各种子串开始和结束。
第一行(在String声明之后)检查String是否以子字符串“This”开头。所以startsWith()方法返回true。
第二行检查当从索引5的字符开始比较时,字符串是否以子字符串“This”开始。由于索引5处的字符是“i”,所以结果为假。
第三行检查字符串是否以子字符串“code”结尾。因此,endsWith()方法返回true。
第四行检查字符串是否以子字符串“shower”结尾。会返回false。
compareTo()
compareTo()方法将String与另一个String进行比较,并返回一个int,告诉String是否小于、等于或大于另一个String。如果第一个String比第二个String的排序靠前,则compareTo()返回负数。如果排序顺序相同,则返回0;如果第一个String比第二个String的排序靠后,那么compareTo()方法返回一个正数。
示例如下:
String one = "abc";
String two = "def";
String three = "abd";
System.out.println( one.compareTo(two) );
System.out.println( one.compareTo(three) );
本例中,将String one与另外两个String比较。输出如下:
-3
-1
返回的数字是负数,因为字符串 one比另外两个字符串的排序靠前。
compareTo()方法实际上属于 Comparable接口。这个接口在我的关于排序的教程中有更详细的描述。
你应该意识到compareTo()方法可能无法正确比较不是英文的字符串。要对特定语言的字符串正确排序,请使用Collator。
用trim()修剪字符串
Java String类包含一个名为trim()的方法,可以修剪字符串对象。修剪是为了删除字符串开头和结尾的空白字符。空白字符包括空格,制表符和换行符。示例如下:
String text = " And he ran across the field ";
String trimmed = text.trim();
上面代码执行后,变量 trimmed会指向一个String对象,该对象值为:
"And he ran across the field"
字符串对象开头和结尾的空白字符已经被删除。字符串内部的空白字符不会被删除。字符串内部是指第一个和最后一个非空白字符之间的意思。
trim()方法不会修改String实例。相反,它会返回一个新的Java String对象,该对象与创建它的String对象相同,只是删除了字符串开头和结尾处的空白。
trim()方法可以非常有用地修剪用户输入到输入字段的文本。例如,用户可以输入他或她的名字,并在最后一个单词之后或在第一个单词之前意外地放置额外的空格符。 trim()方法是用来删除这些额外的空白字符的一个简单方法。
用replace()替换字符串
Java String类包含一个名为replace()的方法,可以替换字符串中的字符。 replace()方法实际上并不替换现有字符串中的字符。相反,它返回一个新的String实例,它等于替换了给定的字符后创建的String实例。示例如下:
String source = "123abc";
String replaced = source.replace('a', '@');
上面代码执行后,变量 replaced指向一个新的String对象,其值为:
123@bc
replace()方法会将所有与第一个参数所匹配的字符替换为第二个参数所传递的字符。
replaceFirst()
Java String replaceFirst()方法只会将第一个通过正则表达式参数匹配到字符串替换为第二个参数。示例如下:
String text = "one two three two one";
String s = text.replaceFirst("two", "five");
上例会返回字符串"one five three two one".
replaceAll()
replaceAll()方法会将所有通过正则表达式参数匹配到字符串替换为第二个参数。示例如下:
String text = "one two three two one";
String t = text.replaceAll("two", "five");
上例会返回字符串"one five three five one".
使用split()分割字符串
Java String类包含一个split()方法,可用于将String分割成一个String对象数组。示例如下:
String source = "A man drove with a car.";
String[] occurrences = source.split("a");
执行完上面代码后, occurrences数组包含如下字符串实例:
"A m"
"n drove with "
" c"
"r."
源字符串已被"a"字符分割为多个字符串。返回的字符串不包含字符"a"。"a"字符用作分隔符来分割字符串,而分隔符不会返回到生成的字符串数组中。
传递给split()方法的参数实际上是一个Java正则表达式。正则表达式可以很复杂。上面的正则表达式只是匹配所有的“a”字符。也只匹配小写“a”。
String split()方法有另外一个版本可以接收第二个参数 limit。示例如下:
String source = "A man drove with a car.";
int limit = 2;
String[] occurrences = source.split("a", limit);
参数 limit限制了返回数组中元素的最大数量。如果字符串中的正则表达式的匹配比给定的限制数量更多,则数组将包含 limit - 1个匹配项,最后一个元素将包含剩余所有字符。所以,在上面的例子中,返回的数组将包含这两个字符串:
"A m"
"n drove with a car."
第一个字符串是正则表达式“a”的匹配。第二个String是第一个匹配后的所有字符串。
如果 limit是3而不是2,将返回如下:
"A m"
"n drove with "
" car."
注意最后一个String仍然包含一个“a”字符。这是因为这个字符串代表了上一次匹配之后的剩余字符串。
如果 limit为4或者大于4,将只返回分割字符串,因为在该String中只能匹配4个正则表达式"a"。
使用valueOf()将数字转换成字符串
Java String类包含一组名为valueOf()的重载静态方法,可用于将数字转换为String。示例如下:
String intStr = String.valueOf(10);
System.out.println("intStr = " + intStr);
String flStr = String.valueOf(9.99);
System.out.println("flStr = " + flStr);
输出如下:
intStr = 10
flStr = 9.99
对象转换成字符串
Object类包含一个名为toString()的方法。由于所有Java类继承自Object类,所有对象都有一个toString()方法。此方法可用于创建给定对象的字符串表示形式。示例如下:
Integer integer = new Integer(123);
String intStr = integer.toString();
注意:用toString()方法来返回给定对象的一个合适的字符串形式,对象的类必须重写toString()方法。如果没有,将会调用默认的toString()方法(继承自Object类)。默认的toString()方法没有提供多少有用的信息。许多内置的Java类已经有了一个合适的toString()方法。
获取字符和字节
使用charAt()方法可以在字符串的某个索引处获取一个字符。示例如下:
String theString = "This is a good day to code";
System.out.println( theString.charAt(0) );
System.out.println( theString.charAt(3) );
输出如下:
T
s
因为"T"处于索引0,"s"处于索引3处。
你还可以使用getBytes()方法获取String方法的字节表示形式。这里有两个例子:
String theString = "This is a good day to code";
byte[] bytes1 = theString.getBytes();
byte[] bytes2 = theString.getBytes(Charset.forName("UTF-8");
第一个getBytes()调用使用机器上的默认字符集编码返回字符串的字节表示形式。默认字符集取决于执行代码的机器。因此,通常会明确指定用于创建字节表示的字符集(如下一行所示)。
第二个getBytes()调用返回一个字符串的UTF-8字节表示形式。
大小写转换
你可以使用toUpperCase()和toLowerCase()方法将字符串转换为大写和小写。这里有两个例子:
String theString = "This IS a mix of UPPERcase and lowerCASE";
String uppercase = theString.toUpperCase();
String lowercase = theString.toLowerCase();
其余方法
String类还有其他的有用的方法并没有在教程讲到。你可以在String JavaDoc中找到它们。
下一篇:Java操作