目录
- 一、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是不同的引用,指向不同的对象。
图解:
(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