目录
一、常用构造方法
二、String类源码
2.1 字符串的不可变性
2.2 String对象如何存储字符串?
2.3 StringTable字符串池
2.3.1 intern()方法
三、字符串对象比较
3.1 ==比较是否引用同一个对象
3.2 equals()方法
3.3 使用compareTo()方法
四、String类常用实用方法
4.1 获取字符串长度
4.2 字符串查找
4.3 字符串转化
4.3.1 数值与字符串的转化
4.3.2 大小写转化
4.3.3 字符串转数组
4.3.4 格式化
4.4 字符串替换
4.5 字符串拆分
4.6 字符串截取
五、StringBuilder和StringBuffer
六、String,StringBuilder,StringBuffer的区别
Java中提供了String类,用来构造字符串并且提供了很多操作字符串的方法,让我们处理字符串相关的问题更加方便。
一、常用构造方法
public class StringStructureDemo {
public static void main(String[] args) {
String str1 = "hello"; //字符串字面量构造
String str2 = new String("hello"); //使用构造方法构造
char []chars = {'h','e','l','l','o'};
String str3 = new String(chars); //使用字符数组构造
String str4 = new String(); //得到空字符串
//传递一个byte类型的数组,根据ASCII值确定字符
byte[] bytes = new byte[]{97,98,99,100};
String str5 = new String(bytes);
System.out.println(str5); //传递一个byte类型的数组
}
}
二、String类源码
String类被final修饰,表明String类不能被继承。被final修饰的变量(基本数据类型与引用数据类型)在初始化后,不能被重新赋值,所以value数组一旦被赋值变不能修改。同时,value被private所修饰,所以只能在String类当中使用。
2.1 字符串的不可变性
value数组被final与private修饰,不能指向新的数组对象并且不能被外部直接引用,原则上来说,value数组的内容是可以修改的,但是在String类内部并没有提供任何方法来改变value数组的内容,所以无法通过外部手段修改。
- final确保value引用在对象的生命周期内不会指向新的数组。
- String没有提供修改value数组内容的方法,因此,即使value数组内部可变,String类保持了不可变性。
2.2 String对象如何存储字符串?
我们知道String是引用类型,String声明的变量是引用变量,而引用变量存储的是所引用的对象的地址,所以String类型的变量并不是直接存储字符串本身,这一点要切记。
在Java8及之前的版本中,String类使用一个内部的char[]数组来存储字符数据。
public class StringCompareDemo {
public static void main(String[] args) {
String str1 = new String("hello");
String str2 = new String("world");
String str3 = str1;
}
}
在Java9之后:
value数组用来存储字符串内容,coder表示编码类型
2.3 StringTable字符串池
StringTable是 Java 中 JVM 内部用来存储字符串池的实现部分。字符串池是 Java 中的一个机制,用于存储所有使用字符串字面量创建的字符串对象。这样可以减少内存消耗并提高效率,因为相同的字符串字面量只会在内存中存储一次。
String str1 = "hello";
String str2 = "hello";
String str3 = "world";
String str4 = new String("hello");
上面代码中先声明变量str1,字符串字面量“hello” 存储在字符串池当中,str1指向字符串池中的字符串对象“hello”。在声明变量str2时,JVM会在StringTable中查找“hello”,如果存在就返回引用,对于上述代码,已经存在“hello”,所以不会在StringTable中创建“hello”,即str1和str2指向同一个字符串对象“hello”。声明变量str3,对于字符串字面量“world”在字符串池当中没有“world”,所以会在StringTable中创建该字符串并返回引用,使得str3指向“world”对象。对于str4,使用new关键字创建字符串会在堆上分配一个新的String对象,而不在StringTable中查找或存储,因此str4不会与str1与str2共享一个对象。
2.3.1 intern()方法
intern()方法会将字符串对象放入StringTable中。如果字符串已经存在于池中,它会返回池中字符串的引用,否则会将该字符串添加到池中并返回引用。
public class StringDome1 {
public static void main(String[] args) {
String str1 = "hello";
String str2 = new String("hello").intern();
String str3 = new String("hello");
System.out.println(str1==str2); //true
System.out.println(str1==str3); //false
}
}
尽量使用字符串字面量:可以让JVM自动使用字符串池,避免重复创建相同内容的字符串对象。节省了内存并且提高了性能。
三、字符串对象比较
3.1 ==比较是否引用同一个对象
public class StringCompareDome1 {
public static void main(String[] args) {
String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
String str4 = str2;
System.out.println(str1==str2); //true
System.out.println(str1==str3); //false
System.out.println(str1==str4); //true
}
}
3.2 equals()方法
String类重写了父类Object的equals()方法,对于Object类的equals方法判断的还是两个引用是否引用同一个对象,而String类的equals()方法判断的是两个对象的内容是否相同,当然如果两个引用指向同一个对象,那么内容自然也相同了。
Object类的equals()方法:
String类的equals()方法:
public class StringCompareDome2 {
public static void main(String[] args) {
String str1 = "hello";
String str2 = new String("hello");
String str3 = new String("HEllo");
System.out.println(str1==str2); //false
System.out.println(str1.equals(str2)); //true
System.out.println(str1.equalsIgnoreCase(str3)); //忽略大小写 ture
}
}
3.3 使用compareTo()方法
String类实现了Comparable接口,可以使用compareTo()方法,用于按字典序顺序比较两个字符串。根据返回值判断大小:
- 返回0表示两个字符串相等。
- 返回负数表示调用者字符串小于参数字符串。
- 返回正数表示调用者字符串大于参数字符串。
比较规则:先根据字典次序大小比较,如果出现不等的字符,直接返回这两个字符的大小差值;如果前面n个字符相等(n为两个字符串中长度小的字符串的长度),返回值为两个字符串长度差值。注意一个点:是谁为调用者谁为参数,两者互换得到的结果不同。
public class StringCompareDome3 {
public static void main(String[] args) {
String str1 = "adc";
String str2 = "abc";
System.out.println(str1.compareTo(str2)); //ret:2
String str3 = "abcasd";
System.out.println(str2.compareTo(str3)); //ret:-3
String str4 = "ABCasD";
//忽略大小写比较
System.out.println(str3.compareToIgnoreCase(str4));//ret:0
}
}
四、String类常用实用方法
4.1 获取字符串长度
String str = "hello";
System.out.println(str.length()); //5
int[] array = {1,2,3,4,5};
System.out.println(array.length); //5
/*
注意区分str.length()与array.length
都是为了获取长度
字符串调用的是方法,数组是属性
*/
4.2 字符串查找
char charAt()
int indexOf()
int lastIndexOf()
String str = "hello";
//char charAt(int index) 返回index上的字符
//index为负数或者越界,抛出IndexOutOfBoundsException异常
System.out.println(str.charAt(2)); //'l'
//int indexOf(int ch) 返回ch第一次出现的位置,没有返回-1
//ch是int类型,可以传字符也可以使用其Unicode值
System.out.println(str.indexOf('e')); //'e'/'101'输出1
//int indexOf(int ch, int fromIndex)
//从fromIndex位置开始找ch第一次出现的位置,没有返回-1
System.out.println(str.indexOf('l',3)); //'l'
//int indexOf(String str)
//返回str第一次出现的位置(字串中第一个字符的下标),没有返回-1
System.out.println(str.indexOf("ll")); //2
//int indexOf(String str, int fromIndex)
//从fromIndex位置开始找str第一次出现的位置,没有返回-1
System.out.println(str.indexOf("he", 1)); //-1
//int lastIndexOf(int ch)
//从后往前找,返回ch第一次出现的位置,没有返回-1
System.out.println(str.lastIndexOf('l')); //3
//int lastIndexOf(int ch,int fromIndex)
//从fromIndex位置开始找,从后往前找ch第一次出现的位置,没有返回-1
System.out.println(str.lastIndexOf('l', 2));//2
//int lastIndexOf(String str)
//从后往前找,返回str第一次出现的位置,没有返回-1
System.out.println(str.lastIndexOf("ll")); //2
//int lastIndexOf(String str, int fromIndex)
//从fromIndex位置开始找,从后往前找str第一次出现的位置,没有返回-1
System.out.println(str.lastIndexOf("ll", 2));//2
System.out.println(str.lastIndexOf("ll", 3));//2
4.3 字符串转化
4.3.1 数值与字符串的转化
String.valueOf(),String类的静态方法,可以直接通过类名去调用。将不同类型的值(如int、double、char、boolean、Object等)转换为String类型。字符串也可以转化为数字。
String str1 = String.valueOf(123);
String str2 = String.valueOf(false);
String str3 = String.valueOf(3.14);
String str4 = String.valueOf(new Student(18,"ls"));
int data1 = Integer.parseInt("123");
double data2 = Double.parseDouble("12.23");
4.3.2 大小写转化
toLowerCase()大写转化为小写
toUpperCase()小写转化为大写
String str1 = "HELLO";
String str2 = "world";
System.out.println(str1.toLowerCase());
System.out.println(str2.toUpperCase());
4.3.3 字符串转数组
toCharArray()
String str1 = "hello";
//字符串转数组
char[] chars = str1.toCharArray();
for (char ch:chars){
System.out.print(ch+" ");
}
System.out.println();
//数组转字符串
String str2 = new String(chars);
System.out.println(str2);
4.3.4 格式化
String.format()
String str = String.format("%d-%d-%d",2024,11,9);
System.out.println(str);
4.4 字符串替换
replace(char oldChar,char newChar) 替换字符串中的单个字符(全部替换)
replaceAll(String regex,String replacement) 替换字符串中的字串(全部字串)
replaceFirlt(String regex,String replacement) 替换字符串中第一个字串
String str1 = "How are you";
System.out.println(str1.replaceAll(" ","-"));
System.out.println(str1.replace('o','x'));
System.out.println(str1.replaceFirst("How", "Where"));
4.5 字符串拆分
String[] split(String regex) 将字符串全部拆分
String[] split(String regex, int limit) 将字符串以指定的格式,拆分为limit组
返回值为一个String类型的数组,将拆分的字符串存入字符数组中。
String str = "How are you";
String[] strings1 = str.split(" ");
for (String s:strings1){
System.out.println(s);
}
String[] strings2 = str.split(" ",2);
for (String s:strings2){
System.out.println(s);
}
4.6 字符串截取
System.out.println("hello world".substring(5));
System.out.println("hello world".substring(2, 6));
前面2.1讲到String是一种不可变对象,字符串的内容是不可改变的。对于上面这些方法,“改变”字符串实际上是返回一个新的String对象,而不是在原本的String对象上进行修改。
五、StringBuilder和StringBuffer
StringBuilder
和 StringBuffer
都是可变字符序列类,它们可以用于高效地修改字符串内容(例如追加、插入、删除等操作)。与不可变的String类不同,StringBuilder和StringBuffer对象可以在原始对象上进行修改,不会创建新的对象实例。两者所使用的方法类似:
StringBuilder stringBuilder = new StringBuilder("hello");
//将指定的字符串追加到当前序列末尾
System.out.println(stringBuilder.append(" world"));
//在指定位置插入字符串
System.out.println(stringBuilder.insert(5, "test"));
//删除从start到end位置的字符
System.out.println(stringBuilder.delete(0, 5));
//将字符序列反转
System.out.println(stringBuilder.reverse());
//用指定字符串替换指定范围内的字符
System.out.println(stringBuilder.replace(1, 4, "se"));
//将字符序列转换为String
stringBuilder.toString();
StringBuffer
:线程安全,因为它的所有方法都被synchronized修饰,确保了多线程环境下的同步访问。适用于多线程环境,适合在需要线程安全的情况下使用。
StringBuilder
:非线程安全,没有同步机制,因此在单线程环境下执行速度比StringBuffer快。适用于单线程环境,推荐在不涉及多线程的情况下使用,以获得更高的性能。
六、String,StringBuilder,StringBuffer的区别
String对象一旦创建,内容不可以修改,线程安全适用不可变的字符串处理,每次修改字符串时,都会创建一个新的String对象,因此在频繁修改字符串时性能较差;而StringBuffer与StringBuilder的内容可以修改(在原对象上修改字符串,而不需要创建新的对象),对于需要频繁修改字符串的场景,它们比String更高效;StringBuffer的方法是 synchronized的,保证了多线程环境下的安全性,适用于多个线程共享同一 StringBuffer对象时,避免数据不一致;StringBuilder不提供同步机制,因此在多线程环境下,多个线程同时访问同一 StringBuilder 对象时,可能会导致数据不一致。在单线程环境下性能更高,因为没有同步开销。
-
String
适用于常量字符串和不需要修改字符串的场景。 -
StringBuffer
适用于多线程环境中需要修改字符串的场景,保证线程安全。 -
StringBuilder
适用于单线程环境中需要频繁修改字符串的场景,提供最佳性能。
根据实际的线程安全需求和性能要求,选择合适的类来处理字符串。
完