String Pool

String Pool,字符串常量池。(位于堆中)
常量池在java用于保存在编译期已确定的,已编译的class文件中的一些数据。
它包括了关于类,方法,接口等中的常量,也包括字符串常量,如String s = "java"这种申明方式;
当然也可扩充,执行器产生的常量也会放入常量池,故认为常量池是JVM的一块特殊的内存空间。

java中的常量池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则在需要重复创建相等变量时节省了很多时间。常量池其实也就是一个内存空间,常量池存在于方法区中。

每一个都是常量池中的一个常量表(常量项)。而这些常量表之间又有不同,class文件共有11种常量表,如下所示:

java 如何查看String常量池有没有该数据 string常量池在哪里_bc

2.1采用字面值赋值方式创建对象

String str1 = "aaa";

   String str2 = "aaa";

当以字符串直接创建String类的对象时,会在字符串池中查找是否已经存在该常量。

  如上例中,会在String Pool中查找是否存在"aaa"这个对象,如果不存在,则在String Pool中创建一个"aaa"对象,然后将其地址返回,赋给变量str1,这样str1会指向String Pool中的这个"aaa"对象。

  如果在String Pool中查找时已经存在该对象,则不创建任何对象,直接将String Pool中的这个对象地址返回,如上面str2直接得到"aaa"对象的地址,它和str1实际上指向同一个对象。

2.2采用new的形式创建对象

  采用new的形式创建对象:

String str = new String("aaa");

实际上创建了两个String对象,一个是”aaa”对象,存储在常量空间中,一个是使用new关键字为对象申请的堆空间。

  这种形式创建字符串对象时仍然会在String Pool中查找。

如果没有,则首先在String Pool中创建相应的对象,然后再在堆中创建一个对象;

若相同对象已经存在,则在堆中创建对象。

  无论哪种情况,最后返回的地址都是堆中的地址。

  如上例,首先在String Pool中查找有没有"aaa"这个字符串对象,如果有,则不在String Pool中再去创建"aaa"这个对象了,直接在堆(heap)中创建一个"aaa"字符串对象,然后将堆中的这个"aaa"对象的地址返回来,赋给str引用。

  如果没有,则首先在String Pool中创建一个"aaa"对象,然后再在堆中创建一个"aaa"对象,然后将堆中的这个"aaa"对象的地址返回来,赋给str引用。

2.3关于String创建对象的数量

(1) 、 String a = "abc";
这样创建 1 个对象。
JVM先到字符串池中查找,看是否已经存在值为"abc"的对象,如果存在,则不再创建新的对象,直接返回已存在对象的引用;
如果不存在,则先创建这个对象,然后把它加入到字符串池中,再将它的引用返回。
(2)、String a = new String("abc");
这样创建 2 个对象。
一个 是放在常量池中的”abc“对象。
另一个是由 new String()构造器在堆中创建一个“abc”对象。
a本身只是一个引用,放在栈里,用来指向堆中创建出来的对象。
(3)   String a = "abc";
       String b = "abc";

这样也只创建 1 个对象。
只在常量池中创建一个对象 "abc",然后将引用分别返回给a 和b。
(4)    String a = "abc" + "def";
这样创建 1 个对象。
由于常量字符串在编译时候也就确定的,又因为“abc”和“def”都是字符串常量,因此变量 a 的值在编译时就可以确定了。与 String a = "abcdef"; 一样。

(5) :

publicclass Concatenation2 {



   publicstaticvoid main(String[] args) {

     

       String a = "abc";

       String b = "def";

       String c = "abcdef";

       

       String d = "abc" + "def"; //不会产生新的字符串

       System.out.println(c == d); //true

       

       String e = a + "def";//会产生新的字符串对象

       System.out.println(e == c); //false

       

       String f = a + b; //会产生新的字符串

       System.out.println(f == c); //false

   }

}

结论:如果“+”连接的两字符串中只要有一个不是字面常量串(即定义过的),是会产生新的字符串对象。

(6)、
            如果将 a 和 b 都定义为常量,则:

publicclass Concatenation2 {



   publicstaticvoid main(String[] args) {

     

       final String a = "abc";

       final String b = "def";

       String c = "abcdef";

       /*

        * 因为 a 和 b 都被定义成了常量,所以编译时就确定。

        * 编译时会将常量替换,等同于 String d = "abc" + "def";

        */

       String d = a + b;

       System.out.println(d == c); //true

   }

}

            如果将 a 和 b 只有一个定义为常量,则:
        publicclass Concatenation2 {


   publicstaticvoid main(String[] args) {

       finalString a = "abc";

       String b = "def";

       String c = "abcdef";

       /*

        * 因为 a 为常量编译时就确定,而b为不是常量不能编译时确定

        * 因此等同于 String d = "abc" + b"

        */

       String d = a + b;

       System.out.println(d == c); //false

   }

}

     (7)、如果 a 和 b 都定义为 final ,但不在定义时候初始化,而是在static 块中初始化:
 

publicclass Concatenation2 {

   finalstatic String a;

   finalstatic String b;

   //此时 a 和 b 类似于变量,在类首次加载时才进行初始化

   static{

      a = "abc";

      b = "def";

   }

  

   publicstaticvoid main(String[] args) {

     

      String c = "abcdef";

      String d = a + b;

      System.out.println(c == d);  //false

   }

}