目录

  • 一、Java中的字符串
  • 二、不可变字符串
  • (1)String
  • (2)字符串池
  • (3)字符串拼接
  • (4)字符串查找
  • (5)字符串比较
  • 比较相等
  • 比较大小
  • 比较前缀和后缀
  • (6)字符串截取
  • 三、可变字符串
  • (1)StringBuffer和StringBuilder
  • (2)字符串追加
  • (3)字符串插入、删除和替换

一、Java中的字符串

Java中的字符串是由双引号括起来的多个字符,下面示例都是表示字符串常量:

"Hello World" 
"\u0048\u0065\u006c\u006c\u006f\u0020\u0057\u006f\u0072\u006c\u0064" 
"世界你好" 
"A" 
""

Java中的字符采用Unicode编码,所以Java字符串可以包含中文等亚洲字符
Java SE提供了三个字符串类:String、StringBuffer和StringBuilder。

  • String是不可变字符串
  • StringBuffer和StringBuilder是可变字符串。

关于字符串的详细解释,可以去查看官方文档 关于查询API的一般流程是:找包→找类或接口→查看类或接口→找方法或变量。可以尝试查找一下 String、StringBuffer和StringBuilder这些字符串类的API文档,熟悉一下这些类的用法。String类在java.lang包中。

二、不可变字符串

(1)String

Java中不可变字符串类是String,属于java.lang包,它也是Java非常重要的类。

创建String对象可以通过构造方法实现,常用的构造方法:

  • String():使用空字符串创建并初始化一个新的String对象。
  • String(String original):使用另外一个字符串创建并初始化一个新的 String 对象。
  • String(StringBuffer buffer):使用可变字符串对象(StringBuffer)创建并初始化一个新的 String 对象。
  • String(StringBuilder builder):使用可变字符串对象(StringBuilder)创建并初始化一个新的 String 对象。
  • String(byte[] bytes):使用平台的默认字符集解码指定的byte数组,通过byte数组创建并初始化一 个新的 String 对象。
  • String(char[] value):通过字符数组创建并初始化一个新的 String 对象。
  • String(char[] value, int offset, int count):通过字符数组的子数组创建并初始化一个新的 String 对 象;offset参数是子数组第一个字符的索引,count参数指定子数组的长度。

示例代码:

// 创建字符串对象
        String s1 = new String();
        String s2 = new String("helloWorld");
        String s3 = new String("\u0048\u0065\u006c\u006c\u006f\u0020\u0057\u006f\u0072\u006c\u0064");
        System.out.println("s2 = " + s2);
        System.out.println("s3 = " + s3);

        char chars[] = { 'a', 'b', 'c', 'd', 'e' };
        // 通过字符数组创建字符串对象
         String s4 = new String(chars);
        // 通过子字符数组创建字符串对象
         String s5 = new String(chars, 1, 4);
         System.out.println("s4 = " + s4);
         System.out.println("s5 = " + s5);

        byte[] bytes = { 97, 98, 99 };
        // 通过byte数组创建字符串对象
         String s6 = new String(bytes);
         System.out.println("s6 = " + s6);
         System.out.println("s6字符串长度 = " + s6.length());

运行结果:

s2 = helloWorld
s3 = Hello World
s4 = abcde
s5 = bcde
s6 = abc
s6字符串长度 = 3

(2)字符串池

前面的示例代码中获得字符串对象时都是直接使用字符 串常量,但Java中对象是使用new关键字创建,字符串对象也可以使用new关键字创建,代码如下:

//字符串常量
        String s9 = "Hello";
        String s10 = "Hello";

        //使用new关键字创建
        String s7 = new String("Hello");
        String s8 = new String("Hello");

        System.out.println(s7 == s9);
        System.out.println(s9 == s10);
        System.out.println(s7 == s8);
        System.out.println("--------------");
        System.out.println(s7.equals(s9));
        System.out.println(s7.equals(s8));

使用new关键字与字符串常量都能获得字符串对象,但它们之间有一些区别。
看打印结果:

false
true
false
--------------
true
true

这里就会抛出一个Java中的常见面试题:== 与equals()的区别?

  • ==运算符比较的是两个引用是否指向相同的对象(即同一内存空间),也就是说在内存空间中的存储位置是否一致。
  • equals是判断两个变量或者实例指向同一个内存空间的值是不是相同

文字解析:

从上面的运行结果可见,s7和s8指的是不同对象, s9和s10指向的是相同对象。
Java中的不可变字符串String常量,采用字符串池(String Pool)管理技术,字符串池是 一种字符串驻留技术。采用字符串常量赋值时会在字符串池中查 找"Hello"字符串常量,如果已经存在把引用赋值给s9,否则创建"Hello"字符串对象,并放到池中。根 据此原理,可以推定s10与s9是相同的引用,指向同一个对象。但此原理并不适用于new所创建的字符 串对象,它并没有放到字符串池中。s7和s8是不同的引用,指向不同的对象。

图解:

java并且字符 java中的字符_java并且字符

(3)字符串拼接

String字符串虽然是不可变字符串,但也可以进行拼接只是会产生一个新的对象。
String字符串拼接可 以使用+运算符或String的concat(String str)方法。+运算符优势是可以连接任何类型数据拼接成为字符 串,而concat方法只能拼接String类型字符串。

字符串拼接示例代码:

String s1 = "Hello"; 
// 使用+运算符连接 
String s2 = s1 + " ";
String s3 = s2 + "World";
System.out.println(s3);

String s4 = "Hello"; 
// 使用+运算符连接,支持+=赋值运算符
s4 += " ";
s4 += "World";
System.out.println(s4);

String s5 = "Hello"; 
// 使用concat方法连接 
s5 = s5.concat(" ").concat("World"); 
System.out.println(s5);

int age = 18; 
String s6= "她的年龄是" + age + "岁。"; 
System.out.println(s6);

char score = 'A'; 
String s7= "她的英语成绩是" + score; 
System.out.println(s7);

java.util.Date now = new java.util.Date();
//对象拼接自动调用toString()方法 
String s8= "今天是:" + now; 
System.out.println(s8);

(4)字符串查找

在给定的字符串中查找字符或字符串是比较常见的操作。在String类中提供了indexOf和lastIndexOf方法 用于查找字符或字符串,返回值是查找的字符或字符串所在的位置,-1表示没有找到。这两个方法有 多个重载版本:

  • int indexOf(int ch):从前往后搜索字符ch,返回第一次找到字符ch所在处的索引。
  • int indexOf(int ch, int fromIndex):从指定的索引开始从前往后搜索字符ch,返回第一次找到字符 ch所在处的索引。
  • int indexOf(String str):从前往后搜索字符串str,返回第一次找到字符串所在处的索引。
  • int indexOf(String str, int fromIndex):从指定的索引开始从前往后搜索字符串str,返回第一次找 到字符串所在处的索引。
  • int lastIndexOf(int ch):从后往前搜索字符ch,返回第一次找到字符ch所在处的索引。
  • int lastIndexOf(int ch, int fromIndex):从指定的索引开始从后往前搜索字符ch,返回第一次找到 字符ch所在处的索引。
  • int lastIndexOf(String str):从后往前搜索字符串str,返回第一次找到字符串所在处的索引。
  • int lastIndexOf(String str, int fromIndex):从指定的索引开始从后往前搜索字符串str,返回第一次 找到字符串所在处的索引。

字符串本质上是字符数组,因此它也有索引,索引从零开始。String的charAt(int index)方法 可以返回索引index所在位置的字符。

字符串查找的示例代码:

String sourceStr = "There is a string accessing example.";

//获得字符串长度 
int len = sourceStr.length(); 
//获得索引位置16的字符 
char ch = sourceStr.charAt(16);

//查找字符和子字符串 
int firstChar1 = sourceStr.indexOf('r'); 
int lastChar1 = sourceStr.lastIndexOf('r'); 
int firstStr1 = sourceStr.indexOf("ing"); 
int lastStr1 = sourceStr.lastIndexOf("ing"); 
int firstChar2 = sourceStr.indexOf('e', 15); 
int lastChar2 = sourceStr.lastIndexOf('e', 15); 
int firstStr2 = sourceStr.indexOf("ing", 5); 
int lastStr2 = sourceStr.lastIndexOf("ing", 5);

System.out.println("原始字符串:" + sourceStr); 
System.out.println("字符串长度:" + len); S
ystem.out.println("索引16的字符:" + ch); 
System.out.println("从前往后搜索r字符,第一次找到它所在索引:" + firstChar1); 
System.out.println("从后往前搜索r字符,第一次找到它所在索引:" + lastChar1); 
System.out.println("从前往后搜索ing字符串,第一次找到它所在索引:" + firstStr1); 
System.out.println("从后往前搜索ing字符串,第一次找到它所在索引:" + lastStr1); 
System.out.println("从索引为15位置开始,从前往后搜索e字符,第一次找到它所在索引:" + firstChar2); 
System.out.println("从索引为15位置开始,从后往前搜索e字符,第一次找到它所在索引:" + lastChar2); 
System.out.println("从索引为5位置开始,从前往后搜索ing字符串,第一次找到它所在索引:" + firstStr2); 
System.out.println("从索引为5位置开始,从后往前搜索ing字符串,第一次找到它所在索引:" + lastStr2);

运行结果:

原始字符串:There is a string accessing example.
字符串长度:36 索引16的字符:g 
从前往后搜索r字符,第一次找到它所在索引:3 
从后往前搜索r字符,第一次找到它所在索引:13 
从前往后搜索ing字符串,第一次找到它所在索引:14 
从后往前搜索ing字符串,第一次找到它所在索引:24 
从索引为15位置开始,从前往后搜索e字符,第一次找到它所在索引:21 
从索引为15位置开始,从后往前搜索e字符,第一次找到它所在索引:4 
从索引为5位置开始,从前往后搜索ing字符串,第一次找到它所在索引:14 
从索引为5位置开始,从后往前搜索ing字符串,第一次找到它所在索引:-1

(5)字符串比较

字符串比较是常见的操作,包括比较相等、比较大小、比较前缀和后缀等。

比较相等

String提供的比较字符串相等的方法:

  • boolean equals(Object anObject):比较两个字符串中内容是否相等。
  • boolean equalsIgnoreCase(String anotherString):类似equals方法,只是忽略大小写。
比较大小

String提供的比较大小的方法:

  • int compareTo(String anotherString):按字典顺序比较两个字符串。如果参数字符串等于此 字符串,则返回值 0;如果此字符串小于字符串参数,则返回一个小于 0 的值;如果此字 符串大于字符串参数,则返回一个大于 0 的值。
  • int compareToIgnoreCase(String str):类似compareTo,只是忽略大小写。
比较前缀和后缀
  • boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束。
  • boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始。

字符串比较示例代码如下:

String s1 = new String("Hello"); 
String s2 = new String("Hello"); 
// 比较字符串是否是相同的引用 
System.out.println("s1 == s2 : " + (s1 == s2)); 
// 比较字符串内容是否相等 
System.out.println("s1.equals(s2) : " + (s1.equals(s2)));

String s3 = "HELlo"; 
// 忽略大小写比较字符串内容是否相等 
System.out.println("s1.equalsIgnoreCase(s3) : " + (s1.equalsIgnoreCase(s3)));

// 比较大小 
String s4 = "java"; 
String s5 = "Swift"; 
// 比较字符串大小 s4 > s5 
System.out.println("s4.compareTo(s5) : " + (s4.compareTo(s5))); 
// 忽略大小写比较字符串大小 s4 < s5 
System.out.println("s4.compareToIgnoreCase(s5) : " + (s4.compareToIgnoreCase(s5)));

// 判断文件夹中文件名
String[] docFolder = { "java.docx", " JavaBean.docx", "Objecitve-C.xlsx", "Swift.docx " };

int wordDocCount = 0; 
// 查找文件夹中Word文档个数 
for (String doc : docFolder) { 
	// 去的前后空格 
	doc = doc.trim(); 
	// 比较后缀是否有.docx字符串 
	if (doc.endsWith(".docx")) { 
		wordDocCount++; 
	} 
}

System.out.println("文件夹中Word文档个数是: " + wordDocCount);

int javaDocCount = 0; 
// 查找文件夹中Java相关文档个数 
for (String doc : docFolder) {
	// 去的前后空格 
	doc = doc.trim(); 
	// 全部字符转成小写 
	doc = doc.toLowerCase(); 
	// 比较前缀是否有java字符串 
	if (doc.startsWith("java")) { 
		javaDocCount++; 
	}
}

System.out.println("文件夹中Java相关文档个数是:" + javaDocCount);

运行结果:

s1 == s2 : false 
s1.equals(s2) : true 
s1.equalsIgnoreCase(s3) : true 
s4.compareTo(s5) : 23 
s4.compareToIgnoreCase(s5) : -9 
文件夹中Word文档个数是: 3 
文件夹中Java相关文档个数是:2

(6)字符串截取

Java中字符串String截取方法主要的方法如下:

  • String substring(int beginIndex):从指定索引beginIndex开始截取一直到字符串结束的子字符串。
  • String substring(int beginIndex, int endIndex):从指定索引beginIndex开始截取直到索引endIndex-1处的字符,注意包括索引为beginIndex处的字符,但不包括索引为endIndex处的字符。

字符串截取方法示例代码如下:

String sourceStr = "There is a string accessing example."; 
// 截取example.子字符串 
String subStr1 = sourceStr.substring(28);  
// 截取string子字符串 
String subStr2 = sourceStr.substring(11, 17);  
System.out.printf("subStr1 = %s%n", subStr1); 
System.out.printf("subStr2 = %s%n",subStr2);

// 使用split方法分割字符串 
System.out.println("-----使用split方法-----"); 
String[] array = sourceStr.split(" "); 
for (String str : array) {
	System.out.println(str);
}

运行结果:

subStr1 = example. 
subStr2 = string 
-----使用split方法----
There 
is 
a 
string 
accessing 
example.

三、可变字符串

可变字符串在追加、删除、修改、插入和拼接等操作不会产生新的对象。

(1)StringBuffer和StringBuilder

Java提供了两个可变字符串类StringBuffer和StringBuilder,中文翻译为“字符串缓冲区”。

  • StringBuffer是线程安全的,它的方法是支持线程同步,线程同步会操作串行顺序执行,在单线程环境 下会影响效率。
  • StringBuilder是StringBuffer单线程版本,它不是线程安全的,但它 的执行效率很高。

总结一下:
  String:适用于少量的字符串操作的情况
  StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
  StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
  
因此在这方面运行速度快慢为:StringBuilder > StringBuffer > String


StringBuffer和StringBuilder具有完全相同的API,即构造方法和普通方法等内容一样。
StringBuilder的中 构造方法有4个:

  • StringBuilder():创建字符串内容是空的StringBuilder对象,初始容量默认为16个字符。
  • StringBuilder(CharSequence seq):指定CharSequence字符串创建StringBuilder对象。CharSequence 接口类型,它的实现类有:String、StringBuffer和StringBuilder等,所以参数seq可以是String、 StringBuffer和StringBuilder等类型。
  • StringBuilder(int capacity):创建字符串内容是空的StringBuilder对象,初始容量由参数capacity指 定的。
  • StringBuilder(String str):指定String字符串创建StringBuilder对象。

上述构造方法同样适合于StringBuffer类,具体可以见官方文档

字符串长度和字符串缓冲区容量区别。字符串长度是指在字符串缓冲区中目前所包含字符 串长度,通过length()获得;字符串缓冲区容量是缓冲区中所能容纳的最大字符数,通过capacity() 获得。当所容纳的字符超过这个长度时,字符串缓冲区自动扩充容量,但这是以牺牲性能为代价 的扩容。

字符串长度和字符串缓冲区容量示例代码如下:

// 字符串长度length和字符串缓冲区容量capacity 
StringBuilder sbuilder1 = new StringBuilder(); 
System.out.println("包含的字符串长度:" + sbuilder1.length()); 
System.out.println("字符串缓冲区容量:" + sbuilder1.capacity());

StringBuilder sbuilder2 = new StringBuilder("Hello"); 
System.out.println("包含的字符串长度:" + sbuilder2.length()); 
System.out.println("字符串缓冲区容量:" + sbuilder2.capacity());

// 字符串缓冲区初始容量是16,超过之后会扩容 
StringBuilder sbuilder3 = new StringBuilder();
for (int i = 0; i < 17; i++) {
	sbuilder3.append(8);
} 
System.out.println("包含的字符串长度:" + sbuilder3.length()); 
System.out.println("字符串缓冲区容量:" + sbuilder3.capacity());

输出结果:

包含的字符串长度:0 
字符串缓冲区容量:16 
包含的字符串长度:5 
字符串缓冲区容量:21 
包含的字符串长度:17 
字符串缓冲区容量:34

(2)字符串追加

StringBuilder在提供了很多修改字符串缓冲区的方法,追加、插入、删除和替换等。
字符串追加方法是append,append有很多重载方法,可以追加任何类型数据,它的返回 值还是StringBuilder。StringBuilder的追加法与StringBuffer完全一样。

字符串追加示例代码如下:

//添加字符串、字符 
StringBuilder sbuilder1 = new StringBuilder("Hello"); 
sbuilder1.append(" ").append("World"); 
sbuilder1.append('.'); 
System.out.println(sbuilder1);

StringBuilder sbuilder2 = new StringBuilder(); 
Object obj = null; 
//添加布尔值、转义符和空对象 
sbuilder2.append(false).append('\t').append(obj); 
System.out.println(sbuilder2);

//添加数值 
StringBuilder sbuilder3 = new StringBuilder();
for (int i = 0; i < 10; i++) {
	sbuilder3.append(i);
} 
System.out.println(sbuilder3);

运行结果:

Hello World. 
false null 
0123456789

(3)字符串插入、删除和替换

StringBuilder中实现插入、删除和替换等操作的常用方法说明如下:

  • StringBuilder insert(int offset, String str):在字符串缓冲区中索引为offset的字符位置之前插入str, insert有很多重载方法,可以插入任何类型数据。
  • StringBuffer delete(int start, int end):在字符串缓冲区中删除子字符串,要删除的子字符串从指定 索引start开始直到索引end - 1处的字符。start和end两个参数与substring(int beginIndex, int endIndex)方法中的两个参数含义一样。
  • StringBuffer replace(int start, int end, String str)字符串缓冲区中用str替换子字符串,子字符串从指 定索引start开始直到索引end - 1处的字符。start和end同delete(int start, int end)方法。

StringBuffer也完全一样

示例代码如下:

// 原始不可变字符串 
String str1 = "Java C"; 
// 从不可变的字符创建可变字符串对象 
StringBuilder mstr = new StringBuilder(str1);

// 插入字符串 
mstr.insert(4, " C++"); 
System.out.println(mstr);

// 具有追加效果的插入字符串 
mstr.insert(mstr.length(), " Objective-C"); 
System.out.println(mstr);

// 追加字符串 
mstr.append(" and Swift"); 
System.out.println(mstr);

// 删除字符串 
mstr.delete(11, 23); 
System.out.println(mstr);

输出结果:

Java C++ C 
Java C++ C Objective-C 
Java C++ C Objective-C and Swift 
Java C++ C and Swift