关于String内存分配的深刻探讨

public class Test { app

   

    public static final String MESSAGE="taobao"; 优化

   

    public static void main(String[] args) { spa

      

       String a = "tao"+"bao"; 设计

       String b = "tao"; 对象

       String c = "bao"; 内存

      

System.out.println(a==MESSAGE);    System.out.println( (b+c)==MESSAGE);  字符串

    } 编译器

} string

对于这道题,考察的是对String类型的认识以及编译器优化。JavaString不是基本类型,可是有些时候和基本类型差很少,如String b = "tao"能够对变量直接赋值,而不用 new 一个对象(固然也能够用 new)。因此String这个类型值得好好研究下。 编译

Java中的变量和基本类型的值存放于栈内存,而new出来的对象自己存放于堆内存,指向对象的引用仍是存放在栈内存。例如以下的代码:

int i=1;

    String s = new String("Hello World");

变量is以及1存放在栈内存,而s指向的对象”Hello World”存放于堆内存。

 

 

 

 

栈内存的一个特色是数据共享,这样设计是为了减少内存消耗,前面定义了i=1i1都在栈内存内,若是再定义一个j=1,此时将j放入栈内存,而后查找栈内存中是否有1,若是有则j指向1。若是再给j赋值2,则在栈内存中查找是否有2,若是没有就在栈内存中放一个2,而后j指向2。也就是若是常量在栈内存中,就将变量指向该常量,若是没有就在该栈内存增长一个该常量,并将变量指向该常量。

 

 

若是j++,这时指向的变量并不会改变,而是在栈内寻找新的常量(比原来的常量大1),若是栈内存有则指向它,若是没有就在栈内存中加入此常量并将j指向它。这种基本类型之间比较大小和咱们逻辑上判断大小是一致的。如定义ij是都赋值1,则i==j结果为true==用于判断两个变量指向的地址是否同样。i==j就是判断i指向的1j指向的1是同一个吗?固然是了。对于直接赋值的字符串常量(如String s=Hello World”;中的Hello World)也是存放在栈内存中,而new出来的字符串对象(即String对象)是存放在堆内存中。若是定义String s=Hello World”和String w=Hello World”,s==w吗?确定是true,由于他们指向的是同一个Hello World

 

 

堆内存没有数据共享的特色,前面定义的String s = new String("Hello World");后,变量s在栈内存内,Hello World 这个String对象在堆内存内。若是定义String w =new String("Hello World");,则会在堆内存建立一个新的String对象,变量w存放在栈内存,w指向这个新的String对象。堆内存中不一样对象(指同一类型的不一样对象)的比较若是用==则结果确定都是false,好比s==w?固然不等,sw指向堆内存中不一样的String对象。若是判断两个String对象相等呢?用equals方法。

 

 

 

说了这么多只是说了这道题的铺垫知识,还没进入主题,下面分析这道题。MESSAGE成员变量及其指向的字符串常量确定都是在栈内存里的,变量a运算完也是指向一个字符串“taobao”啊?是否是同一个呢?这涉及到编译器优化问题。对于字符串常量的相加,在编译时直接将字符串合并,而不是等到运行时再合并。也就是说

String a = "tao"+"bao";String a = "taobao";编译出的字节码是同样的。因此等到运行时,根据上面说的栈内存是数据共享原则,aMESSAGE指向的是同一个字符串。而对于后面的(b+c)又是什么状况呢?b+c只能等到运行时才能断定是什么字符串,编译器不会优化,想一想这也是有道理的,编译器怕你对b的值改变,因此编译器不会优化。运行时b+c计算出来的"taobao"和栈内存里已经有的"taobao"是一个吗?不是。b+c计算出来的"taobao"应该是放在堆内存中的String对象。这能够经过System.out.println( (b+c)==MESSAGE);的结果为false来证实这一点。若是计算出来的b+c也是在栈内存,那结果应该是trueJavaString的相加是经过StringBuffer实现的,先构造一个StringBuffer里面存放”tao”,而后调用append()方法追加”bao”,而后将值为”taobao”StringBuffer转化成String对象。StringBuffer对象在堆内存中,那转换成的String对象理所应当的也是在堆内存中。下面改造一下这个语句System.out.println( (b+c).intern()==MESSAGE);结果是trueintern()方法会先检查String(或者说成栈内存)中是否存在相同的字符串常量,若是有就返回。因此intern()返回的就是MESSAGE指向的"taobao"。再把变量bc的定义改一下,

final String b = "tao";

        final String c = "bao";

            

       System.out.println( (b+c)==MESSAGE);

如今bc不可能再次赋值了,因此编译器将b+c编译成了”taobao”。所以,这时的结果是true

在字符串相加中,只要有一个是非final类型的变量,编译器就不会优化,由于这样的变量可能发生改变,因此编译器不可能将这样的变量替换成常量。例如将变量bfinal去掉,结果又变成了false。这也就意味着会用到StringBuffer对象,计算的结果在堆内存中。

    若是对指向堆内存中的对象的String变量调用intern()会怎么样呢?实际上这个问题已经说过了,(b+c).intern()b+c的结果就是在堆内存中。对于指向栈内存中字符串常量的变量调用intern()返回的仍是它本身,没有多大意义。它会根据堆内存中对象的值,去查找String池中是否有相同的字符串,若是有就将变量指向这个string池中的变量。

String a = "tao"+"bao";

       String b = new String("taobao");

     

      System.out.println(a==MESSAGE); //true

      System.out.println(b==MESSAGE);  //false

     

      b = b.intern();

      System.out.println(b==MESSAGE); //true

System.out.println(a==a.intern()); //true

相关文章
相关标签/搜索