String不可变性
JDK 1.8
中,String 使用 char 数组存储数据
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private final char[] value;
}
JDK 1.9
中,String 类改用 byte 数组存储字符串,同时用 coder 来标识使用哪种编码
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private final byte[] value;
private final byte coder;
}
String 是一个 final 类,意味着它不可以被继承,并且存储数据的 value 数组也被声明为 final,意味着 value 数组初始化后不能再引用其他数组,再加上 String 内部没有更改 value 数组数据的方法,所以 String 不可变。
public static void main(String[] args) {
String s1 = "abc";
//不可变,意味着s1数据不能更改,所以下列过程是重新创建一个新对象赋给s1
s1 = "def";
}
String Pool
常量池的介绍参考此文:https://www.cnblogs.com/jinggod/p/8425748.html
字符串常量池(String Pool
)保存着所有字符串字面量。当程序第一次使用某个字符串字面量时,会使用常量池缓存该字面量,后续程序需要再次使用该字面量时,会直接从常量池中取。
public static void main(String[] args) {
//第一次使用字符串字面量,会在常量池创建一个字符串对象,s1指向该对象
String s1 = "Love You";
//s2的结果在编译期间就已经计算出来,是字符串字面量,从常量池中取数据,s2指向常量池中的字符串对象
String s2 = "Love" + " You";
//s3结果在编译期无法计算出,运行时才能计算,所以需要在堆中创建对象
String s3 = s2 + "";
//使用new直接在堆中创建对象
String s4 = new String("Love You");
System.out.println(s1 == s2); //true
System.out.println(s1 == s3); //false
System.out.println(s1 == s4); //false
}
在运行过程,可以使用 String 的 intern() 方法将字符串添加到 String Pool
中。当一个字符串调用 intern() 方法时,如果 String Pool
已经存在一个字符串和该字符串值相等(使用 equals() 方法进行确定,那么就会返回 String Pool
中字符串的引用;否则,就会在 String Pool
中添加一个新的字符串,并返回这个新字符串的引用。
public static void main(String[] args) {
String s1 = new String("aaa");
String s2 = new String("aaa");
System.out.println(s1 == s2); //false
String s3 = s1.intern(); //常量池中无数据,添加
String s4 = s2.intern(); //常量池中有数据,从常量池中取
System.out.println(s3 == s4); //true
}
在 JDK 1.7
之前,String Pool
被放在运行时常量池中,它属于永久代。而在 JDK 1.7
之后,String Pool
被移到堆中。这是因为永久代的空间有限,在大量使用字符串的场景下会导致 OutOfMemoryError 错误。
new String("abc")
解析
使用这种方式一共会创建两个字符串对象(前提是 String Pool
中还没有 "abc" 字符串对象)。
- "abc" 属于字符串字面量,因此编译时期会在
String Pool
中创建一个字符串对象,存储该字符串字面量; - 运行期时,使用 new 的方式会在堆中创建一个字符串对象。
String类方法
构造方法
//构造一个空字符串对象
String();
//通过byte数组构造字符串对象
String(byte[] bytes);
//指定字符集解析byte数组
String(byte[] bytes, Charset charset);
//通过byte数组,从offset开始,总共length长的字节构造字符串对象
String(byte[] bytes,int offset,int length);
//指定字符集
String(byte[] bytes,int offset,int length, Charset charset);
//通过char数组构造字符串对象
String(char[] value);
//通过char数组,从offset开始,总共length长的字节构造字符串对象
String(char[] char,int offset,int length);
//构造一个original的副本,拷贝一个original
String(String original);
//通过StringBuffer对象构造字符串对象
String(StringBuffer buffer);
//通过StringBuilder对象构造字符串对象
String(StringBuilder builder);
获取序列长度 length
//返回当前字符串长度
int length();
获取指定字符 charAt
//返回字符串指定位置的字符,index取值0到length()-1
char charAt(int index);
获取数组对象 getBytes/toCharArray
//将String对象转换成byte数组
byte[] getBytes();
//指定字符集
byte[] getBytes(String charsetName)
//将该String对象转换成char数组
char[] toCharArray();
比较字符串 compareTo/compareToIgnoreCase
//比较字符串大小,相等返回0;不相等时,返回第一个不相等字符的字典序差;
//如果有个字符串为另一个字符串前缀,则返回长度差,即this.length()-anotherString.length()
int compareTo(String anotherString);
//不考虑大小写
int compareToIgnoreCase(String anotherString);
比较字符串 equals/equalsIgnoreCase/contentEquals
//String类重写了equal方法。字符串与指定对象的字符序列相等返回true,不等返回false
boolean equals(Object anObject);
//不区分大小写比较
boolean equalsIgnoreCase(Object anObject);
//内部实现:
boolean equals(Object anObject){
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
//将 String 与 StringBuilder 的字符序列进行比较,相等返回 true,不能返回 false
boolean contentEquals(StringBuffer sb);
//内部实现:
public boolean contentEquals(StringBuffer sb) {
synchronized (sb) {
return contentEquals((CharSequence) sb);
}
}
此处顺便一提,= 运算符比较的是是否为同一个字符串对象的引用,而不是比较字符序列,所以比较字符序列使用 equals 方法。
连接字符串 concat/"+"
//与运算符“+”号功能相同
String concat(String str);
截取字符串 substring
//获取从 beginIndex 位置开始到结束的子字符串
String substring(int beginIndex);
//获取从 beginIndex 位置开始到 endIndex 位置(不包括)的子字符串
String substring(int beginIndex, int endIndex);
去掉首尾空字符 trim
//去掉该字符串首尾空字符
String trim();
字符串拆分 split
//regex -- 正则表达式分隔符
//limit -- 分割的份数
String[] split(String regex);
String[] split(String regex, int limit);
String str = new String("Welcome-to-Blog");
for (String retval: str.split("-")){
System.out.println(retval);
}
//输出
Welcome
to
Blog
for (String retval: str.split("-", 2)){
System.out.println(retval);
}
//输出
Welcome
to-Blog
大小写 toLowerCase/toUpperCase
//将字符串转换成小写
String toLowerCase();
//将字符串转换成大写
String toUpperCase();
替换 replace/replaceAll/replaceFirst
//用newChar字符替换字符串中出现的所有oldChar字符,并返回替换后的新字符串
String replace(char oldChar, char newChar);
//用字符序列replacement替换所有出现的target,内部实现机理是replaceAll。
String replace(CharSequence target, CharSequence replacement);
//使用给定的参数replacement替换字符串中所有匹配给定的正则表达式的子字符串。
String replaceAll(String regex, String replacement);
//使用给定的参数replacement替换字符串中第一个匹配给定的正则表达式的子字符串
String replaceFirst(String regex, String replacement);
前/后缀匹配 startsWith/endsWith
//用于检测字符串是否以指定的前缀开始
boolean startsWith(String prefix);
//toffset--字符串中开始查找的位置。
boolean startsWith(String prefix, int toffset);
//用于检测字符串是否以指定的后缀结束
boolean endsWith(String suffix);
定位 indexOf/lastIndexOf/contains
//返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
int indexOf(int ch);
//返回从 fromIndex 位置开始查找指定字符在字符串中第一次出现处的索引
int indexOf(int ch, int fromIndex);
//返回指定字符序列在字符串中第一次出现处的索引
int indexOf(String str);
//返回从 fromIndex 位置开始查找指定字符序列在字符串中第一次出现处的索引
int indexOf(String str, int fromIndex);
//同理,LastIndexOf 具有同样的方法,只是返回的是最后一次出现处的索引
//是否包含子字符串
boolean contains(CharSequence s);
StringBuffer和StringBuilder及其方法
String、StringBuffer、StringBuilder都为字符串类,三者都实现了 CharSequence 接口,可认为 CharSequence 是一个字符串的协议接口。
三者的区别:
- String 是不可变类,而 StringBuffer 和 StringBuilder 是可变的。
- String 是不可变类,所有线程安全;StringBuffer 是线程安全的,内部使用 synchronized 进行同步;而 StringBuilder 不是线程安全的。
通常情况下,如果需创建序列可变的字符串对象,优先考虑 StringBuilder 类,因为线程无需同步,性能高。
StringBuffer 和 StringBuilder 方法大多与 String 差不多,但是由于他们是可变的,所以可以进行增、删、改等操作,下面以 StringBuffer 为例的一些方法。
追加字符串 append
//将指定的数据追加到此字符序列后,有多种数据类型的重载方式,这里仅列四种
StringBuffer append(String str);
StringBuffer append(char[] str);
StringBuffer append(char[] str, int offset, int len);
StringBuffer append(char c);
删除指定字符序列 delete
//移除此序列的子字符串中的字符(包括 start, 不包括 end)
delete(int start, int end);
删除指定字符 deleteCharAt
//删除指定字符
StringBuffer deleteCharAt(int index);
替换字符串序列 replace
//使用给定的Str替换此序列的子字符串中的字符(包括 start,不包括 end)
StringBuffer replace(int start, int end, String str);
插入字符序列 insert
//在offset位置插入指定的数据,有多种数据类型的重载方式,这里仅列四种
StringBuffer insert(int offset, String str);
StringBuffer insert(int offset, char[] str);
StringBuffer insert(int offset, char[] str, int offset, int len);
StringBuffer insert(int offset, char c);
设置指定字符 setCharAt
//将index位置的字符设置为ch
void setCharAt(int index, char ch);
反转字符串 reverse
//将此字符序列用其反转形式取代
StringBuffer reverse();