String——存储(源码详解)

1 篇文章 0 订阅

1 String

源码介绍

public final class String
    implements java.io.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; // Default to 0

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

从这行代码/** The value is used for character storage. */ private final char value[];可以看出字符串在内存中是以final 类型的char[] 数组的形式存储的。

    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);
    }

可以从代码return new String(buf, true);看出,返回值是一个新的String类型的对象,最原始的String字符串是没有改变的,String字符串一旦创建就不会改变了。

1.1 字符串常量池

JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化:使用字符串常量池。

每当我们创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。由于String字符串的不可变性我们可以十分肯定常量池中一定不存在两个相同的字符串(转载)

public class Run {
    public static void main(String[] args) {
        String a = "fengchengfeishuai";
        String b = "fengchengfeishuai";
        String c = new String("fengchengefeishuai");
    }
}

==在此判断的是变量a与c中存放的引用地址

public class Run {
    public static void main(String[] args) {
        String a = "fengchengfeishuai";
        String b = "fengchengfeishuai";
        String c = new String("fengchengfeishuai");
        System.out.println("a.hashCode() = " + a.hashCode());
        System.out.println("b.hashCode() = " + b.hashCode());
        System.out.println("c.hashCode() = " + c.hashCode());
        System.out.println("a == c ? "+ (a == c));
    }
}

a.hashCode() = 1428616511
b.hashCode() = 1428616511
c.hashCode() = 1428616511
a == c ? false

在这里插入图片描述

public class Run {
    public static void main(String[] args) {
        String a = "fengchengfeishuai";
        String b = "fengchengfeishuai";
        System.out.println("a.hashCode() = " + a.hashCode());
        System.out.println("b.hashCode() = " + b.hashCode());
        System.out.println("a == b ? " + (a == b));
    }
}
a.hashCode() = 1428616511
b.hashCode() = 1428616511
a == b ? true

当执行String a = "fengchengfeishuai";时,JVM首先会去字符串常量池中查找是否存在fengchengfeishuai这个对象。如果不存在,则在字符串常量池中创建fengchengfeishuai这个对象,然后将池中fengchengfeishuai这个对象的引用地址赋值给a,这样a会指向池中fengchengfeishuai字符串对象;如果存在,则不创建任何对象,直接将池中fengchengfeishuai这个对象的地址返回给other。当创建字符串对象b时,字符串常量池中已经存在fengchengfeishuai这个对象,直接把对象fengchengfeishuai的引用地址返回给b,这样b指向了池中fengchengfeishuai这个对象,也就是说ab指向了同一个对象,System.out.println("a == b ? " + (a == b));输出:true。

public class Run {
    public static void main(String[] args) {
        String c = new String("fengchengfeishuai");
        String d = new String("fengchengfeishuai");
        System.out.println("c.hashCode() = " + c.hashCode());
        System.out.println("d.hashCode() = " + d.hashCode());
        System.out.println("c == d ? " + (c == d));
    }
}
c.hashCode() = 1428616511
d.hashCode() = 1428616511
c == d ? false

采用new关键字新建一个字符串对象时,JVM首先在字符串常量池中查找有没有fengchengfeishuai这个字符串对象。如果有,直接在堆中创建一个fengchengfeishuai字符串对象,然后将堆中fengchengfeishuai对象copy给堆中的fengchengfeishuai字符串对象,这样,c就指向了堆中创建的这个fengchengfeishuai字符串对象;如果没有,在堆中创建一个fengchengfeishuai字符串对象,然后将堆中这个fengchengfeishuai字符串对象的地址赋给c,这样,c指向了堆中创建的这个fengchengfeishuai字符串对象。当执行String c = new String("fengchengfeishuai");时, 因为采用new关键字创建对象时,每次new出来的都是一个新的对象,也即是说引用cd指向的是两个不同的对象,因此语句System.out.println(c == d)输出:false。

public class Run {
    public static void main(String[] args) {
        String c = "fengchengfeishuai";
        String d = "fengchengfeishuai";
        String e = "fengcheng" + "feishuai" ;
        System.out.println("c.hashCode() = " + c.hashCode());
        System.out.println("d.hashCode() = " + d.hashCode());
        System.out.println("e.hashCode() = " + e.hashCode());
        System.out.println("c == d ? " + (c == d));
        System.out.println("c == e ? " + (c == e));
    }
}

c.hashCode() = 1428616511
d.hashCode() = 1428616511
e.hashCode() = 1428616511
c == d ? true
c == e ? true
public class Run {
    public static void main(String[] args) {
        String a = "fengchengfeishuai";
        String b = "fengchengfeishuai";
        String c = a + b;
        System.out.println("a.hashCode() = " + a.hashCode());
        System.out.println("b.hashCode() = " + b.hashCode());
        System.out.println("c.hashCode() = " + c.hashCode());
        System.out.println("a == c ? "+ (a == c));
    }
}
a.hashCode() = 1428616511
b.hashCode() = 1428616511
c.hashCode() = 772752864
a == c ? false

步骤:
1)栈中开辟一块空间存放引用a,a指向池中String常量"fengchengfeishuai"。
2)栈中开辟一块空间存放引用b,b指向池中String常量"fengchengfeishuai"。
3)栈中开辟一块空间存放引用c。
4)a+b通过StringBuilder的最后一步toString()方法还原一个新的String对象"fengchengfeishuaifengchengfeishuai",因此堆中开辟一块空间存放此对象。
5)c指向堆中(a+ b)所还原的新String对象。
6)c指向的对象在堆中,而常量"fengchengfeishuaifengchengfeishuai"在池中,输出为false。

总结:
1 创建字符串的方式
创建字符串的方式归纳起来有两类:

(1)使用"“引号创建字符串;
(2)使用new关键字创建字符串。
结合上面例子,总结如下:
(1)单独使用”“引号创建的字符串都是常量,编译期就已经确定存储到String Pool中;
(2)使用new String(”")创建的对象会存储到heap中,是运行期新创建的;

new创建字符串时首先查看池中是否有相同值的字符串,如果有,则拷贝一份到堆中,然后返回堆中的地址;如果池中没有,则在堆中创建一份,然后返回堆中的地址 (注意,此时不需要从堆中复制到池中,否则,将使得堆中的字符串永远是池中的子集,导致浪费池的空间)!
(3)使用只包含常量的字符串连接符如"aa" + “aa"创建的也是常量,编译期就能确定,已经确定存储到String Pool中;
(4)使用包含变量的字符串连接符如"aa” + s1创建的对象是运行期才创建的,存储在heap中;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值