Java基础之String

String 1.8

类的定义

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence
  • String 类使用 final 关键字修饰,限制了该类无法被继承,里面的方法也无法被重写。这是 String 对象不可变的第一点。

类的成员变量

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

/** Cache the hash code for the string */
private int hash; // Default to 0
  • String 类常用的两个成员变量 value 和 hash
      valuefinal 关键字修饰的字符类型的数组,这是 String 对象不可变的第二点。
      hash: 对应字符串的哈希值。

String为什么要设置为不可变的

  • 保证 String 对象的安全性,防止 String 对象被恶意修改
  • 保证 hash 属性值不会频繁变更,确保了唯一性,使得类似 HashMap 容器能够实现相应的 key-value 存储
  • 可以实现字符串常量池

String对象的创建方式

  • 通过字符串常量的方式
String str1 = "abc";

    使用这种形式创建字符串时, JVM 会在字符串常量池中先检查是否存在该对象,如果存在,返回该对象的引用地址,如果不存在,则在字符串常量池中创建该字符串对象并且返回引用。使用这种方式创建的好处是:避免了相同值的字符串重复创建,节约了内存。

  • 通过 String() 构造函数的方式
String str3 = new String("abc");

    这种创建方式分为两个阶段,首先在编译时,字符串 abc 会被加入到常量结构中,类加载时候就会在常量池中创建该字符串。然后就是在调用 new() 时,JVM 将会调用 String 的构造函数,同时引用常量池中的 abc 字符串,在堆内存中创建一个 String 对象并且返回堆中的引用地址。

intern()方法

  • 在JDK 6中, intern() 方法会把首次遇到的字符串实例复制到永久代的字符串常量池中存储,返回的也是永久代里面这个字符串实例的引用 
  • 而JDK 7之后,字符串常量池移到Java堆中,intern() 方法不需要拷贝字符串的实例到永久代,只需要在常量池里记录一下首次出现的实例引用即可

    intern() 方法用来返回常量池中的某字符串,如果常量池中已经存在该字符串,则直接返回常量池中该对象的引用。否则,在常量池中加入该对象,然后返回引用。
    使用 intern() 方法后,构造相同值的字符串对象时,返回相同的对象引用地址,能够节约不少空间。

代码实例

String str1 = "abc";
String str2 = "abc";
String str3 = new String("abc");
String str4 = new String("abc").intern();
System.out.println(str1 == str2);
System.out.println(str1 == str3);
System.out.println(str1 == str4);

/* output:
 * true
 * false
 * true
 */

字符串常量池

  • JDK1.6中,字符串常量池放在方法区
  • JDK1.7 和 JDK1.8 中,字符串常量池放在堆内存
  • 所以导致 String 的 intern() 方法因为以上变化在不同版本会有不同表现
String str = "abcd";
// 字符串常量池
String s0 = new StringBuilder().append("he").append("llo").toString();
System.out.println(s0.intern() == s0);

String s1 = new StringBuilder().append("ab").append("cd").toString();
System.out.println(s1.intern() == s1);

/**output:
 * true
 * false
 */

代码详解:
    这段代码在JDK 6中运行,会得到两个 false ,而在JDK 7中运行,会得到一个 true 和一个 false 。
    产生差异的原因是,在JDK 6中, intern() 方法会把首次遇到的字符串实例复制到永久代的字符串常量池中存储,返回的也是永久代里面这个字符串实例的引用,而由 StringBuilder 创建的字符串对象实例在Java堆上,所以必然不可能是同一个引用,结果将返回 false 。
    而JDK 7之后的 intern() 方法实现就不需要再拷贝字符串的实例到永久代了,既然字符串常量池已经移到Java堆中,那只需要在常量池里记录一下首次出现的实例引用即可,因此 intern() 返回的引用和由 StringBuilder 创建的那个字符串实例就是同一个。而对 s1 比较返回 false ,这是因为 abcd 这个字符串在执行 StringBuilder.toString() 之前就已经出现过了,字符串常量池中已经有它的引用,不符合 intern() 方法要求“首次遇到”的原则, hello 这个字符串则是首次出现的,因此结果返回 true 。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值