1.从概念上讲,java字符串就是Unicode字符串。
2.字符串拼接
用指定分隔符拼接字符串数组时,使用StringJoiner或者String.join()更方便;
用StringJoiner拼接字符串时,还可以额外附加一个“开头”和“结尾”。
@Test
public void join1() {
String[] names = {"Bob", "Alice", "Grace"};
StringJoiner sj = new StringJoiner(", ", "Hello ", "!");
for (String name : names) {
sj.add(name);
}
System.out.println(sj.toString());
}
@Test
public void join2() {
String[] names = {"Bob", "Alice", "Grace"};
String s = String.join(", ", names);
System.out.println(s);
}
3.子串substring
String类的substring方法可以从一个较大的字符串提取出一个子串,
public static void main(String[] args) {
//子串
String s = "你好,今天是2017年12月24日,圣诞快乐!!";
String date = s.substring(6, 17);
System.out.println(date);//输出:2017年12月24日
}
查看String源码,substring(beginIndex,endIndex):返回子字符串,从beginIndex到endIndex-1,通过调用String构造方法创建了一个新的String对象。
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > value.length) {
throw new StringIndexOutOfBoundsException(endIndex);
}
int subLen = endIndex - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
private final char value[];
public String(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count <= 0) {
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
if (offset <= value.length) {
this.value = "".value;
return;
}
}
// Note: offset or count might be near -1>>>1.
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
4.字符串判等equals
当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。所以一定不要使用运算符检测两个字符串是否相等!这个运算符只能够确定两个字符串是否放置在同一个位置上。如果虚拟机始终将相同的字符串共享,就可以使用运算符检测是否相等。但实际上只有字符串常量是共享的,而+或substring等操作产生的结果并不是共享的。
另外,使用equals方法时要注意避免NP。
public static void main(String[] args) {
//判等
String s3 = "abc";
String s4 = "abc";
String s5 = new String("abc");
String s6 = new String("abc");
System.out.println(s3 == s4);//true
System.out.println(s3.equals(s4));//true
System.out.println(s5 == s6);//false
System.out.println(s5.equals(s6));//true
//避免NP
String s7 = null;
// System.out.println(s7.equals("null"));//NP
System.out.println(s7 != null && s7.equals("null"));
System.out.println("null".equals(s7));
}
public 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;
}
5.String,StringBuilder和StringBuffer
①String字符串为什么是不可变的?
简单的来说:String 类中使用 final 关键字修饰字符数组来保存字符串,所以 String 对象是不可变的。
低版本实现:
private final char value[];
Java9之后的版本:
private final byte[] value;
而 StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串byte[] value, 没有用 final 关键字修饰,所以这两种对象都是可变的。
②线程安全性
String 中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。
@Override
@HotSpotIntrinsicCandidate
public StringBuilder append(String str) {
super.append(str);
return this;
}
@Override
@HotSpotIntrinsicCandidate
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
③对于三者使用的总结:
- 少量操作字符串用 String
- 单线程操作字符串缓冲区下大量数据用 StringBuilder
- 多线程操作字符串缓冲区下大量数据用 StringBuffer
作者:陈敬(公众号:敬YES)
出处:http://www.cnblogs.com/janes/
博客文章仅供交流学习,请勿用于商业用途。如需转载,请务必注明出处。