1.什么是字符串的不可变性?
字符串的不可变性是指,一旦在内存(堆)中创建了一个字符串,它就不能被改变。所有字符串方法都没有改变字符串本身,而是返回了一个新对象。什么意思呢?
public class Example {public static void main(String args[]) {String name = "string1";System.out.println("string1");//输出为:string1name = "string2";System.out.println("string2");//输出为:string2}
我们看以上代码段,内存中的过程如下图所示
Java对不同的字符串值会在堆中分别为他们开辟一个空间。当我们将name从string1改变为string2时,实际上是将name在栈中对string1的引用覆盖为string2的引用。
字符串这种用新值的地址覆盖旧值的地址,但旧值的引用对象还存在堆中的现象,称为字符串的不可变性。
2.JDK6和JDK7中substring的原理和区别?
首先贴上JDK6和JDK7中的关键代码
//JDK 6String(int offset, int count, char value[]) { this.value = value; this.offset = offset; this.count = count;}public String substring(int beginIndex, int endIndex) { return new String(offset + beginIndex, endIndex - beginIndex, value);}//JDK 7public String(char value[], int offset, int count) { this.value = Arrays.copyOfRange(value, offset, offset + count);}public String substring(int beginIndex, int endIndex) { int subLen = endIndex - beginIndex; return new String(value, beginIndex, subLen);}
我们知道字符串有不可变性,所以在调用substring方法的时候,原字符串会指向一个新的字符串。JDK6中调用substring方法时,会创建一个新的string对象,但这个string的值仍然指向堆中的同一个字符数组。只是count和offset不同。这会有什么问题呢,如果你有一个很长的字符串,需要的只是一小段,可是却引用了整个字符串,那么这个很长的字符数组就会一直被引用,无法被回收。
JDK7针对JDK6存在的问题做了改进,substring方法会在堆内存中创建一个新的数组。
3.replace、replaceAll有什么区别?
String s = "string.test.txt";System.out.println(s.replace(".", "#"));//string#test#txtSystem.out.println(s.replaceAll(".", "#"));//###############
直接看代码,replace是针对字符的全替换,replaceAll是针对正则的全替换,"." 在正则中表示除了换行符之外的任意字符。
4.字符串的"+"是怎么实现的(String对"+"的重载)?
Java实际上没有运算符的重载,但对String对象而言,可以直接将两个String对象的字符串值相加即"+"。乍看是对"+"的重载,实际上这只是JVM的语法糖。String的"+"是由StringBuilder以及他的append、toString两个方法实现的。
5.字符串的拼接有几种方式,有什么什么区别?
1. "+"可以用字符串与任意类型"+"号拼接,编译器对其做了优化,使用StringBuilder的append方法进行追加,但是每执行一次都会创建一个StringBuilder对象,且会调用toString方法转换成字符串,所以开销很大。执行一次字符串"+",相当于 new StringBuilder(string).append("string").toString();
2. String的 contact() 方法
调用和传入都必须是字符串,且调用方不能为null,其实是一次数组的拷贝,虽然在内存中的处理都是原子性操作,速度很快,但最后的return语句会创建一个新String对象,限制了concat方法的速度。
3. StringUtils.join() 方法
适用于将ArrayList转换成字符串
4. StringBuffer的 append() 方法
5. StringBuilder的 append() 方法
StringBuffer 和 StringBuilder 的 append()都继承自AbstractStringBuilder,整个逻辑只做字符数组的加长,拷贝,到最后也不会创建新的String对象,所以速度很快,完成拼接处理后在程序中用strBuffer.toString()得到最终的字符串。
综上
1. "+" 和 contact() 适用于小数据量的操作,代码简洁,但时间和空间成本都很高,不能用来做大批量数据的处理。2. StringUtils.join() 适用于将ArrayList转换成字符串,适用于大批量,可以省掉循环读取ArrayList的代码。3. StringBuffer的 append() 和 StringBuilder的append() 本质是一样的,都继承自AbstractStringBuilder,效率最高,大批量的数据处理最好选择这两种方法。
6.String.valueOf和Integer.toString有什么区别?
int i = 4;String i1 = "" + i;String i2 = String.valueOf(i);
如上代码
1. 实际上String.valueOf(i)也是调用Integer.toString(i)来实现的。
2. String i1 = "" + i则是String i1 = (new StringBuilder()).append(i).toString();
7.switch支持String吗?
JDK7前不支持,JDK7及之后支持,实际上JDK7是通过调用switch中string.hashCode将string转换为int来支持switch的。
## 附言
* 转载请注明出处
* 更多文章,请关注公众号《百育科技》,公众号会定期按系列整理文章