String、StringBuffer和StringBuilder总结
1、String
String类: 代表字符串。 Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现。
String是一个final类,代表不可变的字符序列。
字符串是常量,用双引号引起来表示。 它们的值在创建之后不能更改。
String对象的字符内容在源码里是存储在一个字符数组value[]中的。
(1) 常用方法
int length():返回字符串的长度: return value.length
char charAt(int index): 返回某索引处的字符return value[index]
boolean isEmpty():判断是否是空字符串:return value.length == 0
String toLowerCase():使用默认语言环境,将 String 中的所字符转换为小写
String toUpperCase():使用默认语言环境,将 String 中的所字符转换为大写
String trim():返回字符串的副本,忽略前导空白和尾部空白
boolean equals(Object obj):比较字符串的内容是否相同
boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写
String concat(String str):将指定字符串连接到此字符串的结尾。 等价于用“+”
int compareTo(String anotherString):比较两个字符串的大小
String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始
boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始
boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列时,返回 true
int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引
int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引
int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
注:indexOf和lastIndexOf方法如果未找到都是返回-1
替换:
String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所 oldChar 得到的。
String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串所匹配字面值目标序列的子字符串。
String replaceAll(String regex, String replacement):使用给定的 replacement 替换此字符串所匹配给定的正则表达式的子字符串。
String replaceFirst(String regex, String replacement):使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
匹配:
boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。
切片:
String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。
String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。
(2) String与其它结构的转换:
与基本数据类型、包装类之间的转换
String --> 基本数据类型、包装类:调用包装类的静态方法:Integer.parseXxx(str)
基本数据类型、包装类 --> String:调用String重载的valueOf(xxx)
与字符数组之间的转换
String --> char[]:调用String的toCharArray()、getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin):将指定索引范围内的字符串存放到数组
char[] --> String:调用String的构造器:String(char[]) 和 String(char[], int offset, int length)
与字节数组之间的转换
编码:String --> byte[]:调用String的getBytes()、getBytes(String charsetName)
解码:byte[] --> String:调用String的构造器:String(byte[])和String(byte[], int offset, int length)
(3) 参数传递
- 实参如果是基本类型或者String、Integer、Double等,则实参不会变(传的是值);
- 实参如果是对象集合或者数组,则实参会改变(传的是引用)。
面试题, 下列程序运行的结果:String是值传递,不是引用传递。
2、StringBuffer
java.lang.StringBuffer代表可变的字符序列, JDK1.0中声明,可以对字符串内容进行增删,此时不会产生新的对象。
线程安全。
很多方法与String相同。
作为参数传递时,方法内部可以改变值(引用传递)。
abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * The value is used for character storage. */ char[] value; //value没有final声明,value可以不断扩容。 /** * The count is the number of characters used. */ int count; //count记录有效字符的个数。}
StringBuffer类不同于String,其对象必须使用构造器生成。有三个构造器:
StringBuffer():初始容量为16的字符串缓冲区
StringBuffer(int size):构造指定容量的字符串缓冲区
StringBuffer(String str):将内容初始化为指定字符串内容
(1) 常用方法
StringBuffer append(xxx):提供了很多的append()方法, 用于进行字符串拼接
StringBuffer delete(int start,int end):删除指定位置的内容
StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str
StringBuffer insert(int offset, xxx):在指定位置插入xxx
StringBuffer reverse() :把当前字符序列逆转
public int indexOf(String str)
public String substring(int start,int end)
public int length()
public char charAt(int n )
public void setCharAt(int n ,char ch)
3、StringBuilder
StringBuilder 和 StringBuffer 非常类似,均代表可变的字符序列, 而且提供相关功能的方法也一样
线程不安全
(1) String与StringBuffer、StringBuilder之间的转换
String -->StringBuffer、StringBuilder:调用StringBuffer、StringBuilder构造器
StringBuffer、StringBuilder -->String
调用String构造器
StringBuffer、StringBuilder的toString()
(2) 面试题:对比String、 StringBuffer、 StringBuilder
String(JDK1.0): 不可变字符序列
StringBuffer(JDK1.0): 可变字符序列、效率低、线程安全
StringBuilder(JDK 5.0):可变字符序列、效率高、 线程不安全
注意:作为参数传递的话,方法内部String不会改变其值(值传递), StringBuffer和StringBuilder会改变其值(引用传递)
(3) 为什么要使用StringBuilder和StringBuffer拼接字符串?
- str+="abc";的方式缺点:编译器每次碰到"+"的时候,会new一个StringBuilder出来,接着调用append方法,在调用toString方法,生成新字符串。那么,这意味着,如果代码中有很多的"+",就会每个"+"生成一次StringBuilder,这种方式对内存是一种浪费,效率很不好。
- String的concat方法:通过两次字符串的拷贝,产生一个新的字符数组buf[],再根据字符数组buf[],new一个新的String对象出来,这意味着concat方法调用N次,将发生N*2次数组拷贝以及new出N个String对象,无论对于时间还是空间都是一种浪费。
- StringBuffer和StringBuilder原理一样,无非是在底层维护了一个char数组,每次append的时候就往char数组里面放字符而已,在最终sb.toString()的时候,用一个new String()方法把char数组里面的内容都转成String,这样,整个过程中只产生了一个StringBuilder对象与一个String对象,非常节省空间。StringBuilder唯一的性能损耗点在于char数组不够的时候需要进行扩容,扩容需要进行数组拷贝,一定程度上降低了效率。
- StringBuffer和StringBuilder用法一模一样,唯一的区别只是StringBuffer是线程安全的,它对所有方法都做了同步,StringBuilder是线程非安全的,所以在不涉及线程安全的场景,比如方法内部,尽量使用StringBuilder,避免同步带来的消耗。
- StringBuffer和StringBuilder还有一个优化点,上面说了,扩容的时候有性能上的损耗,那么如果可以估计到要拼接的字符串的长度的话,尽量利用构造函数指定他们的长度。
4、String内存模型
JVM中字符串常量池存放位置说明:
jdk 1.6 (jdk 6.0 ,java 6.0):字符串常量池存储在方法区(永久区)
jdk 1.7:字符串常量池存储在堆空间
jdk 1.8:字符串常量池存储在方法区(元空间)
new出来的对象的引用存放在栈区。
对象是放在堆中。
常量是放在方法区中的常量池中。
String使用陷阱
String s1 = "a":在字符串常量池中创建了一个字面量为"a"的字符串。
s1 = s1 + "b":实际上原来的“a”字符串对象已经丢弃了, 现在在堆空间中产生了一个字符串s1+"b"(也就是"ab")。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能。
String s2 = "ab":直接在字符串常量池中创建一个字面量为"ab"的字符串。
String s3 = "a" + "b": s3指向字符串常量池中已经创建的"ab"的字符串。
String s4 = s1.intern():堆空间的s1对象在调用intern()之后,会将常量池中已经存在的"ab"字符串赋值给s4。