三者继承结构:

String

String对象是不可变的,任何String类看起来会修改String值的方法实际上都是创建一个新的String对象。

特点:

  • final:String 被修饰为final,所以是不能被继承的。
  • immutable:immutable 是指不可改变的,里面的内容永远无法改变,不可改变的具体含义是指:不能增加长度,不能减少长度,不能插入字符,不能删除字符,不能修改字符。String 的表现就像是一个常量。

创建字符串

字符串即字符的组合,在Java中,字符串是一个类,所以我们见到的字符串都是对象 。
String 类有 11 种构造方法,这些方法提供不同的参数来初始化字符串,常见创建字符串手段:

  1. 每当有一个字面值出现的时候,虚拟机就会创建一个字符串
  2. 调用String的构造方法创建一个字符串对象
  3. 通过+加号进行字符串拼接也会创建新的字符串对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package character;

public class TestString {

public static void main(String[] args) {
String garen ="盖伦"; //字面值,虚拟机碰到字面值就会创建一个字符串对象

String teemo = new String("提莫"); //创建了两个字符串对象

char[] cs = new char[]{'崔','斯','特'};

String hero = new String(cs);// 通过字符数组创建一个字符串对象

String hero3 = garen + teemo;// 通过+加号进行字符串拼接
}
}

new String()和new String(“”)都是申明一个新的空字符串,是空串不是null

字符串长度

用于获取有关对象的信息的方法称为访问器方法。String 类的一个访问器方法是 length() 方法,它返回字符串对象包含的字符数,可以有长度为0的字符串,即空字符串。用法 stringName.length()。

一些字符串常用方法

StringBuffer 和 StringBuilder

当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。

和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。

StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。

由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

StringBuffer常用方法

方法 描述
public StringBuffer append(String s) 将指定的字符串追加到此字符序列。
public StringBuffer reverse() 将此字符序列用其反转形式取代。
public delete(int start, int end) 移除此序列的子字符串中的字符。
public insert(int offset, int i) int 参数的字符串表示形式插入此序列中。
replace(int start, int end, String str) 使用给定 String 中的字符替换此序列的子字符串中的字符。
int capacity() 返回当前容量。
char charAt(int index) 返回此序列中指定索引处的 char 值。
void ensureCapacity(int minimumCapacity) 确保容量至少等于指定的最小值。
void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 将字符从此序列复制到目标字符数组 dst
int indexOf(String str) 返回第一次出现的指定子字符串在该字符串中的索引。
int indexOf(String str, int fromIndex) 从指定的索引处开始,返回第一次出现的指定子字符串在该字符串中的索引。
int lastIndexOf(String str) 返回最右边出现的指定子字符串在此字符串中的索引。
int lastIndexOf(String str, int fromIndex) 返回 String 对象中子字符串最后出现的位置。
int length() 返回长度(字符数)。
void setCharAt(int index, char ch) 将给定索引处的字符设置为 ch
void setLength(int newLength) 设置字符序列的长度。
CharSequence subSequence(int start, int end) 返回一个新的字符序列,该字符序列是此序列的子序列。
String substring(int start) 返回一个新的 String,它包含此字符序列当前所包含的字符子序列。
String substring(int start, int end) 返回一个新的 String,它包含此序列当前所包含的字符子序列。
String toString() 返回此序列中数据的字符串表示形式。

三者区别

  1. 运行速度:StringBuilder > StringBuffer > String

    why:String长度不可变,每次操作都是不断地创建新的对象并不断触发GC(Garbage Collection,垃圾回收机制)。而StringBuilder则不会,它从头到尾一直是一个实例对象。实现方法是初始化的时候放在一个叫value的char数组里,而数组是可以扩容的,这样就无需创建新的对象。源码中,数组默认的初始长度是16,也可以根据构造方法指定。扩容系数: value.length * 2 + 2,而且只有当append是数据长度+value.count > value.length时才会扩容一次,不会每次都扩容去调用Arrays.copyof()。

  2. 线程安全:在线程安全上,StringBuilder 是线程不安全的,而 StringBuffer 是线程安全的。如果要进行的操作是多线程的,那么就要使用 StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的 StringBuilder。

  3. 初始化上的区别:String可以空赋值,StringBuffer 和 StringBuilder 不行。

  4. 字符修改上的区别:

    String:不可变字符串

    StringBuffer:可变字符串

    StringBuilder:可变字符序列

  5. 适用场景

    String:适用于少量的字符串操作的情况

    StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况

    StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

一些常用字符串实例

以下内容搬运自菜鸟教程

字符串比较

以下实例中我们通过字符串函数 compareTo (string) ,compareToIgnoreCase(String) 及 compareTo(object string) 来比较两个字符串,并返回字符串中第一个字母ASCII的差值。

实例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class StringCompareEmp{
public static void main(String args[]){
String str = "Hello World";
String anotherString = "hello world";
Object objStr = str;

System.out.println( str.compareTo(anotherString) );
System.out.println( str.compareToIgnoreCase(anotherString) ); //忽略大小写
System.out.println( str.compareTo(objStr.toString()));
}
}
/*Output:
-32
0
0
*/

compareTo() 方法的实现思路:两个字符数组依次从前开始比较,如果对象位置出现字符不同则返回两个字符的编码之差,后面的字符不再比较;如果两个字符数组的长度不一样,并且较短的数组和较长数组所有对应位置的字符都相同,则返回两个数组的长度之差。

查找字符串最后一次出现的位置

以下实例中我们通过字符串函数 strOrig.lastIndexOf(Stringname) 来查找子字符串 Stringname 在 strOrig 出现的位置:

实例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class SearchlastString {
public static void main(String[] args) {
String strOrig = "Hello world ,Hello Runoob";
int lastIndex = strOrig.lastIndexOf("Runoob");
if(lastIndex == - 1){
System.out.println("没有找到字符串 Runoob");
}else{
System.out.println("Runoob 字符串最后出现的位置: "+ lastIndex);
}
}
}
/*Output:
Runoob 字符串最后出现的位置: 19
*/

删除字符串中的一个字符

以下实例中我们通过字符串函数 substring() 函数来删除字符串中的一个字符,我们将功能封装在 removeCharAt 函数中。

实例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
public class Main {
public static void main(String args[]) {
String str = "this is Java";
System.out.println(removeCharAt(str, 3));
}
public static String removeCharAt(String s, int pos) {
return s.substring(0, pos) + s.substring(pos + 1);
}
}
/*Output:
thi is Java
*/

字符串替换

以下实例中我们使用 java String 类的 replace 方法来替换字符串中的字符:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class StringReplaceEmp{
public static void main(String args[]){
String str="Hello World";
System.out.println( str.replace( 'H','W' ) );
System.out.println( str.replaceFirst("He", "Wa") );
System.out.println( str.replaceAll("He", "Ha") );
}
}
/*Output:
Wello World
Wallo World
Hallo World
*/

字符串反转

以下实例演示了如何使用 Java 的反转函数 reverse() 将字符串反转:

1
2
3
4
5
6
7
8
9
10
11
12
public class StringReverseExample{
public static void main(String[] args){
String string="runoob";
String reverse = new StringBuffer(string).reverse().toString();
System.out.println("字符串反转前:"+string);
System.out.println("字符串反转后:"+reverse);
}
}
/*Output:
字符串反转前:runoob
字符串反转后:boonur
*/

字符串查找

以下实例使用了 String 类的 indexOf() 方法在字符串中查找子字符串出现的位置,如果存在返回字符串出现的位置(第一位为0),如果不存在返回 -1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class SearchStringEmp {
public static void main(String[] args) {
String strOrig = "Google Runoob Taobao";
int intIndex = strOrig.indexOf("Runoob");
if(intIndex == - 1){
System.out.println("没有找到字符串 Runoob");
}else{
System.out.println("Runoob 字符串位置 " + intIndex);
}
}
}
/*Output:
Runoob 字符串位置 7
*/

字符串分割

以下实例使用了 split(string) 方法通过指定分隔符将字符串分割为数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class JavaStringSplitEmp {
public static void main(String args[]){
String str1 = "www.runoob.com";
String[] temp1;
String delimeter1 = "\\."; // 指定分割字符, . 号需要转义
temp1 = str1.split(delimeter1); // 分割字符串
for(String x : temp1){
System.out.println(x);
}
}
}
/*Output:
www
runoob
com
*/

字符串分隔(StringTokenizer)

Java 中我们可以使用 StringTokenizer 设置不同分隔符来分隔字符串,默认的分隔符是:空格、制表符(\t)、换行符(\n)、回车符(\r)。

以下实例演示了 StringTokennizer 使用空格和等号来分隔字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) {
String str = "This is String , split by StringTokenizer, created by runoob";
StringTokenizer st = new StringTokenizer(str);
System.out.println("----- 通过空格分隔 ------");
while (st.hasMoreElements()) {
System.out.println(st.nextElement());
}
System.out.println("----- 通过逗号分隔 ------");
StringTokenizer st2 = new StringTokenizer(str, ",");
while (st2.hasMoreElements()) {
System.out.println(st2.nextElement());
}
}
}
/*Output:
----- 通过空格分隔 ------
This
is
String
,
split
by
StringTokenizer,
created
by
runoob
----- 通过等号分隔 ------
This is String
split by StringTokenizer
created by runoob
*/

字符串小写转大写

以下实例使用了 String toUpperCase() 方法将字符串从小写转为大写:

1
2
3
4
5
6
7
8
9
10
11
12
public class StringToUpperCaseEmp {
public static void main(String[] args) {
String str = "string runoob";
String strUpper = str.toUpperCase();
System.out.println("原始字符串: " + str);
System.out.println("转换为大写: " + strUpper);
}
}
/*Output:
原始字符串: string runoob
转换为大写: STRING RUNOOB
*/

测试两个字符串区域是否相等

以下实例使用了 regionMatches() 方法测试两个字符串区域是否相等:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class StringRegionMatch{
public static void main(String[] args){
String first_str = "Welcome to Microsoft";
String second_str = "I work with microsoft";
boolean match1 = first_str.
regionMatches(11, second_str, 12, 9);
boolean match2 = first_str.
regionMatches(true, 11, second_str, 12, 9); //第一个参数 true 表示忽略大小写区别
System.out.println("区分大小写返回值:" + match1);
System.out.println("不区分大小写返回值:" + match2);
}
}
/*Output:
区分大小写返回值:false
不区分大小写返回值:true
*/

first_str.regionMatches(11, second_str, 12, 9) 表示将 first_str 字符串从第11个字符”M”开始和 second_str 字符串的第12个字符”M”开始逐个比较,共比较 9 对字符,由于字符串区分大小写,所以结果为false。如果设置第一个参数为 true ,则表示忽略大小写区别,所以返回 true。

字符串性能比较测试

以下实例演示了通过两种方式创建字符串,并测试其性能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class StringComparePerformance{
public static void main(String[] args){
long startTime = System.currentTimeMillis();
for(int i=0;i<50000;i++){
String s1 = "hello";
String s2 = "hello";
}
long endTime = System.currentTimeMillis();
System.out.println("通过 String 关键词创建字符串"
+ " : "+ (endTime - startTime)
+ " 毫秒" );

long startTime1 = System.currentTimeMillis();
for(int i=0;i<50000;i++){
String s3 = new String("hello");
String s4 = new String("hello");
}
long endTime1 = System.currentTimeMillis();
System.out.println("通过 String 对象创建字符串"
+ " : " + (endTime1 - startTime1)
+ " 毫秒");
}
}
/*Output:
通过 String 关键词创建字符串 : 6 毫秒
通过 String 对象创建字符串 : 14 毫秒
*/

字符串优化

intern()方法设计的初衷,就是重用String对象,以节省内存消耗。但是缺点是比较耗时。

以下实例演示了通过 String.intern() 方法来优化字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class StringOptimization {
public static void main(String[] args){
String variables[] = new String[50000];
for( int i=0;i <50000;i++){
variables[i] = "s"+i;
}
long startTime0 = System.currentTimeMillis();
for(int i=0;i<50000;i++){
variables[i] = "hello";
}
long endTime0 = System.currentTimeMillis();
System.out.println("直接使用字符串: "+ (endTime0 - startTime0) + " ms" );
long startTime1 = System.currentTimeMillis();
for(int i=0;i<50000;i++){
variables[i] = new String("hello");
}
long endTime1 = System.currentTimeMillis();
System.out.println("使用 new 关键字:" + (endTime1 - startTime1) + " ms");
long startTime2 = System.currentTimeMillis();
for(int i=0;i<50000;i++){
variables[i] = new String("hello");
variables[i] = variables[i].intern();
}
long endTime2 = System.currentTimeMillis();
System.out.println("使用字符串对象的 intern() 方法: "
+ (endTime2 - startTime2)
+ " ms");
}
}
/*Output:
直接使用字符串: 3 ms
使用 new 关键字:5 ms
使用字符串对象的 intern() 方法: 10 ms
*/

存在于 .class 文件中的常量池(常量池( constant pool )指的是在编译期被确定,并被保存在已编译的 .class 文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。),在运行期被 JVM 装载,并且可以扩充。String 的 intern() 方法就是扩充常量池的一个方法;当一个 String 实例 str 调用 intern() 方法时,Java 查找常量池中是否有相同 Unicode 的字符串常量,如果有,则返回其的引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用。

字符串格式化

以下实例演示了通过 format() 方法来格式化字符串,还可以指定地区来格式化:

1
2
3
4
5
6
7
8
9
10
11
12
import java.util.*;
public class StringFormat {
public static void main(String[] args){
double e = Math.E;
System.out.format("%f%n", e);
System.out.format(Locale.CHINA , "%-10.4f%n%n", e); //指定本地为中国(CHINA)
}
}
/*Output:
2.718282
2.7183
*/

连接字符串

以下实例演示了通过 “+” 操作符和StringBuffer.append() 方法来连接字符串,并比较其性能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class Main {
public static void main(String[] args){
String result1 = null;
StringBuffer result = new StringBuffer();
long startTime = System.currentTimeMillis();
for(int i=0;i<5000;i++){
result1 += "This is"
+ "testing the"
+ "difference"+ "between"
+ "String"+ "and"+ "StringBuffer";
}
long endTime = System.currentTimeMillis();
System.out.println("字符串连接"
+ " - 使用 + 操作符 : "
+ (endTime - startTime)+ " ms");
long startTime1 = System.currentTimeMillis();
for(int i=0;i<5000;i++){

result.append("This is");
result.append("testing the");
result.append("difference");
result.append("between");
result.append("String");
result.append("and");
result.append("StringBuffer");
}
long endTime1 = System.currentTimeMillis();
System.out.println("字符串连接"
+ " - 使用 StringBuffer : "
+ (endTime1 - startTime1)+ " ms");
}
}
/*Output:
字符串连接 - 使用 + 操作符 : 1151 ms
字符串连接 - 使用 StringBuffer : 2 ms
*/

当需要对字符串对象的长度进行变化时,用 + 拼接的性能在循环时就会慢的慢的多,实际上 + 号拼接字符串也是通过 StringBuild 或 StringBuffer 实现的,但当进行频繁的修改本身时,+ 拼接会比直接用方法拼接产生更多的中间垃圾对象,耗用更多的内存,因此更推荐使用 StringBuild。