String源码解读以及的intern()方法探究

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Pengjx2014/article/details/82820485

String的属性

先看下String的属性如下:

/**通过数组存储值*/
private final char value[];
/** 默认hashcode 为0 */
private int hash; // Default to 0
/**描述序列化类中的串行化字段*/
private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];

hashCode()计算方式如下:

public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;
            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

为啥子用31计算,传说是能更多的避免hash值重复与编译器优化,没怎么明白

构造函数

给出几个常见的知道编码方式的构造函数

/**用字节数组构建字符串,并指定编码名称*/
public String(byte bytes[], int offset, int length, String charsetName)
            throws UnsupportedEncodingException {
        if (charsetName == null)
            throw new NullPointerException("charsetName");
        checkBounds(bytes, offset, length);
        this.value = StringCoding.decode(charsetName, bytes, offset, length);
    }

/**用字节数组构建字符串,并指定编码集*/
public String(byte bytes[], int offset, int length, Charset charset) {
        if (charset == null)
            throw new NullPointerException("charset");
        checkBounds(bytes, offset, length);
        this.value =  StringCoding.decode(charset, bytes, offset, length);
    }

public String(byte bytes[], String charsetName)
            throws UnsupportedEncodingException {
        this(bytes, 0, bytes.length, charsetName);
    }

intern()方法

返回: 一个字符串,内容与此字符串相同,但它保证来自字符串池中。

public String intern()返回字符串对象的规范化表示形式。一个初始时为空的字符串池,它由类 String 私有地维护。
当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并且返回此 String 对象的引用。它遵循对于任何两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern()才为 true。
所有字面值字符串和字符串赋值常量表达式都是内部的。

我们知道,一个Java程序运行后,String类会在内存的方法区中维护一个字符串池。对一个字符串调用intern()方法后,会先检查池内是否有该字符串,若有则返回;若没有没有则先创建再返回,确保返回的字符串已经以字面量的形式存在于池中。

public class Test {
	public static void main(String argv[])
	{
		String s1 = "HelloWorld";
		String s2 = new String("HelloWorld");
		String s3 = "Hello";
		String s4 = "World";
		String s5 = "Hello" + "World";
		String s6 = s3 + s4;
		
		System.out.println(s1 == s2);
		System.out.println(s1 == s5);
		System.out.println(s1 == s6);
		System.out.println(s1 == s6.intern());
		System.out.println(s2 == s2.intern());
	}
}

运行结果如下:

false

true

false

true

false


解释一下:
 s1 创建的 HelloWorld 存在于方法区中的常量池其中的字符串池,而 s2 创建的 HelloWorld 存在于堆中,故第一条 false 。
 s5 的创建是先进行右边表达式的字符串连接,然后才对 s5 进行赋值。赋值的时候会搜索池中是否有 HelloWorld 字符串,若有则把 s5 引用指向该字符串,若没有则在池中新增该字符串。显然 s5 的创建是用了 s1 已经创建好的字面量,故 true 。
 第三个比较容易弄错,s6 = s3 + s4;中间多出来了一个StringBuilder对象, 其实相当于 s6 = new String(s3 + s4); s6 是存放于堆中的,不是字面量。所以 s1 不等于 s6 。
 第四个 s6.intern() 首先获取了 s6 的 HelloWorld 字符串,然后在字符串池中查找该字符串,找到了 s1 的 HelloWorld 并返回。这里如果事前字符串池中没有 HelloWorld 字符串,那么还是会在字符串池中创建一个 HelloWorld 字符串再返回。总之返回的不是堆中的 s6 那个字符串。
 第四条也能解释为什么第五条是 false 。s2是堆中的 HelloWorld,s2.intern() 是字符串池中的 HelloWorld 。

猜你喜欢

转载自blog.csdn.net/Pengjx2014/article/details/82820485