1. public final class String
  2. implements java.io.Serializable, Comparable<String>, CharSequence {…}

String类用了final修饰符,表示它不可以被继承,同时还实现了三个接口, 实现Serializable接口表示String类可被序列化;实现Comparable<T> 接口主要是提供一个compareTo 方法用于比较String字符串;还实现了CharSequence 接口,这个接口代表的是char值得一个可读序列(CharBufferSegmentStringStringBufferStringBuilder也都实现了CharSequence接口)

  1. /*字符数组value,存储String中实际字符 */
  2. private final char value[];

  3. /*字符串的哈希值 默认值0*/
  4. private int hash;
    /*字符串的哈希值 默认值0*/
  5. /*一个比较器,用来排序String对象, compareToIgnoreCase方法中有使用 */
  6. public static final Comparator<String> CASE_INSENSITIVE_ORDER
  7. = new CaseInsensitiveComparator();

String类提供了系列的构造函数,其中有几个都已经不推荐使用了,如下图:

以下是两个常用的构造函数的实现:

 

  1. //String str = new String(“123”)
  2. public String(String original) {
  3. this.value = original.value;
  4. this.hash = original.hash;
  5. }
  6. //String str3 = new String(new char[] {'1','2','3'});
  7. public String(char value[]) {
  8. //将字符数组值copy至value
  9. this.value = Arrays.copyOf(value, value.length);
  10. }

 String 类重写了 equals 方法,将此字符串与指定的对象比较。当且仅当该参数不为 null,并且是与此对象表示相同字符序列的 String 对象时,结果才为 true

  1. public boolean equals(Object anObject) {
  2. //直接将对象引用相比较,相同返回true
  3. if (this == anObject) {
  4. return true;
  5. }
  6. //比较当前对象与anObject的字符序列value
  7. if (anObject instanceof String) {
  8. String anotherString = (String)anObject;
  9. int n = value.length;
  10. if (n == anotherString.value.length) {
  11. char v1[] = value;
  12. char v2[] = anotherString.value;
  13. int i = 0;
  14. while (n-- != 0) {
  15. if (v1[i] != v2[i])
  16. return false;
  17. i++;
  18. }
  19. return true;
  20. }
  21. }
  22. return false;
  23. }

 逐位比较两个字符串的字符序列,如果某一位字符不相同,则返回该位的两个字符的Unicode 值的差,所有位都相同,则计算两个字符串长度之差,两个字符串相同则返回0

  1. public int compareTo(String anotherString) {
  2. int len1 = value.length;
  3. int len2 = anotherString.value.length;
  4. //取长度较小的字符串的长度
  5. int lim = Math.min(len1, len2);
  6. char v1[] = value;
  7. char v2[] = anotherString.value;
  8. int k = 0;
  9. while (k < lim) {
  10. //将两个字符串的字符序列value逐个比较,如果不等,则返回该位置两个字符的Unicode 之差
  11. char c1 = v1[k];
  12. char c2 = v2[k];
  13. if (c1 != c2) {
  14. return c1 - c2; //返回Unicode 之差
  15. }
  16. k++;
  17. }
  18. //长度较小的字符串所有位都比较完,则返回两个字符串长度之差
  19. //如果两个字符串相同,那么长度之差为0,即相同字符串返回0
  20. return len1 - len2;
  21. }
  1. compareToIgnoreCase(String str)方法实现于此类似,比较时忽略字符的大小写,实现方式如下:
  1. public int compare(String s1, String s2) {
  2. int n1 = s1.length();
  3. int n2 = s2.length();
  4. int min = Math.min(n1, n2);
  5. for (int i = 0; i < min; i++) {
  6. char c1 = s1.charAt(i);
  7. char c2 = s2.charAt(i);
  8. if (c1 != c2) {
  9. c1 = Character.toUpperCase(c1);
  10. c2 = Character.toUpperCase(c2);
  11. if (c1 != c2) {
  12. c1 = Character.toLowerCase(c1);
  13. c2 = Character.toLowerCase(c2);
  14. if (c1 != c2) {
  15. // No overflow because of numeric promotion
  16. return c1 - c2;
  17. }
  18. }
  19. }
  20. }
  21. return n1 - n2;
  22. }

当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。

所有字面值字符串和字符串赋值常量表达式都使用 intern 方法进行操作,例如:String str1 = “123”;

String对象可以直接通过字面量创建,也可以通过构造函数创建,有什么区别呢?

 1.通过字面量或者字面量字符串通过”+”拼接的方式创建的String对象存储在常量池中,实际创建时如果常量池中存在,则直接返回引用,如果不存在则创建该字符串对象

 2.使用构造函数创建字符串对象,则直接在堆中创建一个String对象

 3.调用intern方法,返回则会将该对象放入常量池(不存在则放入常量池,存在则返回引用)

下面举例说明String对象内存分配情况:

  1. String str1 = new String("123");
  2. String str2 = "123";
  3. String str3 = "123";
  4. String str4 = str1.intern();
  5. System.out.println(str1==str2); // false str1在堆中创建对象,str2在常量池中创建对象
  6. System.out.println(str2==str3); // true str2在常量池中创建对象,str3直接返回的str2创建的对象的引用 所以str2和str3指向常量池中同一个对象
  7. System.out.println(str4==str3); // true str4返回常量池中值为"123"的对象,因此str4和str2、str3都相等

关于字符串拼接示例:

  1. public class StringTest {
  2. public static final String X = "ABC"; // 常量X
  3. @Test
  4. public void Test() {
  5. String str5 = new String("ABC");
  6. String str6 = str5+"DEF"; //堆中创建
  7. String str7 = "ABC"+"DEF"; //常量池
  8. String str8 = X+"DEF"; //X为常量,值是固定的,因此X+"DEF"值已经定下来为ABCDEF,实际上编译后得代码相当于String str8 = "ABCDEF"
  9. String str9 = "ABC";
  10. String str10 = str9+"DEF"; //堆中
  11. System.out.println(str6==str7); //false
  12. System.out.println(str8==str7); //true
  13. System.out.println(str10==str7); //false
  14. }
  15. }

反编译后的代码看一下便一目了然:

内存分配如下:

 由于String类型内部维护的用于存储字符串的属性value[]字符数组是用final来修饰的:

  1. /** The value is used for character storage. */
  2. private final char value[];

表明在赋值后可以再修改,因此我们认为String对象一经创建后不可变,在开发过程中如果碰到频繁的拼接字符串操作,如果使用String提供的contact或者直接使用”+”拼接字符串会频繁的生成新的字符串,这样使用显得低效。Java提供了另外两个类:StringBuffer和StringBuilder,用于解决这个问题:

看一下下面的代码:

  1. String str1="123";
  2. String str2="456";
  3. String str3="789";
  4. String str4 = "123" + "456" + "789"; //常量相加,编译器自动识别 String str4=“123456789”
  5. String str5 = str1 + str2 + str3; //字符串变量拼接,推荐使用StringBuilder
  6. StringBuilder sb = new StringBuilder();
  7. sb.append(str1);
  8. sb.append(str2);
  9. sb.append(str3);

下面是StringBuilder类的实现,只截取了分析的部分代码:

 

  1. public final class StringBuilder
  2. extends AbstractStringBuilder
  3. implements java.io.Serializable, CharSequence
  4. {
  5. //拼接字符串
  6. @Override
  7. public StringBuilder append(String str) {
  1. //调用父类AbstractStringBuilder.append
    super.append(str); return this;
    }
    }
  1. abstract class AbstractStringBuilder implements Appendable, CharSequence {
  2. /**
  3. * 存储字符串的字符数组,非final类型,区别于String类
  4. */
  5. char[] value;
  6. /**
  7. * The count is the number of characters used.
  8. */
  9. int count;
  10. public AbstractStringBuilder append(String str) {
  11. if (str == null)
  12. return appendNull();
  13. int len = str.length();
  14. //检查是否需要扩容
  15. ensureCapacityInternal(count + len);
  16. //字符串str拷贝至value
  17. str.getChars(0, len, value, count);
  18. count += len;
  19. return this;
  20. }
  21. private void ensureCapacityInternal(int minimumCapacity) {
  22. // overflow-conscious code
  23. // minimumCapacity=count+str.length
  24. //拼接上str后的容量 如果 大于value容量,则扩容
  25. if (minimumCapacity - value.length > 0) {
  26. //扩容,并将当前value值拷贝至扩容后的字符数组,返回新数组引用
  27. value = Arrays.copyOf(value,
  28. newCapacity(minimumCapacity));
  29. }
  30. }
  31. //StringBuilder扩容
  32. private int newCapacity(int minCapacity) {
  33. // overflow-conscious code
  34. // 计算扩容容量
  35. // 默认扩容后的数组长度是按原数(value[])组长度的2倍再加上2的规则来扩展,为什么加2?
  36. int newCapacity = (value.length << 1) + 2;
  37. if (newCapacity - minCapacity < 0) {
  38. newCapacity = minCapacity;
  39. }
  40. return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
  41. ? hugeCapacity(minCapacity)
  42. : newCapacity;
  43. }
  44. }

StringBuffer和StringBuilder用一样,内部维护的value[]字符数组都是可变的,区别只是StringBuffer是线程安全的,它对所有方法都做了同步,StringBuilder是线程非安全的,因此在多线程操作共享字符串变量的情况下字符串拼接处理首选用StringBuffer, 否则可以使用StringBuilder,毕竟线程同步也会带来一定的消耗。

 

参考:http://www.importnew.com/24769.html

 

版权声明:本文为ashleyboy原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/ashleyboy/p/9063153.html