前言
在C语言中已经涉及到字符串了,但是在C语言中要表示字符串只能使用字符数组或者字符指针,可以使用标准库提供的字符串系列函数完成大部分操作,但是这种将数据和操作数据方法分离开的方式不符合面向对象的思想,而字符串应用又非常广泛,因此Java语言专门提供了String
类
String类
一、常用方法
(1)字符串构造
- 区别于C语言,Java中字符串没有以
\0
结尾的说法
String
类提供的构造方式非常多,常用的就以下三种
public class demo1 {
public static void main(String[] args) {
//使用常量串构造->直接赋值
String s1 = "hello";
System.out.println(s1);
//直接new String对象->本质
String s2 = new String("hello");
System.out.println(s2);
//使用字符数组进行构造
char[] array = {'h','e','l','l','o'};
String s3 = new String(array);
System.out.println(s3);
}
}
//输出结果
hello
hello
hello
Process finished with exit code 0
- 【注意】
String
是引用类型,内部并不存储字符串本身,在jdk1.8中,字符串实际保存在char
类型的数组中
(2)获取字符串长度和判断空串
在Java中,我们获取字符串长度非常简单,只需使用
xxx.length()
即可;还有一种方法可以判断字符串是不是空的,使用xxx.isEmpty
,如果字符串长度为0,返回ture,否则返回flase。代码理解如下
String a1 = new String("hello");
String a2 = new String("world");
String a3 = new String("");
System.out.println(a1.length());
System.out.println(a2.isEmpty());
System.out.println(a3.isEmpty());
//输出结果
5
false
true
Process finished with exit code 0
- 【注意】一个字符串等于
null
代表字符串不指向任何对象,并不代表字符串为空,只有字符串中什么也不写才为空!
(3)String
对象的比较
1.对象引用的理解
在此代码中,b1和b2引用的是不同对象,b1和b3引用的是同一对象
String b1 = new String("hello");
String b2 = new String("world");
String b3 = b1;
这是为什么呢?让我们一起来了解它们的存储吧!
- 只要
new
一个对象就会在堆区开辟一块空间
2. ==
比较是否引用同一个对象
<1>普通情况
对于内置类型,“= =“比较的是变量中的值;对于引用类型”= =“比较的是引用中的地址
//基本类型
int a = 10;
int b = 20;
int c = 10;
System.out.println(a == b);//false
System.out.println(a == c);//ture
//引用类型
String c1 = new String("hello");
String c2 = new String("world");
String c3 = new String("hello");
String c4 = c2;
System.out.println(c1 == c3);//false
System.out.println(c2 == c3);//false
System.out.println(c2 == c4);//ture
//输出结果
false
true
false
false
true
Process finished with exit code 0
- 任何情况下只要等号两边是引用类型一定要注意看比较的是什么?如果要比较两个引用所指向的内容是否一致,一定要重写
equals
方法,因为不重写就会默认调用object
的equals
方法(比较的是地址)。我们建议自定义类型一定要重写hashcode
和equals
方法
<2>特殊情况
在如下情况下,比较的结果为
true
,这是为什么呢?
String d1 = "abcde";
String d2 = "abcde";
System.out.println(d1 == d2);
//输出结果
true
Process finished with exit code 0
这就涉及到了常量池的原理,在这里我们简单介绍
常量池的作用:提高存储效率,避免反复存储相同数据
常量池的原理
- 存储双引号引起来的字符串,存的是字符串的常量值
- 先看常量池是否存在当前字符串
- 如果没有则存进去
- 如果有则获取当前这个字符串在常量池中的地址
3. boolean equals(Object anObject)
方法:按照字典序比较
字典序:字符大小的顺序
String
类重写了父类Object中equals
方法,Object
中equals
默认按照==比较,String
重写equals
方法后,按照如下规则进行比较,比如:s1.equals(s2)
equals
比较的是String
对象中的逐个字符,只要里面放的内容相同,就返回true
内部原理
public boolean equals(Object anObject) {
// 1. 先检测this 和 anObject 是否为同一个对象比较,如果是返回true
if (this == anObject) {
return true;
}
// 2. 检测anObject是否为String类型的对象,如果是继续比较,否则返回false
if (anObject instanceof String) {
// 将anObject向下转型为String类型对象
String anotherString = (String)anObject;
int n = value.length;
// 3. this和anObject两个字符串的长度是否相同,是继续比较,否则返回 false
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
// 4. 按照字典序,从前往后逐个字符进行比较
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
==
与equals
的比较
String e1 = new String("hello");
String e2 = new String("hello");
String e3 = new String("hi");
//s1,s2,s3引用的是三个不同对象,因此==比较的结果全部为false
System.out.println(e1 ==e2);//false
System.out.println(e1 ==e3);//false
//equals比较的是String中的逐个字符,里面放置的内容相同,返回true
System.out.println(e1.equals(e2));//true
System.out.println(e1.equals(e3));//false
//输出结果
false
false
true
false
Process finished with exit code 0
3.int comepareTo(String s)
方法:按照字典序进行比较
与equals
不同的是,equals
返回的是boolean
类型,而compareTo
返回的是int
类型,具体的比较方式为:先按照字典次序大小比较,如果出现不等的字符,直接返回这两个字符的大小差值,如果前k个字符相等(k为两个字符长度最小值),返回两个字符串长度差值
比较逻辑
- 如果两个字符串长度是一样的,那么第一个不一样的字符大小就是整个字符串的大小
- 如果两个字符串长度不一样那么先比较两个长度的差值,在这个差值范围内,有不一样字符就能够比较出大小,如果是一样的,那么谁长谁就大
代码理解
String f1 = new String("abc");
String f2 = new String("ac");
String f3 = new String("abc");
String f4 = new String("abcdef");
System.out.println(f1.compareTo(f2));//不同输出字符差值-1
System.out.println(f1.compareTo(f3));//相同输出0
System.out.println(f1.compareTo(f4));//前k个字符完全相同,输出长度差值-3
//输出结果
-1
0
-3
Process finished with exit code 0
4.int comepareToIgnoreCase(String str)
方法
与int comepareTo
方式相同,但是忽略大小写比较
代码理解
String f1 = new String("abc");
String f2 = new String("ac");
String f3 = new String("ABc");
String f4 = new String("abcdef");
System.out.println(f1.compareToIgnoreCase(f2));//不同输出字符差值-1
System.out.println(f1.compareToIgnoreCase(f3));//相同输出0
System.out.println(f1.compareToIgnoreCase(f4));//前k个字符完全相同,输出长度差值-3
//输出结果
-1
0
-3
Process finished with exit code 0
(4)字符串的查找
字符串查找也是字符串中非常常见的操作,String类提供的常用查找方法:
char charAt(int index)
:返回index位置上字符,如果index为负数或者越界,抛出IndexOutOfBoundsException
异常int indexOf(int ch)
:返回ch第一次出现的位置,没有返回-1int indexOf(int ch, int fromIndex)
:从fromIndex位置开始找ch第一次出现的位置,没有返回-1int indexOf(String str)
:返回str第一次出现的位置,没有返回-1int indexOf(String str, int fromIndex)
:从fromIndex位置开始找str第一次出现的位置,没有返回-1int lastIndexOf(int ch)
:从后往前找,返回ch第一次出现的位置,没有返回-1int lastIndexOf(int ch, int fromIndex)
:从fromIndex位置开始找,从后往前找ch第一次出现的位置,没有返回-1int lastIndexOf(String str)
:从后往前找,返回str第一次出现的位置,没有返回-1int lastIndexOf(String str, int fromIndex)
:从fromIndex位置开始找,从后往前找str第一次出现的位置,没有返回-1
用语言理解看起来很难懂,但是用代码理解会非常简单
String g1 = "aaabbbcccaaabbbccc";
System.out.println(g1.charAt(3));//'b'获取指定位置的字符
System.out.println(g1.indexOf('c'));//6 获取指定字符出现的位置
System.out.println(g1.indexOf('c',10));//15
System.out.println(g1.indexOf("bbb"));//3
System.out.println(g1.indexOf("bbb",10));//12
System.out.println(g1.lastIndexOf('c'));//17
System.out.println(g1.lastIndexOf('c',10));//8
System.out.println(g1.lastIndexOf("bbb"));//12
System.out.println(g1.lastIndexOf("bbb",10));//3
//输出结果
b
6
15
3
12
17
8
12
3
Process finished with exit code 0
注意:上述方法都是实例方法
(5)字符串的转化
1.数值和字符串转化valueOf
方法
//数字转字符串
String h1 = String.valueOf(1234);
String h2 = String.valueOf(12.34);
String h3 = String.valueOf(true);
String h4 = String.valueOf(new student("lili",10));
System.out.println(h1);
System.out.println(h2);
System.out.println(h3);
System.out.println(h4);
//字符串转数字,注意,Integer、Double等是Java中的包装类型
int k1 = Integer.parseInt("1234");
double k2 = Double.parseDouble("12.34");
System.out.println(k1);
System.out.println(k2);
//输出结果
1234
12.34
true
student{name='lili', age=10}
1234
12.34
Process finished with exit code 0
2.大小写转化
在Java中,String是不可变的,hello->HELLO这个转换不是在原来的字符串上进行改变,它是生成了一个新的对象
String m1 = "hello";
String m2 = "HELLO";
//小写转大写
System.out.println(m1.toUpperCase());
//大写转小写
System.out.println(m2.toLowerCase());
//输出结果
HELLO
hello
Process finished with exit code 0
3.字符串转数组(常用)
String j1 = "hello";
//字符串转数组
char[] ch = j1.toCharArray();
for (int i = 0; i < j1.length(); i++) {
System.out.println(ch[i]);
}
System.out.println();
//数组转字符串
String j2 = new String(ch);
System.out.println(j2);
//输出结果
h
e
l
l
o
hello
Process finished with exit code 0
4.格式化
String p = String.format("%d-%d-%d",2025,2,18);
System.out.println(p);
//运行结果
2025-2-18
Process finished with exit code 0
(6)字符串的替换
使用一个指定的新的字符串替换掉已有的字符串数据,可用的方法如下
String replaceAll(String regex, String replacement)
:替换所有的指定内容String replaceFirst(String regex, String replacement)
:替换首个内容
由于字符串是不可变对象,替换不修改当前字符串,而是产生一个新的字符串
String r1 = "helloworld";
System.out.println(r1.replaceAll("l","ooooooo"));
System.out.println(r1.replaceFirst("l","ooooooooo"));
//输出结果
heoooooooooooooooworoooooood
heoooooooooloworld
Process finished with exit code 0
(7)字符串的拆分
可以将一个完整的字符串按照指定的分隔符划分为若干个子字符串,可用方法如下
String[] split(String regex)
:将字符串全部拆分,分割之后的结果要存储到数组当中String[] split(String regex, int limit)
:将字符串以指定的格式,拆分为limit组
代码示例:实现字符串的拆分处理
String q1 = "hello world hello everyone";
String[] result = q1.split(" ");
for(String q2 : result){
System.out.println(q2);
}
//输出结果
hello
world
hello
everyone
Process finished with exit code 0
代码示例:字符串的部分拆分
String q3 = "hello world hello everyone";
String[] result1 = q3.split(" ",2);
for(String q4 : result1){
System.out.println(q4);
}
//输出结果
hello
world hello everyone
Process finished with exit code 0
- 拆分是特别常用的操作,一定要重点掌握,另外有些特殊字符作为分隔符可能无法正确切分,需要加上转义字符
代码示例:拆分IP地址
String q5 = "192.168.1.2";
String[] result2 = q5.split("\\.");
for (String q6 : result2){
System.out.println(q6);
}
//输出结果
192
168
1
2
Process finished with exit code 0
注意事项:
- 字符”|”,“*”,“+”都得加上转义字符,前面加上“\”
- 而如果是”\“,那么就得写成”\\“
- 如果一个字符串中有多个分隔符,可以用”|“作为连字符
代码示例:多次拆分
String q8 = "name=zhangsan&age=18";
String[] result4 = q8.split("&|=");
for (String q9 : result4) {
System.out.println(q9);
}
//输出结果
name
zhangsan
age
18
Process finished with exit code 0
(8)字符串的截取
从一个完整的字符串之中截取出部分内容,可用方法如下
String substring(int beginIndex)
:从指定索引截取到结尾String substring(int beginIndex, int endIndex)
:截取部分内容
代码示例:观察字符串的截取
String str = "helloworld";
System.out.println(str.substring(5));
System.out.println("===============");
System.out.println(str.substring(0,5));
//运行结果
world
===============
hello
Process finished with exit code 0
注意事项
- 这里如果传入的是0下标,那么默认返回的是原来的对象,但是如果传入的是其他对象,此时返回新的对象
- 索引从0开始
- 注意左闭右开的写法
(9)字符串去除空格
这是一个很实用的方法,trim
方法会去掉字符串开头和结尾的空白字符,空格,换行,制表符等,保留中间空格
String str2 = " hello world";
System.out.println(str2.trim());
//输出结果
hello world
Process finished with exit code 0
(10)字符串的不可变性
String
是一种不可变对象,字符串中的内容是不可改变,字符串不可被修改,是因为:
String
类在设计时就是不可改变的,String
类实现描述中已经说明了String
类中的字符实际保存在内部维护的value
字符数组中String
类被final修饰,表明该类不能被继承value
被final
修饰,表明value
自身的值不能改变,即不能使用其他字符数组,但是引用空间中的内容可以修改,value的指向不能改变- 有涉及到可能修改字符串内容的操作都是创建一个新对象,改变的是新对象
final
修饰类表明该类不想被继承,final
修饰引用类型表明该引用变量不能引用其他对象,但是其引用对象中的内容是可以修改的
(11)字符串的修改
注意:尽量避免直接对String类型对象进行修改,因为String
类是不能修改的,所有的修改都会创建新对象,效率非常低下
String ret3 = "hello";
ret3 += "world";
System.out.println(ret3);
这种方式不推荐使用,因为其效率非常低,中间创建了好多临时变量
我们来看一下运行速度
public class demo2 {
public static void main(String[] args) {
long start = System.currentTimeMillis();
String s = "";
for(int i = 0; i < 10000; ++i){
s += i;
}
long end = System.currentTimeMillis();
System.out.println(end - start);
start = System.currentTimeMillis();
StringBuffer sbf = new StringBuffer("");
for(int i = 0; i < 10000; ++i){
sbf.append(i);
}
end = System.currentTimeMillis();
System.out.println(end - start);
start = System.currentTimeMillis();
StringBuilder sbd = new StringBuilder();
for(int i = 0; i < 10000; ++i){
sbd.append(i);
}
end = System.currentTimeMillis();
System.out.println(end - start);
}
}
//输出结果
24
1
0
Process finished with exit code 0
我们可以看到在对String
类进行修改时效率是非常慢的,因此:尽量避免对String
的直接修改,如果要修改尽量使用StringBuffer
或者StringBuilder
二、StringBuffer
和StringBuilder
由于String的不可更改特性,为了方便字符串的修改,Java中又提供StringBuilder
和StringBuffer
类。这两个类大部分功能是相同的,这里介绍 StringBuilder
常用的一些方法
(1) StringBuff append(String str)
尾部追加
在尾部追加,相当于String的+=,可以追加:boolean、char、char[]、double、float、int、long、Object、String、StringBuff的变量
StringBuilder sb1 = new StringBuilder("hello");
StringBuilder sb2 = sb1;
sb1.append(" ");
sb1.append("world");
sb1.append(" ");
sb1.append(123);
System.out.println(sb1);//hello world 123
System.out.println(sb2);//hello world 123
System.out.println(sb1 == sb2);//true
//输出结果
hello world 123
hello world 123
true
Process finished with exit code 0
(2)StringBuffer reverse()
字符串逆转
StringBuilder sb3 = new StringBuilder("hello");
System.out.println(sb3.reverse());
//输出结果
olleh
Process finished with exit code 0
(3)方法大汇总
public class demo3 {
public static void main(String[] args) {
StringBuilder sb1 = new StringBuilder("hello");
StringBuilder sb2 = sb1;
// 追加:即尾插-->字符、字符串、整形数字
sb1.append(' '); // hello
sb1.append("world"); // hello world
sb1.append(123); // hello world123
System.out.println(sb1); // hello world123
System.out.println(sb1 == sb2); // true
System.out.println(sb1.charAt(0)); // 获取0号位上的字符 h
System.out.println(sb1.length()); // 获取字符串的有效长度14
System.out.println(sb1.capacity()); // 获取底层数组的总大小
sb1.setCharAt(0, 'H'); // 设置任意位置的字符 Hello world123
sb1.insert(0, "Hello world!!!"); // Hello world!!!Hello world123
System.out.println(sb1);
System.out.println(sb1.indexOf("Hello")); // 获取Hello第一次出现的位置
System.out.println(sb1.lastIndexOf("Hello")); // 获取
Hello最后一次出现的位置
sb1.deleteCharAt(0); // 删除首字符
sb1.delete(0,5); // 删除[0, 5)范围内的字符
String str = sb1.substring(0, 5); // 截取[0, 5)区间中的字符以String的方式返回
System.out.println(str);
sb1.reverse(); // 字符串逆转
str = sb1.toString(); // 将StringBuffer以String的方式返回
System.out.println(str);
}
}
//输出结果
hello world123
true
h
14
21
Hello world!!!Hello world123
0
14
world
321dlrow olleH!!!dlrow
Process finished with exit code 0
从上述例子可以看出:String
和StringBuffer
最大的区别就在于String
的内容无法修改,而StringBuffer
的内容可以修改,频繁修改字符串的情况考虑使用StringBuffer
注意:String
和StringBuffer
类不能直接转换,如图想要相互转换可以采用以下原则:
String
变为StringBuffer
:利用StringBuffer
的构造方法或者append()
方法StringBuffer
变为String
:调用toString()
方法
StringBuilder
未采用同步处理,一般在普通情况下使用;StringBuffer
采用同步处理,一般在多线程情况下使用