String
String常用方法
package stringTest;
import java.util.Arrays;
public class StringTest {
public static void main(String[] args) {
String str = "abcabcabc";
//返回指定索引处字符
char ch = str.charAt(0);//abcabcabc -----> a
//从字符串中截取一部分子字符串,含头不含尾
String str_sub = str.substring(2,5);//abcabcabc -----> cab
//以指定的规则将此字符串分割成数组
String[] str_array1 = str.split("a");//abcabcabc -----> [, bc, bc, bc]
String[] str_array2 = str.split("c");//abcabcabc -----> [ab, ab, ab]
//去除字符串前后空格
String str1 = " abcabcabc ";
String str_trim = str1.trim();//" abcabcabc "----->abcabcabc
//返回字串在此字符首次出现的位置
int index = str.indexOf("b");//abcabcabc -----> 1
//返回字串在此字符最后出现的位置
int index_last = str.lastIndexOf("b");//abcabcabc -----> 7
//将字符串所有字符大写
String str_up = str.toUpperCase();//abcabcabc -----> ABCABCABC
//将字符串所有字符小写
String str2 = "abcAbcABC";
String str_low = str2.toLowerCase();//"abcAbcABC" ------> abcabcabc
//返回字符串长度
int length = str.length();//abcabcabc -----> 9
//是否为空字符串
Boolean bool1 = str.isEmpty();//abcabcabc -----> false
String str3 = " ";
Boolean bool2 = str3.isEmpty();//" " -----> false
String str4 = "";
Boolean bool3 = str4.isEmpty();//"" -----> true
//将指定字符串替换所有匹配子串
String str5 = str.replaceAll("abc","ccc");//abcabcabc -----> ccccccccc
}
}
基本数据类型和包装类与String的相互转换
包装类--->String String.valueOf(xxx);
String--->基本数据类型/包装类型 包装类的静态方法:parseXxx(str);
String--->char[] str.tocharArray();
char[]--->String String的构造方法:new String(char[]);
String--->byte[] str.getbyte();
byte[]--->String String的构造方法:new String(byte[]);
String的不可变性
String被final修饰,不可以被继承,代表不可变字符序列;String对象字符内容存储在底层一个字符数组final value[]中;实现了Serializable接口-->支持序列化;实现了Comparable(内部比较器)--->可以比较大小。
String字符串存储特性:
创建字符串用new和不用的区别
String为什么要设置为不可变类
出于安全和性能考虑。
- 在多线程中,只有不变的对象和值是线程安全的,可以在多个线程中共享数据。
- 字符串不变时,常量池才有意义,可以节约更多堆内存空间。String作为我们最常用的引用数据类型,设为不可变,为其在方法区中开辟一块常量池,避免重复的创建String对象,可以缓解堆内存空间的压力,内存溢出常常就是因为堆内存空间溢出造成的。
- 在散列集合中,根据hashCode确定元素位置。String的不可变性,避免了重复计算hashCode,只需使用缓存的hashCode即可,可以提高在散列集合中使用String的性能。
String、StringBuffer、StringBuildedr
String:不可变字符序列;底层使用char[]存储。
StringBuffer:可变字符序列;线程安全(里面方法都用synchornized修饰),效率低,底层使用char[]存储,初始容量可以容纳16个字符。当字符容纳不下时,创建一个容量(扩容)为原来的2倍+2的字符数组,并将原数组中字符都复制过来。
/**
* Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuffer() {
super(16);
}
StingBuffer中toStringCache变量:当bf对象第一次调用toString对象时,会将bf对象cope到toStringCache中(这个copy是从下标0到有效字符的下标的copy,不会造成空间浪费)。
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
然后返回一个String对象,在创建这个String对象时,是直接将toStringCache这个字符数组传给String,此过程不用再进行数组的复制。源码如下(这就是这个缓存的作用,当值没变需要转换为String时,不用去重复的进行数组的复制,这一点与StingBuffer不同)。
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}
当bf对象发生改变时会将toStringCache这个缓存置为null。下次toString()时确保toStringCache中的缓存都是最新值。
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
PS:如果你用IDEA想DEBUGE这个过程,千万要把下面标红的地方关掉。不然你会发现toStringCache根本不会置为null)
StringBuildedr :可变字符序列;JDK1.5新增,线程不安全,底层使用char[]存储,初始容量可以容纳16个字符。当字符容纳不下时,创建一个容量(扩容)为原来的2倍+2的字符数组,并将原数组中字符都复制过来。
StringBulider中的toString():如下,由于StringBuilder的字符数组存储的字符并不一定能将这个数组存满。所以调用的String构造方法与StringBuffer(toStringCache已提前处理好)不一样,需要传入字符数组,下标0,有效字符下标count这三个参数。
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
这个构造方法每次都会进行数组的copy,根据传入的三个参数进行copy。即使StringBulider对象的值从头到尾都没变过,只要调用一次toString(),就会进行一次copy。(StringBuffer只会进行一次)
效率:StringBulider>StringBuffer>String。
字符串的拼接
- +号拼接:
- 拼接的字符串中有变量(new出来的String对象)时:底层使用StringBuilder.append,然后调用StringBuilder的toString()返回字符串。
- 拼接的字符串中无变量时:即拼接的都是字符串常量,在编译时,编译器会直接将其优化为一个完整的字符串,和直接写一个完整字符串是一样的,此时效率很高。
- concat拼接:底层先创建一个字符数组,将两个字符串的值复制到字符数组中,然后使用这个字符数组创建一个新的String对象返回。
- StringUtils.join拼接:底层使用StringBuffe.append,然后调用StringBuffer的toString()返回字符串。
- StringBuffer
- StringBuilder
使用选择:
- 循环中进行拼接
- 要线程安全直接选用StringBuffer。
- 不要求线程安全直接使用StringBuilder。
- 千万不要用+,当拼接的对象都是字符常量时,会一直创建char[]对象。
- 非循环中进行拼接:如果是两个字符串拼接用concate;字符串和List<String>里的元素进行拼接用StringUtils.join;其余情况就用 + ,简单方便。