String

来源:互联网 发布:网络软文推广案例 编辑:程序博客网 时间:2024/04/29 12:37
String 相信大家都不会陌生,几乎每天都会用到,那么你真的了解它吗?一起看看下面的例子来确认下你是否正的了解它。

public static void main(String[] args) {String a = "1" + "2" + "3";String b = "123";String c = new String("123");System.out.println(a == b);System.out.println(a == c);}

答案:
        true
        false

如果你答对了,你知道为什么吗?

首先 “==” 对于基本数据类型来说是直接比较它们的值。那么如果是引用类型呢?其实比较的是两个对象的引用地址值是否相同。

提到String不得不说下equals()方法,是在Object中定义的。它默认定义的就是使用“==”来比较(下面是源码)

 public boolean equals(Object obj) {return (this == obj);}

在String中是重写了这个方法,用于比较值是否相等。

在看下String a = "1" + "2" + "3" 和  String b = "123"; a是通过“+”赋值,而b是通过直接赋值。

由于JVM在编译时会做优化,所以 String a = "1" + "2" + "3"; 在编译时即为String a = “123”;

因为JVM会认为"1、2、3"这3个常量叠加后是一个固定的值,不同在运行时运算,所以采取优化并且存放在常量池中。


而String c = new String("123"); 在运行时创建了一个新的对象。

你真的明白了吗?看看下面的例子

public static void main(String[] args) {String a = "1";String b = "2";final String c = "12";String d = a + b;String e = "12";System.out.println(c == d);System.out.println(e == d);System.out.println(c == e);}

答案:
        false
        false
        true
怎么样,这次答对了吗?

之前说过JVM在编译期间会做优化,当运行String d = a + b;时,JVM也不能确定会发生什么(有很多情况会使它在运行时方法改变如:字节码增强技

术)。所以它就无法做出优化,以在编译时是如下结果

StringBuilder temp = new StringBuilder();

temp.append(a).append(b);

String b = temp.toString();


由于变量c被final修饰,那么JVM自然会认为它是不可变的。

补充两个问题
      1、String.intern();
                这个方法大家可能平时很少用到,当调用这个方法的时候,JVM会从常量池中去循环做匹配,如果找到了就直接

                返回这个对象的地址,如果没找到则会创建一个并返回创建后对象的地址。

                String a = "12";

                String b = a.intern();

                System.out.println(a == b);
         答案:
                true

2、String的”+“拼接
      String a = "1" + "2";大家可能经常使用这种方式对字符串进行拼接。 之前也说了,因为都是常量,
              所以JVM会在编译期间优化。
     如果是动态拼接呢?下面这个方式相信大家也肯定经常使用。
       String a = null;
       for(int i=0; i<n; n++){
                      a = a + i;

       }

       其实每循环一次,内部都在做

               StringBuilder temp = new StringBuilder(); 

               temp.append(a).append(i); 

               a = temp.toString();

如果循环次数较多,会非常消耗内存,严重会导致内存溢出。看下StringBuilder中的部分源码

void expandCapacity(int minimumCapacity) {
    int newCapacity = value.length * 2 + 2;
    if (newCapacity - minimumCapacity < 0)
    newCapacity = minimumCapacity;
    if (newCapacity < 0) {
       if (minimumCapacity < 0) // overflow
                      throw new OutOfMemoryError();
        newCapacity = Integer.MAX_VALUE;
    }
       value = Arrays.copyOf(value, newCapacity);

}

从源码中可以看出,如果每次空间不足时,会按照最少二倍的方式扩容(默认是16个字节),对象的数据会暂时保留,知道扩容完成。
那么就相当于需要三倍的内存空间,这样循环下去,内存的消耗可想而知。
简单的说就是在运行时频繁的做“+”拼接,会使堆中Young空间不足导致频繁的GC(复制算法),从而进入Old区域,

然而Old区域与Young区域不同(后面说JVM内存模型及GC策略时会详细说明),会发生FULL GC,时间会非常的慢。


总结:需要大量的动态“+”拼接时,用StringBuilder而不要使用“+”。

1 0
原创粉丝点击