Java 语言的设计者认为共享带来的效率远远高于提取、拼接字符串所带来的低效率。 ——Core Java
在之前的学习和使用过程中,遇到过字符串常量池的概念,对于整形,在其源码中也有缓存数组的概念。其实这些类似概念,都是JVM对获取常用的字符串、整形对象这一操作所做的优化。
下面我们来分析下创建字符串的两种方式
1 String s1 = "hello";
2 String s2 = "hello";
3 String s3 = new String("hello");
4 // equals比较字符串字符值是否相等,均为 true
5 System.out.println(s1.equals(s2));
6 System.out.println(s2.equals(s3));
7 System.out.println(s1.equals(s3));
8
9 System.out.println(s1==s2); // true
10 System.out.println(s1==s3); // false
11 System.out.println(s2==s3); // false
对于第一种方式
用字面值的方式创建一个字符串时,JVM首先会去字符串常量池中查找是否存在"hello"这个对象,如果不存在,则在字符串常量池中创建"hello"这个对象,然后将池中"hello"这个对象的引用地址返回给"hello"对象的引用s1,这样s1会指向字符串常量池中"hello"这个字符串对象;如果存在,则不创建任何对象,直接将池中"hello"这个对象的地址返回,赋给引用s2。因为s1、s2都是指向同一个字符串池中的"hello"对象,所以“==”比较结果true。
对于第二种方式
采用new关键字新建一个字符串对象时,JVM会首先在字符串池中查找有没有"hello"这个字符串对象,如果有,则不在池中再去创建"hello"这个对象了,直接在堆中创建一个"hello"字符串对象,然后将堆中的这个"hello"对象的地址返回赋给引用s3,这样,s3就指向了堆中创建的这个"hello"字符串对象;如果没有,则首先在字符串池中创建一个"hello"字符串对象,然后再在堆中创建一个"hello"字符串对象,然后将堆中这个"hello"字符串对象的地址返回赋给s3引用,这样,s3指向了堆中创建的这个"hello"字符串对象。所以在进行“==”比较的时候,s1 和 s2 都是指向常量池中的字符串对象。s3 是指向堆中字符串对象。结果肯定为false。
考虑下面的整形恒等判断的代码
1 Integer a = Integer.valueOf(3);
2 Integer b = Integer.valueOf(3);
3
4 Integer c = Integer.valueOf(323);
5 Integer d = Integer.valueOf(323);
6
7 System.out.println(a==b);
8 System.out.println(c==d);
输出结果为
1 ```
2 true
3 false
4 ```
@HotSpotIntrinsicCandidate
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
其方法注释的意思是:返回表示指定 Integer 的实例。如果不需要新的 integer 实例,则通常应优先使用此方法,而不是使用构造函数 integer(int),因为通过缓存频繁请求值,此方法可能会显著提高空间和时间性能。此方法将始终缓存范围为-128到127(含)的值,并可能缓存此范围之外的其他值。再去看看IntegerCache类的源码,发现在这个静态内部类声明了一个Integer cache[],对于-128~127的整形,也就是占用1个字节的整形来说,如果用Integer.valueOf()方法进行初始化,就会从这个缓存数组中取值,因为JVM对这种操作进行了优化,,所以可以用一个缓存数组进行速度上的提升。
1 Integer e = 42;
2 Integer f = 42;
3
4 Integer g = 423;
5 Integer h = 423;
6
7 System.out.println(e==f); // true
8 System.out.println(g==h); // false
Integer e = 42; // Integer d = Integer.valueOf(42);
Integer f = 42; // Integer d = Integer.valueOf(42);
1 Integer g = 423;
2 Integer h = 423;
1 Integer g = new Integer(423);
2 Integer h = new Integer(423);
都是使用了 new 关键字进行整形的创建,g 和 h指向的是堆中两个不同的地址,他们进行恒等判断结果当然就是false。IntegerCache 类的 cache[] 数组和数组字符串常量池有异曲同工之妙。