String:字符串常量池

一、设计思想

  为字符串开辟一个字符串常量池,创建字符串常量时,首先检测字符串常量池是否存在该字符串。如存在该字符串对象,返回其引用;若不存在,实例化该字符串并放入常量池中,并返回其引用。

二、实现基础

  (1)String类字符串实例化后的对象是不可变的,初始化是什么则这个对象就永远是什么,相当于是常量,因此String的对象们可以维护成一个常量池。
  (2)运行时字符串常量池中有一个表,总是为池中所有的字符串对象维护一个引用,所以常量池中的这些字符串不会被GC回收。

三、操作常量池的情况

  凡是" "形式定义的字符串一定会操作常量池。
  不满足上面的情况,但是被编译成String str = " "形式的,会操纵常量池(从中取该常量,如果取不到,就创建一个)

  (1)直接赋值

String str = "Java";

  当"Java"字符串对象已经存在于常量池中时,str直接指向常量池中的对象。如果不存在,在常量池中创建"Java",令str指向它。
  (2)运算符重载

String str = "Ja" + "va";

  Java中可以使用+进行两个字符串的拼接。会被直接编译成str = "Java",会操作常量池。事实上这句话在常量池中创建了3个对象:"Ja"、"va"、"Java"(如果常量池中原本没有这些对象)。
  注意,如果是下面的情况:

String temp = "va";
String str = "Ja" + temp;

  或者:

String str = "Ja" + new String("va");

  此时str不会在编译时不会被自动拼接,即不会被编译成str = "Java"的形式,也就不会在常量池中创建"Java"的对象。但是还是会在常量池中创建"Ja"和"va"。

四、图形化的例子

String m = "hello,world";
String n = "hello,world";
String u = new String(m);
String v = new String("hello,world");

1.会分配一个11长度的char[ ]对象['h','e','l','l','o',',','w','o','r','l','d'],并在常量池分配一个由这个char数组组成的字符串对象"hello,world",然后由m去引用这个字符串。
2.用n去引用常量池里边的字符串"hello,world",所以和m引用的是同一个对象。
3.在堆中生成一个新的字符串,但内部的字符数组引用着m内部的字符数组。看一下源码就比较直观了:

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
/**
     * Initializes a newly created {@code String} object so that it represents
     * the same sequence of characters as the argument; in other words, the
     * newly created string is a copy of the argument string. Unless an
     * explicit copy of {@code original} is needed, use of this constructor is
     * unnecessary since Strings are immutable.
     *
     * @param  original
     *         A {@code String}
     */
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

4.同样会在堆中生成一个新的字符串,但内部的字符数组引用常量池里边的字符串"hello,world"内部的字符数组,也就是m/n内部字符数组。

  使用图来表示的话,情况就大概是这样的(使用虚线只是表示两者其实没什么特别的关系):



  测试:

String m = "hello,world";
String n = "hello,world";
String u = new String(m);
String v = new String("hello,world");
            
System.out.println(m == n); //true 
System.out.println(m == u); //false
System.out.println(m == v); //false
System.out.println(u == v); //false 

五、String的equals()和intern()

  (1)在Java中用==判断左右两边非基本数据类型的引用,是指判断两个引用是否是引用同一个对象。String的equals()方法则是判断两个字符串内部引用的字符数组对象的值是否相同(注意不要求是引用同一个数组对象)。源码如下:

/**
     * Compares this string to the specified object.  The result is {@code
     * true} if and only if the argument is not {@code null} and is a {@code
     * String} object that represents the same sequence of characters as this
     * object.
     *
     * @param  anObject
     *         The object to compare this {@code String} against
     *
     * @return  {@code true} if the given object represents a {@code String}
     *          equivalent to this string, {@code false} otherwise
     *
     * @see  #compareTo(String)
     * @see  #equalsIgnoreCase(String)
     */
    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            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;
    }

  (2)intern()方法:如果常量池中有与本字符串相同的(equals)字符串,就直接返回池中对象的引用,如果没有就在常量池中创建该对象并返回其引用。源码的注释:

/**
     * When the intern method is invoked, if the pool already contains a
     * string equal to this {@code String} object as determined by
     * the {@link #equals(Object)} method, then the string from the pool is
     * returned. Otherwise, this {@code String} object is added to the
     * pool and a reference to this {@code String} object is returned.
     */

  因此对两个字符串使用intern()和==,也可以起到和equals()一样的功能:

String m = new String("xixi");
String n = new String("xixi");
System.out.println(m.intern() == n.intern()); // true
System.out.println(m.equals(n)); // true
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 前言 最先接触编程的知识是在大学里面,大学里面学了一些基础的知识,c语言,java语言,单片机的汇编语言等;大学毕...
    oceanfive阅读 3,124评论 0 7
  • 从网上复制的,看别人的比较全面,自己搬过来,方便以后查找。原链接:https://www.cnblogs.com/...
    lxtyp阅读 1,357评论 0 9
  • 紫鹊界梯田 采芹人 他们说的我都不信 我只信你 在我眼前的模样 山溪清澈似西施的眼眸 红花艳艳似刑天共工留下的热血...
    论语说文阅读 513评论 0 2
  • 题目:在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的...
    qmss阅读 548评论 0 0
  • 下班后依然是照例在85度C买夜宵,左挑右选终究还是买了最常吃的海苔肉松和奶酥。其实在吃夜宵这件事上,深夜的朝晖路有...
    Whyya阅读 458评论 0 1