总结Java中的String类。
String类
字符串内部是使用字符数组char[] value来存储内容的,字符串是一种线性表(线性表还包括数组、链表等)。
1.类中常用方法
返回类型 | 方法名称 | 作用 |
---|---|---|
char | charAt(int) | 得到指定下标位置对应的字符 |
int | indexOf(String) | 得到指定内容第一次出现的下标 |
int | lastIndexOf(String) | 得到指定内容最后一次出现的下标 |
int | length() | 得到一个字符串的字符个数 |
char[] | toCharArray() | 将一个字符串转换成字符数组 |
String[] | split(String) | 将一个字符串按照指定内容劈开 |
byte[] | getBytes() | 将一个字符串转换成字节数组 |
boolean | equals(String) | 判断两个字符串的内容是否相同 |
boolean | equalsIgnoreCase(String) | 忽略大小写比较两个字符串的内容是否相同 |
boolean | contains(String) | 判断一个字符串里面是否包含指定的内容 |
boolean | startsWith(String) | 判断一个字符串是否以指定的内容开头 |
boolean | endsWith(String) | 判断一个字符串是否以指定的内容结尾 |
String | toUpperCase() | 将一个字符串全部转换成大写 |
String | toLowerCase() | 将一个字符串全部转换成小写 |
String | replace(String,String) | 将某个内容全部替换成指定内容 |
String | repalceFirst(String,String) | 将第一次出现的某个内容替换成指定的内容 |
String | substring(int) | 从指定下标开始一直截取到字符串的最后 |
String | substring(int,int) | 从下标x截取到下标y-1对应的元素 |
String | trim() | 去除一个字符串的前后空格 |
2.String类的赋值和intern方法
public class test {
public static void main(String[] args) {
String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
String str4 = new String("hello");
System.out.println(str1 == str2);
System.out.println(str1 == str3);
System.out.println(str3 == str4);
}
}
输出:
true
false
false
解释:
采用直接赋值:String str = "字符串字面量"的方式创建字符串对象,会在堆区使用字符串的常量池,若该对象是第一次出现,就在堆中开辟新空间,产生新对象,产生之后将该对象置入字符串的常量池,当下次再次使用该对象时(还是采用直接赋值的方式),若常量池中已有该内容的字符串,直接引用常量池中的对象,将该字符串的地址赋值给str,不再创建字符串。
若使用String str = new String(“字符串字面量”)的方式创建字符串对象,则在堆区的常量池外开辟一块空间,空间里面储存该字符串,将该空间的地址赋值过去str。
字符串的intern方法:
作用:调用这个inern方法,会将产生的字符串对象置入常量池(若不存在则置入),若常量池中已存在该对象,则返回常量中的字符串对象引用(返回地址)。
示例1:字符串比较
public static void main(String[] args) {
String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
// str3=str3.intern();
System.out.println(str1 == str2);
System.out.println(str1 == str3);
}
输出:
true
false
示例2:在常量池中已有字符串情况下intern()方法使用
public static void main(String[] args) {
String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
//此时常量池中已有"hello"对象,返回常量池中的hello对象的地址
str3=str3.intern();
System.out.println(str1 == str2);
System.out.println(str1 == str3);
}
输出:
true
true
示例3:在常量池中没有字符串情况下intern()方法使用
3.1:"abc"字符串第一次出现在intern()方法后面:
public static void main(String[] args) {
char[] ch=new char[]{'a','b','c'};
String s1=new String(ch);
s1.intern();
String s2="abc";
System.out.println(s1 == s2);
}
输出:
true
图解:
解释:“abc"出现在intern()字符之后,则调用intern时,常量池中没有"abc”,所以intern()方法的作用是将常量池外面的"abc"移动到常量池内部,s1的地址值不改变,s2赋值时,常量池中已有该字符串,返回该字符串的地址,所以最后输出true。
3.2:"abc"字符串第一次出现在intern()方法前面:
public static void main(String[] args) {
String s1=new String("abc");
s1.intern();
String s2="abc";
System.out.println(s1 == s2);
}
输出:
false
解释:“abc"出现在intern()字符之前,则调用intern时,常量池中已有"abc”,所以intern()方法的作用是返回的是常量池中"abc"的地址,但没有被接收,所以s1的地址值没有改变,返回false。
3.String类的不可变性
当一个字符串的对象产生之后,他的内容就无法修改,这称之为字符串对象的不可变性。
示例:
public static void main(String[] args) {
String str = "hello";
str += "world";
str += "!!!";
}
图示:
因为字符串的不可变性,在执行str += “world"时,会先在堆区的常量池中产生一个"world"对象,然后再产生一个"helloworld"对象,最后把str的引用改为指向"helloworld”,执行str += "!!!"时同理。正是由于字符串对象的不可变性,因此当直接使用字符串对象进行+=操作的时候会产生大量的临时对象,浪费内存!
面试问题:结合String的源码来分析一下为什么唯独字符串对象的内容无法修改?(你怎么理解字符串对象不可变,是如何做到内容无法修改的?)
源码:
解答:有很多错误的理解在于value数组被final修饰导致内容不能改,其实不是这样的!!!这里final修饰的是引用数据类型,不能修改的是该类型的地址值,而不是里面保存的具体内容。其本质在于在String类中,private关键字将value数组完全封装,且对外没有提供任何获取或者修改该value内容的方法,所以String类外部完全无法拿到这个value数组,因此字符串对象一旦产生,内容无法修改!!!
面试问题:string具有不可变性,Sting类内部有很多操作字符串的方法,如subString截取字符串,替换replace等等方法看起来这不都在修改字符串对象内容吗?
解答:String类的内部提供的所有涉及字符串修改的方法,都会产生新的字符串对象,并不会影响原字符串对象。
4.StringBuilder类和StringBuffer类
String、StringBuilder、StringBuffer区别:
String | StringBuffer | StringBuilder | |
---|---|---|---|
执行速度 | 慢 | 中等 | 快 |
线程安全 | 线程安全 | 线程安全 | 线程不安全 |
使用场景 | 少量字符串操作 | 多线程环境下的大量操作 | 单线程环境下的大量操作 |
PS:正常做题大部分情况下都使用StringBuilder。
面试问题:String.StringBuilder、StringBuffer的区别?
1.String的内容无法修改,StringBuffer、StringBuilder的对象内容可以修改,因此我们一般在字符串对象频繁修改的时候使用StringBuilder或StringBuffer类;
2.StringBuilder和StringBuffer的方法使用完全一致,StringBuffer采用同步处理,线程安全,效率较低;
3.StringBuilder采用异步处理,线程不安全,效率较高,开发中若不考虑线程安全问题,一般推荐使用StringBuilder类进行字符串内容的修改。