@html
一、java.lang.Stringjava
二、java.lang.StringBuffer数组
三、java.lang.StrungBuilder安全
相同点: 都是final类, 不容许被继承;多线程
不一样点:app
/—————————————本篇主要讨论String类型————————————/性能
字符串常量池(如下简称常量池/字符串池)的存在乎义:实际开发中,String类是使用频率很是高的一种引用对象类型。可是不断地建立新的字符串对象,会极大地消耗内存。所以,JVM为了提高性能和减小内存开销,内置了一块特殊的内存空间即常量池,以此来避免字符串的重复建立。JDK 1.8 后,常量池被放入到堆空间中。学习
字符串池中维护了共享的字符串对象,这些字符串不会被垃圾收集器回收。优化
若直接使用“==”进行比较对象,则比较的是两个对象的引用地址;ui
若使用str1.equals(str2)方法进行比较,因为String类内部已经覆盖Object类中的equals()方法,实际比较的是两个字符串的值。
// 源码 public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String aString = (String)anObject; if (coder() == aString.coder()) { return isLatin1() ? StringLatin1.equals(value, aString.value) : StringUTF16.equals(value, aString.value); } } return false; }
String str_01 = "aa"; String str_02 = "aa"; System.out.println(str_01 == str_02);
使用这种方式建立字符串,会先在栈中建立一个引用变量str_01,再去常量池中寻找是否已存在值为"aa"的字符串:
// result true
String str_01 = new String("xyz"); String str_02 = new String("xyz"); System.out.println(str_01 == str_02);
这种方式至少会建立一个对象,由于本质是调用了String类的构造器方法public String(String original){...},在堆中必定会建立一个字符串对象。
使用"new"关键字创造对象主要分为三步:
// result false
String str_01 = new String("abc").intern(); String str_02 = "abc"; String str_03 = new String("abc"); System.out.println(str_01 == str_02); System.out.println(str_02 == str_03); String str_04 = new String("cba"); String str_05 = new String("cba").intern(); System.out.println(str_04 == str_05);
当使用构造器建立字符串调用 intern()方法时,若是常量池中已经存在一个值相同的字符串(内部使用equals()方法来肯定),则返回常量池中的字符串对象的引用地址;不然,将堆中新建立的字符串对象添加到常量池中,并返回池中字符串对象的引用地址。
// result true false false
字符串的本质:char类型数组 private final char[] str
String类实现了CharSequence接口
String类型的不可变性指的是内存地址不可变,若是将一个对象从新赋值,则本质上是改变了其引用对象。
String a = "hello"; System.out.println(a.hashCode()); a = "hey"; System.out.println(a.hashCode());
// result 99162322 103196
StringBuffer类型和StringBuilder类型的字符串定义好后能够进行值改变,而且不会建立新的内存地址。
StringBuilder a = new StringBuilder(); System.out.println(a.hashCode()); a.append("Hello"); a.append("World"); System.out.println(a.hashCode());
// result 1395089624 1395089624
String类中的valueOf(Object obj)方法能够将任意一个对象转换为字符串类型。
// 源码 public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.toString(); }
String类中,重载了+与+=运算,这也是Java中惟一重载的两个运算符。
两个字符串相加便是字符串的拼接,在进行拼接时,会先调用valueOf(Object obj)方法将其为字符串类型,再进行拼接。从源码能够看出,若是字符串为null,会将其转换为字面值为"null"的字符串。
String s = null; s = s + "World"; System.out.println("Hello " +s); // result: Hello nullWorld
所以在进行字符串拼接时,初始字符串应该设置成空字符串"",而非null。
在字符串间使用加法运算时:
String str1 = "ABCD"; String str2 = "AB" + "CD"; String str3 = "A" + "B" + "C" + "D"; String temp1 = "AB"; String temp2 = "CD"; String str4 = temp1 + temp2; // String str4 = new StringBuilder().append(temp1).append(temp2).toString(); String temp = "AB"; String str5 = temp + "CD"; // String str4 = new StringBuilder(String.valueOf(temp)).append("CD").toString(); System.out.println(str1 == str2); System.out.println(str1 == str3); System.out.println(str1 == str4); System.out.println(str1 == str5);
// result true true false false
public class test { public static final String str1 = "abc"; public static final String str2 = "def"; public static void main(String[] args) { String str3 = str1 + str2; String str4 = "abcdef"; System.out.println(str3 == str4); } }
str1和str2都是final类型的,而且在编译阶段都是已经被赋值了,至关于一个常量,当执行Strings str3 = str1 + str2 的时候,str3已是"abcdef"常量了,已被建立在常量池中,因此地址是相等的。
// result true
public class test { public static final String s1; public static final String s2; static{ s1 = "ab"; s2 = "cd"; } public static void main(String[] args) { String s3 = s1 + s2; String s4 = "abcd"; System.out.println(s3 == s4); } }
虽然s1和s2都是final类型,可是起初并无初始化,在编译期还不能肯定具体的值,此处是变量,因此这里会调用StringBuilder类中的构造方法及append()方法来建立新的字符串s3,返回的新字符串s3在堆中的地址,因此与s4不相等。
// result false
参考内容:
If you have any question, please let me know, your words are always welcome.* 新人入坑,若有错误/不妥之处,欢迎指出,共同窗习。