文章目录
- 一、String 类
- 1 String 类的介绍
- 2 String 对象创建的两种方式
- 3 测试题加深理解
- (1) 例题一
- (2)例题二
- (3) 例题三
- 4 String 类的常用方法
- (1)`equals()`
- (2)`equalsIgnoreCase()`
- (3)`length()`
- (4)`indexOf()`
- (5)`lastIndexOf()`
- (6)`substring()`
- (7)`trim()`
- (8)`charAt()`
- (9)`toUpperCase()`
- (10)`toLowerCase()`
- (11)`concat()`
- (12)`replace()`
- (13)`split()`
- (14)`compareTo()`
- 1. 逐字符比较:
- 2. 长度比较:
- 总结:
- (15)`toCharArray()`
- (16)`format()`
- 二、StringBuffer 类
- 1 基本介绍
- 2 String 和 StringBuffer 相互转换
- 3 StringBuffer 类的常用方法
- (1)增: `append()`
- (2)删: `delete()`
- (3)`replace()`
- (4)`indexOf()`
- (5)`insert()`
- (6)`length()`
- 三、StringBuilder 类
- 1 基本介绍
- 2 StringBuilder 的方法和 StringBuffer 是一样的。
- 3 String、StringBuffer 和 StringBuilder 的比较
- 四 、String、StringBuffer 和 StringBuilder 的选择
一、String 类
1 String 类的介绍
- String 对象用于保存字符串,也就是一组字符序列。
- 字符串常量对象是用双引号括起来的字符序列。例如:“这是字符串” 、“1234” 等。
- 字符串的字符使用 Unicode 字符编码,一个字符(不区分字母还是汉字)占两个字节。
- String 类较常用构造器:
String s1 = new String();
String s2 = new String(String original);
String s3 = new String(char[] a);
String s4 = new String(char[] a, int startIndex, int count);
- String 类的继承和实现关系:
- CharSequence 接口:表示一系列可读的 char 值。它提供了对多种不同类型的 char 序列的统一、只读访问。这个接口的主要实现类包括 CharBuffer、String、StringBuffer 和 StringBuilder
- Comparable 接口:在Java编程中,Comparable接口是实现对象排序的一种方式。它定义了对象的自然顺序,使得对象可以根据其内在特征进行排序。例如,整数可以按大小排序,字符串可以按字典顺序排序。Comparable接口通过其compareTo方法,允许对象自行决定如何与其他对象比较。
- Serializable 接口:一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象才能被序列化。简单理解就是,可以在网络传输。
- String 类是 final 类,不能被其他的类继承。
- String 类有属性
private final char value[];
用于存放字符串的内容,也就是说,字符串的内容实际上是存放在 value 数组中的。 private final char value[];
:可以看到,value 数组是 final 修饰的。在java中,当final
修饰数组时,数组的引用不能被重新赋值,但数组的元素可以被修改。
public final char arr[] = {'1', '2', '3'};
char[] arr2 = {'1'};
//数组的引用不能重新赋值
//即arr不能再指向其他数组
//arr = arr2 这样就是错误的
//数组的元素可以被修改
arr[0] = '8';//这个是被允许的
2 String 对象创建的两种方式
- 直接将字符串赋值给 String 类型:
String str1 = "xiong";
- 调用构造器:
String str2 = new String("xiong");
两种方式的区别:
- 方式一:先从常量池查看是否有 “xiong” 数据空间,如果有,直接指向常量池的对象;如果没有,则在常量池中创建 “xiong" 对象,然后指向,str1 最终指向的是常量池的空间地址。
- 方式二:先在堆中创建空间,里面维护了 value 属性,指向常量池存储 ”xiong“ 的空间。如果常量池没有 “xiong” ,则会在常量池中进行创建,再让 value 指向;如果有,则 value 直接指向。
String str1 = "xiong";
String str2 = new String("xiong");
(1)创建 str1 时,首先检查常量池中是否已经存在 “xiong” ,如果不存在,就创建然后再让 str1 指向常量池中存储 “xiong” 的地址空间。如果存在,则直接指向。
(2)创建 str2 时,先在堆区创建一个 String 对象,该对象有属性 value 数组,value 数组首先检查常量池中是否已经存在 “xiong” ,如果不存在,就创建然后再让 value 指向常量池中存储 “xiong” 的地址空间。如果存在,则直接指向。
3 测试题加深理解
(1) 例题一
String a = "xiong" + "da";
//创建了几个对象
解答: 创建了一个对象,在 使用 + 进行拼接时,编译器会进行一个优化,即上式等价于 String a = "xiongda";
执行。
(2)例题二
String a = "xiong";
String b = "da";
String c = a + b;
//创建了几个对象
我们使用 idea 进行debug 调试,看看 String c = a + b 是如何执行的:
首先,会进入 StringBuilder 类中的无参构造器中:
构造器执行完之后,将进入 append 方法中,通过 super.append(str)
先将 “xiong” 添加进 StringBuilder 对象中,然后返回。
“xiong” 完成添加之后,接着添加 “da”。
然后调用 StringBuilder 的 toString() 方法,返回一个字符串 “xiongda”。
(3) 例题三
首先介绍一下 String 中的 intern() 方法:简单来说就是,如果有 String s = new String("xiong");
如果执行 s.intern()
,那么如果常量池中已经存在 “xiong”,就会返回常量池存储 “xiong” 的空间地址,如果不存在,就会在常量池中创建,然后再返回。
//下面的代码输出什么
String s1 = "xiong";
String s2 = "da";
String s3 = "xiongda";
String s4 = (s1 + s2).intern();
System.out.println(s3 == s4);//输出 true
System.out.println(s3.equals(s4));//输出true
4 String 类的常用方法
(1)equals()
区分大小写,判断字符串的内容是否相等。
使用:
String a = "xiong";
String b = new String("xiong");
System.out.println(a.equals(b));//true
//equals()方法比较的是字符串的内容是否相同
(2)equalsIgnoreCase()
忽略大小写,判断字符串的内容是否相同。
字符串是区分大小写的,比如 abc
和 Abc
这两个就是不同的字符串。但是,如果使用 equalsIgnoreCase()
方法进行比较这两个就是相等的。
String a = "abc";
String b = new String("Abc");
System.out.println(a.equalsIgnoreCase(b));//true
//equalsIgnoreCase()方法
//忽略大小写比较字符串的内容是否相同
(3)length()
获取字符的个数,字符串的长度。
String a = "123456";
System.out.println(a.length());//6
//字符串有6个字符,长度为6
(4)indexOf()
public int indexOf(int ch)
获取字符在字符串中第一次出现的索引,索引从0开始,如果找不到,返回 -1。
String a = "abcdefg";
int n = a.indexOf('c');
//n = 2,索引从0开始,c 的索引为2
-
public int indexOf(int ch, int formIndex)
返回指定字符在此字符串中第一次出现的索引,从指定索引处(formIndex)开始搜索。
String a = "123123";
//从索引为1的位置开始,获取字符在字符串中第一次出现的索引
int n = a.indexOf('1', 1);
// n = 3;从索引1开始查找,出现的第一个1的索引为3
(5)lastIndexOf()
(6)substring()
public String substring(int beginIndex)
-
public String substring(int beginIndex, int endIndex)
截取范围为: [ b e g i n I n d e x , e n d I n d e x ) [beginIndex, endIndex) [beginIndex,endIndex)
(7)trim()
去除前后空格。
String a = " abc ";
a = a.trim();// a="abc"
//去除了前后空格
(8)charAt()
返回指定索引处的 char 值。
String a = "abcd";
char ch = a.charAt(2);
// ch = c; 字符串索引为2的字符为c
(9)toUpperCase()
将 String 中的所有字符转换为大写。
String a = "aBcdef";
a = a.toUpperCase();
// a = "ABCDEF";
(10)toLowerCase()
将 String 中的所有字符转换为小写。
String a = "ABCdef";
a = a.toLowerCase();
// a = "abcdef";
(11)concat()
将指定的字符串连接到此字符串的末尾。
String a = "ab";
a = a.concat("c").concat("d");
// a = "abcd"
(12)replace()
String a = "123123";
a = a.replace('2', '6');
// a = "163163"
(13)split()
分割字符串,对于某些分割字符,我们需要转义,比如 |、\\
等。
String a = "My name is xiong";
String[] str = a.split(" ");
// str = {"My", "name", "is", "xiong"}
(14)compareTo()
这个直接看源码:
public int compareTo(String anotherString) {
// 获取当前字符串的长度
int len1 = value.length;
// 获取另一个字符串的长度
int len2 = anotherString.value.length;
// 计算两个字符串的最小长度,用于后续逐字符比较
int lim = Math.min(len1, len2);
// 将当前字符串的字符数组赋值给v1
char v1[] = value;
// 将另一个字符串的字符数组赋值给v2
char v2[] = anotherString.value;
// 初始化索引变量k,用于遍历字符数组
int k = 0;
// 遍历两个字符串的字符数组,直到最小长度lim
while (k < lim) {
// 获取当前索引位置的字符
char c1 = v1[k];
char c2 = v2[k];
// 如果两个字符不相等
if (c1 != c2) {
// 返回两个字符的ASCII值差,表示字符串的字典序比较结果
return c1 - c2;
}
// 如果字符相等,继续比较下一个字符
k++;
}
// 如果所有字符都相等,返回两个字符串长度的差值
// 如果长度相等,返回0,表示两个字符串相等
// 如果当前字符串更长,返回正数,表示当前字符串大于另一个字符串
// 如果当前字符串更短,返回负数,表示当前字符串小于另一个字符串
return len1 - len2;
}
1. 逐字符比较:
- 从两个字符串的起始位置开始,逐字符比较。
- 如果在某个位置发现字符不相等,则直接返回这两个字符的ASCII值差(
c1 - c2
),表示字符串的字典序关系。 - 如果字符相等,则继续比较下一个字符,直到遍历完较短字符串的长度(
lim
)。
2. 长度比较:
- 如果所有字符都相等(即遍历完较短字符串后仍未分出胜负),则比较两个字符串的长度。
- 如果当前字符串更长,返回正数;如果更短,返回负数;如果长度相等,返回0,表示两个字符串完全相等。
总结:
这段代码通过逐字符比较和长度比较,实现了字符串的字典序比较,返回值表示两个字符串的大小关系:
- 负数:当前字符串小于另一个字符串。
- 正数:当前字符串大于另一个字符串。
- 0:两个字符串相等。
(15)toCharArray()
String a = "abcd";
char[] ch = a.toCharArray();
// ch = {'a', 'b', 'c', 'd'}
(16)format()
格式化字符串:
%s
:字符串%c
:字符%d
:整型%.2f
:浮点型,四舍五入保留2位小数。
//1. %s , %d , %.2f , %c 称为占位符
//2. 这些占位符由后面变量来替换
//3. %s 表示后面由字符串来替换
//4. %d 是整数来替换
//5. %.2f 表示使用小数来替换,替换后,只会保留小数点两位, 并且进行四舍五入的处理
//6. %c 使用char 类型来替换
String formatStr = "我的姓名是%s 年龄是%d,成绩是%.2f 性别是%c.希望大家喜欢我!";
String info2 = String.format(formatStr, "xiong", 18, 99.9, '男');
System.out.println("info2=" + info2);
二、StringBuffer 类
1 基本介绍
-
java.lang.StringBuffer 代表可变的字符序列,可以对字符串内容进行增删。
-
很多方法与 String 相同,但 StringBuffer 是可变长度的。
-
StringBuffer 是一个容器。
-
StringBuffer 是 final 类,不能被继承。
-
StringBuffer 继承了抽象类 AbstractStringBuilder。
- AbstractStringBuilder 拥有 value 字符数组,StringBuffer 的字符串内容就存放在这个数组。
- StringBuffer 的构造器。
其中,字符串缓冲区就是 value 字符数组。
-
StringBuffer(String str)
使用这个构造器时,value 的大小为
str.length() + 16
2 String 和 StringBuffer 相互转换
在使用中,我们经常需要将 String 和 StringBuffer 进行转换,以下是转换的方式:
public class Test {
public static void main(String[] args) {
String str = "xiong";
// Sting -> StringBuffer
//1.使用构造器
StringBuffer s1 = new StringBuffer(str);
//2.使用 append()方法
StringBuffer s2 = new StringBuffer();
s2 = s2.append(str);
// StringBuffer -> String
StringBuffer s3 = new StringBuffer("da");
//1.使用 StringBuffer 的toString()方法
String str1 = s3.toString();
//2.使用构造器
String str2 = new String(s3);
}
}
3 StringBuffer 类的常用方法
StringBuffer s = new StringBuffer("xiong");
(1)增: append()
s.append(",");
s.append("da");
// s = "xiong,da"
注意: 有很多重载的 append() 方法。具体查询可以通过 jdk 文档。
(2)删: delete()
StringBuffer str = new StringBuffer("xiong");
str.delete(1,3);// 删除索引为 1,2 的字符
// str = "xng"
(3)replace()
(4)indexOf()
(5)insert()
插入:
StringBuffer str = new StringBuffer("feimu");
//在索引为3的位置插入 "-"
str.insert(3,"-");
// str="fei-mu"
注意: StringBuffer 类中有很多重载的 append() 方法,有需要可以通过查阅官方文档。
(6)length()
三、StringBuilder 类
1 基本介绍
-
一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API,但不保证同步(StringBuilder 不是线程安全的)。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可以,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。
-
在 StringBuilder 上的主要操作是 append() 方法和 insert() 方法,可重载这些方法,以接受任意类型的数据。
-
StringBuffer 继承了抽象类 AbstractStringBuilder。
- AbstractStringBuilder 拥有 value 字符数组,StringBuffer 的字符串内容就存放在这个数组。
2 StringBuilder 的方法和 StringBuffer 是一样的。
3 String、StringBuffer 和 StringBuilder 的比较
- StringBuilder 和 StringBuffer 非常类似,均代表可变的字符序列,而且方法也一样。
- String :不可变字符序列,效率低,但是复用率高。
- StringBuffer :可变字符序列,效率较高(增删)、线程安全。
- StringBuilder :可变字符序列、效率最高、线程不安全。
- String 使用注意事项:
String s = "abc";
//实际上原来的"abc"对象会被丢弃
//又产生一个字符串对象 "abcd"
s = s + "d";
//如果多次执行这样的操作,会导致大量的副本字符串对象留存在内存中
//降低效率
四 、String、StringBuffer 和 StringBuilder 的选择
使用的原则:
- 如果字符串存在大量的修改操作,一般使用 StringBuffer 或 StringBuilder
- 如果字符串存在大量的修改操作,并且在单线程的情况,使用 StringBuilder
- 如果字符串存在大量的修改操作,并在多线程的情况,使用 StringBuffer
- 如果我们的字符串很少修改,被多个对象引用,使用 String,比如配置信息等。