String 内在分配解析

1.String类概念

(1)String是final的,不可被继承。public final class String。String是的本质是字符数组char[],
并且其值不可改变。private final char value[];

(2)Java运行时会维护一个String
Pool(String池)。String池用来存放运行时中产生的各种字符串,并且池中的字符串的内容不重复。而一般对象不存在这个缓冲池,仅仅存在于方法的堆栈区。

(3)创建字符串的方式很多,归纳起来有三类:(1)使用new关键字创建字符串-->String s1 = new
String("abc");(2)直接指定-->String s2 = "abc";(3)用串联生成新的字符串-->String s3 = "ab"
+ "c";

2.String对象创建的机制

原理1:当使用任何方式来创建一个字符串对象s时,Java运行时(运行中JVM)会拿着这个s在String池中找是否存在内容相同的字符串对象,如果不存在,则在池中创建一个字符串s,否则,不在池中添加。例如:String
str="abc";这行代码被执行的时候,JAVA虚拟机首先在字符串池中查找是否已经存在了值为"abc"的这么一个对象,它的判断依据是String
类equals(Object
obj)方法的返回值。如果有,则不再创建新的对象,直接返回已存在对象的引用;如果没有,则先创建这个对象,然后把它加入到字符串池中,再将它的引用返回。

原理2:Java中,只要使用new关键字来创建对象,则一定会(在堆区或栈区)创建一个新的对象。而不在常量池中创建String对象,只有使用了str.intern(),才会在常量池中创建一个String对象。

原理3:使用直接指定或者使用纯字符串串联来创建String对象,则仅仅会检查维护String池中的字符串,池中没有就在池中创建一个,有则罢了!但绝不会在堆栈区再去创建该String对象。

原理4:使用包含变量的表达式来创建String对象,则不仅会检查维护String池,而且还会在堆栈区创建一个String对象。

最后,有几点问题请大家注意:String a; 与String
a=null在作为类变量时候是等价的,在局部变量则不同。null表示一个空引用,String
a=null意思是在栈中声明了a,但是这个a没有指向任何地址。此时我们注意到String
a在栈中声明了a,但是也没有指向任何地址,但是java的语法检查如果在局部变量中,String a是不能直接使用的,String
a=null中的这个a可以直接使用。

3.经典面试题

(1)String s = new String("abc");创建了几个String Object?
答:[两个,pool中1个,heap中1个]

(2)String s0 = new String("abc");String s1 = new String("abc");创建了几个String
Object?答:[三个,pool中1个,heap中2个]

(3)解释

涉及概念:字符串池[pool of literal strings]的字符串对象和堆[heap]中的字符串对象。

字符串对象的创建:由于字符串对象的大量使用[它是一个对象,一般而言普通对象总是在heap分配内存],Java中为了节省内存空间和运行时间,在编译阶段就把所有的字符串文字放到一个字符串池[pool
of literal
strings]中,而运行时字符串池成为常量池的一部分。字符串池的好处,就是该池中所有相同的字符串常量被合并,只占用一个空间。另一种解释:在JAVA虚拟机(JVM)中存在着一个字符串池,其中保存着很多String对象,并且可以被共享使用,因此它提高了效率。由于String类是final的,它的值一经创建就不可改变,因此我们不用担心String对象共享而带来程序的混乱。字符串池由String类维护,我们可以调用intern()方法来访问字符串池。

现在看String s = new String("abc")语句,在执行new
String()时,先检查pool中有没有"abc"对象,如果没有,则先在pool中创建一个"abc"对象,再将其复制一份放到heap中,并且把heap中的这个对象的引用交给s持有,这条语句就创建了2个String对象;如果有则将pool中的对象复制一份放到heap中,并且把heap中的这个对象的引用交给s持有,这条语句就创建了1个String对象(也可以理解为:如果有则新创建的"abc"对象将原有pool中的"abc"对象覆盖,这样可以勉强说创建了2个String对象)。

补充:常量池(constant
pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。jdk编译时会将字符串池并入常量池。

4.举例

  1. public static void main(String[] args) {

  2. String str_0 = "forrest";

  3. String str_1 = "forrest";

  4. if (str_0 == str_1) System.out.println("pool中值创建了一个String对象:/"forrest/", str_0和str_1分别对其进行了引用");

  5. else System.out.println("Trouble");
  6. String str_2 = "vivian";

  7. String str_3 = new String("vivian");

  8. String str_4 = new String("vivian");

  9. if (str_2 != str_3) System.out.println("str_2是对pool中String对象引用, str_3是对heap中String对象引用, 所以二者不是引用的同一个对象");

  10. else System.out.println("Trouble");

  11. if (str_2 != str_4) System.out.println("str_2是对pool中String对象引用, str_4是对heap中String对象引用, 所以二者不是引用的同一个对象");

  12. else System.out.println("Trouble");

  13. if (str_3 != str_4) System.out.println("str_3和str_4都是对heap中String对象引用, 但二者引用对象的内存地址不同, 所以二者不是引用的同一个对象");

  14. else System.out.println("Trouble");
  15. String str_5 = str_3.intern(); //把str_3在heap中的String对象复制到loop中(虽然在String str_3 = new String("vivian")时已经在loop中创建了一份, 但intern()方法会要求重新复制),并将loop中这个对象引用到str_5

  16. if (str_5 != str_3) System.out.println("str_5是对pool中String对象引用, str_3是对heap中String对象引用, 所以二者不是引用的同一个对象");

  17. else System.out.println("Trouble");

  18. if (str_5 == str_2) System.out.println("str_5和str_2都是对loop中某一String对象引用, 所以二者引用的是同一个对象");

  19. else System.out.println("Trouble");
  20. str_4.intern();

  21. if (str_4 != str_2) System.out.println("str_4虽然执行了str_4.intern(), 但它的返回值没有赋给str_4, 所以str_4和str_2不是引用的同一个对象");

  22. else System.out.println("Trouble");

  23. if (str_4.intern() == str_2) System.out.println("str_4.intern()和str_2都是对loop中某一String对象引用, 所以二者引用的是同一个对象");

  24. else System.out.println("Trouble");

  25. if (str_4.intern() != str_4) System.out.println("str_4.intern()是对pool中String对象引用, str_4是对heap中String对象引用, 所以二者不是引用的同一个对象");

  26. else System.out.println("Trouble");

  27. }
  28. /*

  29. * 输出为:

  30. * pool中值创建了一个String对象:"forrest", str_0和str_1分别对其进行了引用

  31. * str_2是对pool中String对象引用, str_3是对heap中String对象引用, 所以二者不是引用的同一个对象

  32. * str_2是对pool中String对象引用, str_4是对heap中String对象引用, 所以二者不是引用的同一个对象

  33. * str_3和str_4都是对heap中String对象引用, 但二者引用对象的内存地址不同, 所以二者不是引用的同一个对象

  34. * str_5是对pool中String对象引用, str_3是对heap中String对象引用, 所以二者不是引用的同一个对象

  35. * str_5和str_2都是对loop中某一String对象引用, 所以二者引用的是同一个对象

  36. * str_4虽然执行了str_4.intern(), 但它的返回值没有赋给str_4, 所以str_4和str_2不是引用的同一个对象

  37. * str_4.intern()和str_2都是对loop中某一String对象引用, 所以二者引用的是同一个对象

  38. * str_4.intern()是对pool中String对象引用, str_4是对heap中String对象引用, 所以二者不是引用的同一个对象

  39. *

  40. * */

  1. public static void main(String[] args) {

  2. String a = "ab";

  3. String b = "cd";

  4. String c = "abcd";

  5. String d = "ab" + "cd";
  6. // 如果d和c指向了同一个对象,则说明d被加入字符串池

  7. if (c == d) System.out.println("/"ab/"+/"cd/" 创建的对象 /"加入了/" 字符串池中");

  8. else System.out.println("/"ab/"+/"cd/" 创建的对象 /"没加入/" 字符串池中");
  9. String e = a + "cd";

  10. // 如果e和c指向了同一个对象,则说明e也被加入了字符串池

  11. if (e == c) System.out.println("a +/"cd/" 创建的对象 /"加入了/" 字符串池中");

  12. else System.out.println("a +/"cd/" 创建的对象 /"没加入/" 字符串池中");
  13. String f = "ab" + b;

  14. // 如果f和c指向了同一个对象,则说明f也被加入了字符串池

  15. if (f == c) System.out.println("/"ab/"+ b 创建的对象 /"加入了/" 字符串池中");

  16. else System.out.println("/"ab/"+ b 创建的对象 /"没加入/" 字符串池中");
  17. String g = a + b;

  18. // 如果g和c指向了同一个对象,则说明g也被加入了字符串池

  19. if (g == c) System.out.println("a + b 创建的对象 /"加入了/" 字符串池中");

  20. else System.out.println("a + b 创建的对象 /"没加入/" 字符串池中");

  21. }
  22. /*

  23. * 输出为:

  24. * "ab"+"cd" 创建的对象 "加入了" 字符串池中

  25. * a +"cd" 创建的对象 "没加入" 字符串池中

  26. * "ab"+ b 创建的对象 "没加入" 字符串池中

  27. * a + b 创建的对象 "没加入" 字符串池中

  28. *

  29. * */

从第二段代码结果中我们不难看出,只有使用引号包含文本的方式创建的String对象之间使用“+”连接产生的新对象才会被加入字符串池中。对于所有包含new方式新建对象(包括null)的“+”连接表达式,它所产生的新对象都不会被加入字符串池中;对于类似str=1+"AAA"+2+3这种强制类型转化形成的String对象也不会放到pool中的。对此我们不再赘述。因此我们提倡大家用引号包含文本的方式来创建String对象以提高效率,实际上这也是我们在编程中常采用的。

String 内在分配解析,布布扣,bubuko.com

时间: 2024-10-15 08:10:01

String 内在分配解析的相关文章

String源码解析

定义 先看一下文档中的注释 12345 * Strings are constant; their values cannot be changed after they * are created. String buffers support mutable strings. * Because String objects are immutable they can be shared. */ String对象是常量,创建之后就不能被修改,所以该对象可以被多线程共享. 123456789

死磕 Java 系列(一)—— 常用类(1) String 源码解析

写在前面 这是博主新开的一个 java 学习系列,听名字就可以看出来,在这一些系列中,我们学习的知识点不再是蜻蜓点水,而是深入底层,深入源码.由此,学习过程中我们要带着一股钻劲儿,对我们不懂的知识充满质疑,力求把我们学过的知识点都搞清楚,想明白. 一.引言 在 java 的世界里,存在一种特殊的类,它们的创建方式极为特别,不需要用到 new XXX(当然也可以用这种方式创建), 但是却大量出现在我们的代码中,那就是 String 类.作为日常中使用频率最高的类,它是那么普通,普通到我们从来都不会

java笔记--String类对象解析与运用

1.String中的equals和==的区别 String是对象而非基本数据类型,不能使用"=="来判断两个字符串是否相当, 判断两个字符串内容是否相同用equals(); 判断两个字符串内存地址是否相同用"==" 2.startsWith(String str): 判断字符串是否以str为前缀 3.endsWith(String str): 判断字符串是否以str为后缀 4.String 字符串的比较: 1).compareTo()和compareToIgnore

String源码解析(一)

本篇文章内的方法介绍,在方法的上面的注释讲解的很清楚,这里只阐述一些要点. Java中的String类的定义如下: 1 public final class String 2 implements java.io.Serializable, Comparable<String>, CharSequence { ...} 可以看到,String是final的,而且继承了Serializable.Comparable和CharSequence接口. 正是因为这个特性,字符串对象可以被共享,例如下面

对String的内存解析

@Test public void stringTest(){ /* * str1和str2地址指向字符串常量池 * 解析: str1 在字符串常量池中创建出java 地址例如为:0x456 * str2建立时会去常量池中找是否有java 有的话赋值 str2地址为0x456 * str3和str4地址指向堆空间 * str在对空间创建,Stringvalue属性指向字符串常量池,存在赋值给其内部属性 value地址为0x456 而对于创建的空间而言 也是有自己的地址为0x789 * 所以str

Java String源码解析

String类概要 所有的字符串字面量都属于String类,String对象创建后不可改变,因此可以缓存共享,StringBuilder,StringBuffer是可变的实现 String类提供了操作字符序列中单个字符的方法,比如有比较字符串,搜索字符串等 Java语言提供了对字符串连接运算符的特别支持(+),该符号也可用于将其他类型转换成字符串. 字符串的连接实际上是通过StringBuffer或者StringBuilder的append()方法来实现的 一般情况下,传递一个空参数在这类构造函

java基础(五) String性质深入解析

引言 本文将讲解String的几个性质. 一.String的不可变性 对于初学者来说,很容易误认为String对象是可以改变的,特别是+链接时,对象似乎真的改变了.然而,String对象一经创建就不可以修改.接下来,我们一步步 分析String是怎么维护其不可改变的性质: 1. 手段一:final类 和 final的私有成员 我们先看一下String的部分源码: public final class String implements java.io.Serializable, Comparab

java内存分配和String类型的深度解析(转)

一.引题 在java语言的所有数据类型中,String类型是比较特殊的一种类型,同时也是面试的时候经常被问到的一个知识点,本文结合java内存分配深度分析 关于String的许多令人迷惑的问题.下面是本文将要涉及到的一些问题,如果读者对这些问题都了如指掌,则可忽略此文. 1.java内存具体指哪块内存?这块内存区域为什么要进行划分?是如何划分的?划分之后每块区域的作用是什么?如何设置各个区域的大小? 2.String类型在执行连接操作时,效率为什么会比StringBuffer或者StringBu

【转】java内存分配和String类型的深度解析

一.引题 在java语言的所有数据类型中,String类型是比较特殊的一种类型,同时也是面试的时候经常被问到的一个知识点,本文结合java内存分配深度分析关于String的许多令人迷惑的问题.下面是本文将要涉及到的一些问题,如果读者对这些问题都了如指掌,则可忽略此文. 1.java内存具体指哪块内存?这块内存区域为什么要进行划分?是如何划分的?划分之后每块区域的作用是什么?如何设置各个区域的大小? 2.String类型在执行连接操作时,效率为什么会比StringBuffer或者StringBui