String源码:基于jdk1.8
public final class String implements Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char[] value; /** Cache the hash code for the string */ private int hash; public String() { this.value = "".value; } public String(String var1) { this.value = var1.value; this.hash = var1.hash; } public String(char[] var1) { this.value = Arrays.copyOf(var1, var1.length); } public String(byte bytes[], int offset, int length) { checkBounds(bytes, offset, length); this.value = StringCoding.decode(bytes, offset, length); } }
主要参数:
1、char[] value,用来存储数据的char类型数组
2、int hash,字符串的hash
结论:
1、String是final修饰的,证明是不可变的
2、实现了Comparable接口,可以通过CompareTo()进行比较,实现里Serializable接口,可以在网络中传输
3、通过数组保存数据
常用方法:
//获取String中第index个字符,首位为0 public char charAt(int index) { if ((index < 0) || (index >= value.length)) { throw new StringIndexOutOfBoundsException(index); } return value[index]; } //当前字符串和另一个字符串比较,比较同每个位置字符的大小,'A':65,'Z':90,'a':97,'z':122 public int compareTo(String anotherString) { int len1 = value.length; int len2 = anotherString.value.length; int lim = Math.min(len1, len2); char v1[] = value; char v2[] = anotherString.value; int k = 0; while (k < lim) { char c1 = v1[k]; char c2 = v2[k]; if (c1 != c2) { return c1 - c2; } k++; } return len1 - len2; } //拼接字符串 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); } //是否包含某个字符串 public boolean contains(CharSequence s) { return indexOf(s.toString()) > -1; } //根据数组生成一个字符串 public static String copyValueOf(char data[]) { return new String(data); } //是否以某个字符串结尾 public boolean endsWith(String suffix) { return startsWith(suffix, value.length - suffix.value.length); } //比较两个字符串是否相等 public boolean equals(Object anObject) { if (this == anObject) { //首先通过==比较,==比较的是对象,如果相等,返回true return true; } if (anObject instanceof String) { //判断是否是String类型的实例,如果不是,返回false String anotherString = (String)anObject; //强转成String 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; } //返回该字符串的hash值 //空字符串返回0,否则计算公式如下:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; } //判断是否包含字符串,和contains相同,包含返回0,否则-1 public int indexOf(int ch) { return indexOf(ch, 0); } //本地方法 public native String intern(); //判断是否为空," "并不是空字符串 public boolean isEmpty() { return value.length == 0; } //匹配正则表达式 public boolean matches(String regex) { return Pattern.matches(regex, this); } //将字符串中某个字符替换为另一个字符 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[],并把原字符之前的值复制进去 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); //生成一个新的String } } return this; } //截取字符串,首位为0 public String substring(int beginIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } int subLen = value.length - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } return (beginIndex == 0) ? this : new String(value, beginIndex, subLen); } //字符串中字符转小写 public String toLowerCase() { return toLowerCase(Locale.getDefault()); } //字符串中字符转大写 public String toUpperCase() { return toUpperCase(Locale.getDefault()); } //剔除字符串两端的空格字符 public String trim() { int len = value.length; int st = 0; char[] val = value; /* avoid getfield opcode */ while ((st < len) && (val[st] <= ' ')) { //从左到右找到第一个空格字符后面一个下标 st++; } while ((st < len) && (val[len - 1] <= ' ')) { //从右到左找到第一个空格字符前面一个下标 len--; } return ((st > 0) || (len < value.length)) ? substring(st, len) : this;//截取这两个下标的字符串 } //生成一个字符串 public static String valueOf(char c) { char data[] = {c}; return new String(data, true); }
结论:所有改变字符串的操作都是重新生成一个新的字符串,包括+,+=
深入理解:
public static void main(String[] args) { String str1 = "abc"; String str2 = new String("abc"); String str3 = "abc"; String str4 = new String("abc"); System.out.println(str1==str2); System.out.println(str1==str3); System.out.println(str2==str4); }
结果:
false true false
我们首先讲一下class常量池和字符串常量池的概念,之前在jvm已经讲过了,这里再简单讲一下
class常量池(Class Constant Pool):
我们写的每一个Java类被编译后,就会形成一份class文件,class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息
就是常量池(constant pool table),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References);
字面量包括:
1.String 2.八种基本类型的值 3.被声明为final的常量等;
符号引用包括:
1.类和方法的全限定名 2.字段的名称和描述符 3.方法的名称和描述符。
字符串常量池(String Constant Pool):
在HotSpot VM里实现的string pool功能的是一个StringTable类,被所有的类共享。字符串常量由一个一个字符组成,放在了StringTable上。
Class常量池里面的字符串在类加载过程放到字符串常量池
在JDK1.7版本,字符串常量池从方法区移到了堆中了。
解释:
String str1 = "abc";和String str3 = "abc; 都在编译期间生成了字面量,保存在字符串常量池中。jvm每次先到常量池中查找是否存在这个
字符串,如果存在str3直接指向这个字符串
而通过new生成的是对象,保存在堆中。每次new都会生成一个对象,所以是不相等的
String、StringBuffer、StringBuilder
public static void main(String[] args) { String s = "aaa" ; s += "bbb"; System.out.println(s); }
反编译查看字节码
public class com.it.Test { public com.it.Test(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: ldc #2 // String aaa 2: astore_1 3: new #3 // class java/lang/StringBuilder 6: dup 7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 10: aload_1 11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: ldc #6 // String bbb 16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: astore_1 23: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream; 26: aload_1 27: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 30: return }
第三行能看到遇到+的时候,会new一个StringBuilder,然后append这个字符串,然后调用toString(),如果多次用+=,就会生成很多StringBuilder对象,就会浪费内存
如果是这样使用+,就没问题
public static void main(String[] args) { String s = "aaa" + "bbb" + "ccc"; }
在编译期间直接生成一个字符串"aaabbbccc",是因为"aaa","bbb","ccc",都是编译器可知的常量,当然一般也不会这样用的
public static void main(String[] args) { String s1 = "bbb" ; String s = "aaa" + s1 + "ccc"; }
如果这样使用,也是不行的
String本身contact()效率也不高,每次生成一个新的char[],然后通过数组new一个String,所以需要拼接字符串的时候,可以使用StringBuilder
public static void main(String[] args) { StringBuilder builder = new StringBuilder("aaa"); builder.append("bbb"); }
public static void main(java.lang.String[]); Code: 0: new #2 // class java/lang/StringBuilder 3: dup 4: ldc #3 // String aaa 6: invokespecial #4 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 9: astore_1 10: aload_1 11: ldc #5 // String bbb 13: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 16: pop 17: return }
而StringBuffer和StringBuilder都继承了AbstractStringBuilder,也是通过char[]进行保存数据,所以效率会好很多
StringBuilder和StringBuffer区别就是是否保证线程安全,StringBuffer通过synchronized保证线程安全
性能比较:
"aaa"+"ccc"这样直接字符串叠加,性能肯定是最好的
而其余情况下:StringBuilder>StringBuffer>String
所以字符串改动较少使用String,否则使用StringBuilder,多线程环境下使用StringBuffer
内容参考:https://www.cnblogs.com/dolphin0520/p/3778589.html和Java编程思想