String
1:字符串的不可变性
什么是不可变对象?不可变对象是指创建后无法变更的对象
String为什么是不可变的?String类为final,并且内部字符数组也为final。所以String对象是不可变对象。
String类为什么要设计为不可变?
主要出于对效率和安全的考量。
当你复制一个对象的时候,如果你知道它是不可变的,那么你只需要复制此对象的引用即可,一般引用会比对象小很多,所以能提高效率;String是不可变的,所以字符串常量池才可以存在,减少很多heap内存的占用;因为String的不可变性,所以在创建的时候hashcode就可以缓存,很适合作为map的key值;
安全方面:不可变对象是线程安全的。在多线程情况下,可变对象的内部状态可能会被其他线程改变,导致不可预期的结果。比如数据库连接,socket连接的IP PORT,类加载器等,都是通过String传参的,如果String是可变的,那会引起很大的安全问题。
2:JDK 6和JDK 7中substring的原理及区别
subString(int beginIndex, int endIndex)方法用来截取字符串
String x = "qwertt";x = x.substring(1,2);System.out.println(x);
结果输出:
w
JDK6中的subString
String类有三个属性:
char[] value:字符数组
int offset:起始位置
int count:字符串长度
对于subString方法,生成的String对象,value相同,只是改变了offset和count。这样会导致一个严重的问题:本来只需要很短的字符串,但是因为指向了一个很长的字符串,导致这个长字符串无法回收,存在内存泄漏的风险。
//JDK 6
String(int offset, int count, char value[]) {this.value = value;this.offset = offset;this.count = count;
}public String substring(int beginIndex, int endIndex) {//check boundaryreturn new String(offset + beginIndex, endIndex - beginIndex, value);
}
jdk6中,为解决上述问题,一般生成一个新的字符串并引用它:
x = x.substring(x, y) + ""
JDK7中的subString
在jdk7中,对上述问题进行了优化。每次执行subString的时候,都会去生成一个新的char[] ,从而避免了上述问题
jdk7源码如下:
//JDK 7
public String(char value[], int offset, int count) {//check boundarythis.value = Arrays.copyOfRange(value, offset, offset + count);
}public String substring(int beginIndex, int endIndex) {//check boundaryint subLen = endIndex - beginIndex;return new String(value, beginIndex, subLen);
}
3:replaceFirst、replaceAll、replace区别
先看一个示例:
String s = "my.test.txt";
System.out.println(s.replace(".", "#"));
System.out.println(s.replaceAll(".", "#"));
System.out.println(s.replaceFirst(".", "#"));
System.out.println(s.replaceFirst("\\.", "#"));
执行结果:
my#test#txt
###########
#y.test.txt
my#test.txt
原因:
replace方法有两个实现,一个是传入字符,循环匹配;一个是传入字符串,使用Pattern的逐个按字符进行匹配;
replaceFirst和replaceAll是使用Pattern,进行正则表达式的匹配。因为“.”在正则表达式中,表示任一字符,所以出现了“###########”的结果。
附源代码实现(jdk1.8版)
replace():
public String replace(char oldChar, char newChar) {if (oldChar != newChar) {int len = value.length;int i = -1;char[] val = value; /* avoid getfield opcode */while (++i < len) {if (val[i] == oldChar) {break;}}if (i < len) {char buf[] = new char[len];for (int j = 0; j < i; j++) {buf[j] = val[j];}while (i < len) {char c = val[i];buf[i] = (c == oldChar) ? newChar : c;i++;}return new String(buf, true);}}return this;}
replace():
public String replace(CharSequence target, CharSequence replacement) {return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(this).replaceAll(Matcher.quoteReplacement(replacement.toString()));}
replaceAll():
public String replaceAll(String regex, String replacement) {return Pattern.compile(regex).matcher(this).replaceAll(replacement);}
replaceFirst():
public String replaceFirst(String regex, String replacement) {return Pattern.compile(regex).matcher(this).replaceFirst(replacement);}
4:String对“+”的重载、字符串拼接的几种方式和区别
字符串的拼接方式:“+”、StringBuffer、new String().concat、StringBuilder
“+”:底层是使用StringBuilder实现。如:
String s1 = "11";
String s2 = "22";
String s = s1+s2;
System.out.println(s);
其实此段代码基本等价于:
String s1= "11";
String s2= "22";
StringBuilder sb = new StringBuilder();
sb.append(s1);
sb.append(s2);
String str = sb.toString();
System.out.println(str);
在大量使用“+”进行字符串拼接的时候,会产生大量的StringBuilder和String对象,会严重影响效率
concat:
concat其实是申请一个新的数组,进行数组的拷贝,然后用来创建新的String对象。底层是调用:System.arraycopy()
public String concat(String str) {int otherLen = str.length();if (otherLen == 0) {return this;}int len = value.length;char buf[] = Arrays.copyOf(value, len + otherLen);str.getChars(buf, len);return new String(buf, true);}
StringBuffer & StringBuilder
两者调用的父类方法如下,区别在于StringBuffer 方法用了synchronized,是线程安全的
与concat的区别在于:
扩容逻辑不同,concat为需要多少扩多少,StringBuilder等是指数级扩容;
concat每次会生成新的String对象,而StringBuilder不会
public AbstractStringBuilder append(String str) {if (str == null)return appendNull();int len = str.length();ensureCapacityInternal(count + len);str.getChars(0, len, value, count);count += len;return this;}
5:String.valueOf和Integer.toString的区别
直接看源代码就好
public static String valueOf(int i) {return Integer.toString(i);
}//对null进行了处理
public static String valueOf(Object obj) {return (obj == null) ? "null" : obj.toString();} public static String toString(int i) {if (i == Integer.MIN_VALUE)return "-2147483648";int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);char[] buf = new char[size];getChars(i, size, buf);return new String(buf, true);}
6:switch对String的支持
Java JDK7中switch添加了对String的支持,之前仅支持(short、int、byte、char),并且底层最终都会转为int类型。那么对于String,是如何支持的呢?
请看以下代码:
public static void test(String status) {switch (status) {case "INIT":System.out.println("INIT"); break;case "PAY_ING":System.out.println("PAY_ING"); break;case "PAY_SUCCESS":System.out.println("PAY_SUCCESS"); break;case "PAY_FAIL":System.out.println("PAY_FAIL"); break;default:System.out.println("default"); break;}}
反编译class文件得到:
public void test(String status){String str;switch ((str = status).hashCode()){case -2113017739:if (str.equals("PAY_FAIL")) break label129; break;case -68158581:if (str.equals("PAY_ING")) break label107; break;case 2252048:if (str.equals("INIT")) break label96; break;case 1643683628:if (!(str.equals("PAY_SUCCESS"))) { break label140:System.out.println("INIT");return;System.out.println("PAY_ING");return;}System.out.println("PAY_SUCCESS");return;label129: System.out.println("PAY_FAIL");label140: break;default:label96: label107: System.out.println("default");}}
虽然看不懂有些带标签的break语句(break label;)但是很明显可以看出来支持String的方式:
将String转为了int类型的hashCode,因为hashCode可能会冲突,又加入了equals判断。
7:字符串池、常量池(运行时常量池、Class常量池)、intern
偷个懒,先放个链接,后续再慢慢完善
https://www.cnblogs.com/tiancai/p/9321338.html