java中关于String 类型数据 的存储方式

  1. Constant Pool常量池的概念:
  2. 在讲到String的一些特殊情况时,总会提到String Pool或者Constant Pool,但是我想很多人都不太
  3. 明白Constant Pool到底是个怎么样的东西,运行的时候存储在哪里,所以在这里先说一下Constant Pool的内容.
  4. String Pool是对应于在Constant Pool中存储String常量的区域.习惯称为String Pool,也有人称为
  5. String Constant Pool.好像没有正式的命名??
  6. 在java编译好的class文件中,有个区域称为Constant Pool,他是一个由数组组成的表,类型
  7. 为cp_info constant_pool[],用来存储程序中使用的各种常量,包括Class/String/Integer等各
  8. 种基本Java数据类型,详情参见The Java Virtual Machine Specification 4.4章节.
  9. 对于Constant Pool,表的基本通用结构为:
  10. cp_info {
  11. u1 tag;
  12. u1 info[];
  13. }
  14. tag是一个数字,用来表示存储的常量的类型,例如8表示String类型,5表示Long类型,info[]根据
  15. 类型码tag的不同会发生相应变化.
  16. 对于String类型,表的结构为:
  17. CONSTANT_String_info {
  18. u1 tag;
  19. u2 string_index;
  20. }
  21. tag固定为8,string_index是字符串内容信息,类型为:
  22. CONSTANT_Utf8_info {
  23. u1 tag;
  24. u2 length;
  25. u1 bytes[length];
  26. }
  27. tag固定为1,length为字符串的长度,bytes[length]为字符串的内容.
  28. (以下代码在jdk6中编译)
  29. 为了详细理解Constant Pool的结构,我们参看一些代码:
  30. String s1 = "sss111";
  31. String s2 = "sss222";
  32. System.out.println(s1 + " " + s2);
  33. 由于"sss111"和"sss222"都是字符串常量,在编译期就已经创建好了存储在class文件中.
  34. 在编译后的class文件中会存在这2个常量的对应表示:
  35. 08 00 11 01 00 06 73 73 73 31 31 31 08 00 13 01 ; ......sss111....
  36. 00 06 73 73 73 32 32 32                         ; ..sss222
  37. 根据上面说的String常量结构,我们分析一下
  38. 开始的08为CONSTANT_String_info结构中的tag,而11应该是它的相对引用,01为
  39. CONSTANT_Utf8_info的tag,06为对应字符串的长度,73 73 73 31 31 31为字符串对
  40. 应的编码,接着分析,会发现后面的是对应"sss222"的存储结构.
  41. 经过上面分析,我们知道了11和13是两个字符串的相对引用,就可以修改class文件
  42. 来修改打印的内容,把class文件中的
  43. 00 6E 00 04 00 03 00 00 00 24 12 10 4C 12 12 4D
  44. 改成
  45. 00 6E 00 04 00 03 00 00 00 24 12 10 4C 12 10 4D
  46. 程序就会输出sss111 sss111,而不是和原程序一样输出sss111 sss222,因为我
  47. 们把对"sss222"的相对引用12改成了对"sss111"的相对引用10.
  48. ------------分割线
  49. public class Test {
  50. public static void main(String[] args) {
  51. String s1 = "sss111";
  52. String s2 = "sss111";
  53. }
  54. }
  55. 在上面程序中存在2个相同的常量"sss111",对于n个值相同的String常量,在Constant Pool中
  56. 只会创建一个,所以在编译好的class文件中,我们只能找到一个对"sss111"的表示:
  57. 000000abh: 08 00 11 01 00 06 73 73 73 31 31 31             ; ......sss111
  58. 在程序执行的时候,Constant Pool会储存在Method Area,而不是heap中.
  59. 另外,对于""内容为空的字符串常量,会创建一个长度为0,内容为空的字符串放到Constant Pool中,
  60. 而且Constant Pool在运行期是可以动态扩展的.
  61. 关于String类的说明
  62. 1.String使用private final char value[]来实现字符串的存储,也就是说String对象创建之后,就不能
  63. 再修改此对象中存储的字符串内容,就是因为如此,才说String类型是不可变的(immutable).
  64. 2.String类有一个特殊的创建方法,就是使用""双引号来创建.例如new String("i am")实际创建了2个
  65. String对象,一个是"i am"通过""双引号创建的,另一个是通过new创建的.只不过他们创建的时期不同,
  66. 一个是编译期,一个是运行期!
  67. 3.java对String类型重载了+操作符,可以直接使用+对两个字符串进行连接.
  68. 4.运行期调用String类的intern()方法可以向String Pool中动态添加对象.
  69. String的创建方法一般有如下几种
  70. 1.直接使用""引号创建.
  71. 2.使用new String()创建.
  72. 3.使用new String("someString")创建以及其他的一些重载构造函数创建.
  73. 4.使用重载的字符串连接操作符+创建.
  74. 例1
  75. /*
  76. * "sss111"是编译期常量,编译时已经能确定它的值,在编译
  77. * 好的class文件中它已经在String Pool中了,此语句会在
  78. * String Pool中查找等于"sss111"的字符串(用equals(Object)方法确定),
  79. * 如果存在就把引用返回,付值给s1.不存在就会创建一个"sss111"放在
  80. * String Pool中,然后把引用返回,付值给s1.
  81. *
  82. */
  83. String s1 = "sss111";
  84. //此语句同上
  85. String s2 = "sss111";
  86. /*
  87. * 由于String Pool只会维护一个值相同的String对象
  88. * 上面2句得到的引用是String Pool中同一个对象,所以
  89. * 他们引用相等
  90. */
  91. System.out.println(s1 == s2); //结果为true
  92. 例2
  93. /*
  94. * 在java中,使用new关键字会创建一个新对象,在本例中,不管在
  95. * String Pool中是否已经有值相同的对象,都会创建了一个新的
  96. * String对象存储在heap中,然后把引用返回赋给s1.
  97. * 本例中使用了String的public String(String original)构造函数.
  98. */
  99. String s1 = new String("sss111");
  100. /*
  101. * 此句会按照例1中所述在String Pool中查找
  102. */
  103. String s2 = "sss111";
  104. /*
  105. * 由于s1是new出的新对象,存储在heap中,s2指向的对象
  106. * 存储在String Pool中,他们肯定不是同一个对象,只是
  107. * 存储的字符串值相同,所以返回false.
  108. */
  109. System.out.println(s1 == s2); //结果为false
  110. 例3
  111. String s1 = new String("sss111");
  112. /*
  113. * 当调用intern方法时,如果String Pool中已经包含一个等于此String对象
  114. * 的字符串(用 equals(Object)方法确定),则返回池中的字符串.否则,将此
  115. * String对象添加到池中,并返回此String对象在String Pool中的引用.
  116. */
  117. s1 = s1.intern();
  118. String s2 = "sss111";
  119. /*
  120. * 由于执行了s1 = s1.intern(),会使s1指向String Pool中值为"sss111"
  121. * 的字符串对象,s2也指向了同样的对象,所以结果为true
  122. */
  123. System.out.println(s1 == s2);
  124. 例4
  125. String s1 = new String("111");
  126. String s2 = "sss111";
  127. /*
  128. * 由于进行连接的2个字符串都是常量,编译期就能确定连接后的值了,
  129. * 编译器会进行优化直接把他们表示成"sss111"存储到String Pool中,
  130. * 由于上边的s2="sss111"已经在String Pool中加入了"sss111",
  131. * 此句会把s3指向和s2相同的对象,所以他们引用相同.此时仍然会创建出
  132. * "sss"和"111"两个常量,存储到String Pool中.
  133. */
  134. String s3 = "sss" + "111";
  135. /*
  136. * 由于s1是个变量,在编译期不能确定它的值是多少,所以
  137. * 会在执行的时候创建一个新的String对象存储到heap中,
  138. * 然后赋值给s4.
  139. */
  140. String s4 = "sss" + s1;
  141. System.out.println(s2 == s3); //true
  142. System.out.println(s2 == s4); //false
  143. System.out.println(s2 == s4.intern()); //true
  144. 例5
  145. 这个是The Java Language Specification中3.10.5节的例子,有了上面的说明,这个应该不难理解了
  146. package testPackage;
  147. class Test {
  148. public static void main(String[] args) {
  149. String hello = "Hello", lo = "lo";
  150. System.out.print((hello == "Hello") + " ");
  151. System.out.print((Other.hello == hello) + " ");
  152. System.out.print((other.Other.hello == hello) + " ");
  153. System.out.print((hello == ("Hel"+"lo")) + " ");
  154. System.out.print((hello == ("Hel"+lo)) + " ");
  155. System.out.println(hello == ("Hel"+lo).intern());
  156. }
  157. }
  158. class Other { static String hello = "Hello"; }
  159. package other;
  160. public class Other { static String hello = "Hello"; }
  161. 输出结果为true true true true false true,请自行分析!
  162. 结果上面分析,总结如下:
  163. 1.单独使用""引号创建的字符串都是常量,编译期就已经确定存储到String Pool中.
  164. 2.使用new String("")创建的对象会存储到heap中,是运行期新创建的.
  165. 3.使用只包含常量的字符串连接符如"aa" + "aa"创建的也是常量,编译期就能确定,已经确定存储到String Pool中.
  166. 4.使用包含变量的字符串连接符如"aa" + s1创建的对象是运行期才创建的,存储在heap中.
  167. 6.使用"aa" + s1以及new String("aa" + s1)形式创建的对象是否加入到String Pool中我不太确定,可能是必须
  168. 调用intern()方法才会加入,希望高手能回答 @[email protected]
  169. 还有几个经常考的面试题:
  170. 1.
  171. String s1 = new String("s1") ;
  172. String s2 = new String("s1") ;
  173. 上面创建了几个String对象?
  174. 答案:3个 ,编译期Constant Pool中创建1个,运行期heap中创建2个.
  175. 2.
  176. String s1 = "s1";
  177. String s2 = s1;
  178. s2 = "s2";
  179. s1指向的对象中的字符串是什么?
  180. 答案: "s1"
  181. //=======================================================================
  182. 综上:
  183. String str1 = "a";//constant pool
  184. String str2 = "b";//constant pool
  185. String str3 = new String("a");//heap
  186. System.out.println(str1 == str3);//false
  187. String str4 = "a"+str2;//heap
  188. String str5 = "a"+"b";//constant pool
  189. String str6 = new String("ab");//heap
  190. String str7 = "ab";//constant pool
  191. System.out.println(str4 == str5);//false
  192. System.out.println(str4.intern() == str5);//true
  193. System.out.println(str4 == str6);//false
  194. System.out.println(str5 == str7);//true
  195. String str8 = str6;//heap
  196. System.out.println(str6 == str8);//true
  197. String str9 = new String("ab");//heap
  198. System.out.println(str6 == str9);//false
时间: 2024-11-19 20:12:45

java中关于String 类型数据 的存储方式的相关文章

java 中的String类型

java 中的String类型   (1)String类型的数据可以表示所有的数据类型. (2)String中的字符串常量与一般的字符串:                String str0 = "hello";//字符串常量“hello”被预先放到了数据段的字符串常量池中                String str1 = "hello";//直接从常量池中寻找已有的字符串常量                String str2 = new String

如何在ArrayList<Integer>中添加String类型数据

黑马入学测试题: ArrayList list = new ArrayList(); 在这个泛型为Integer的ArrayList中存放一个String类型的对象. package itheima; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; /** * 在这个泛型为Integer的ArrayList中存放一个St

Java中的String类型

1.基本类型和引用类型 在C语言里面,是有指针这么一个变量类型的,指针变量保存的就是所要指向内容的地址.在Java里面,没有了指针的这么个说法,而是换了一个词:引用类型变量. 先说Java里面的基本类型,所谓基本类型,很简单,就是一个数字,一个字符或一个布尔值,数字里面包含整型.浮点型等,没有什么好说的. 再说这个引用类型,他的概念就很像指针了.引用变量的值指向内存空间的引用,就是C中的地址,所在这个地址里面的内容就是要被他引用的对象. 2.String就是一个引用类型 大多数时候,我们在用St

java中的String类型(不知道理解的好不,请教大神)

当执行String a = "abc"; 时候(前提条件是:执行这行代码之前在常量池中没有abc,若有,a直接引用在常量池中abc不在创建一个对象,若无,执行下面的描述) java虚拟机会在栈中创建char型的值'a','b','c',然后在堆中创建一个String 对象,它的值(value)是刚才在栈中创建的三个char类型值组成数组{'a','b','c'},最后这个新创建的String 对象会被添加到字符串池中. String b = new String("abc&q

Java中关于String类型的10个问题

1. 如何比较两个字符串?用“=”还是equals 简单来说,“==”是用来检测俩引用是不是指向内存中的同一个对象,而equals()方法则检测的是两个对象的值是否相等.只要你想检测俩字符串是不是相等的,你就必须得用equals()方法. 如果你知道“字符串保留(string intern)”的概念那就更好了. 2. 为什么安全敏感的字符串信息用char[]会比String对象更好? String对象是不可变的就意味着直到垃圾回收器过来清扫之前它们都不会发生变化的.用数组的话,就可以很明确的修改

关于JAVA中的String类型的总结

因为前几天在贴吧里看到有人问了一道面试题: 原题地址:http://tieba.baidu.com/p/3479790275?pid=61955018493&cid=0#61955018493 1 public class StringTest { 2 3 public static void change(String str){ 4 str = "bbb"; 5 } 6 7 8 public static void main(String[] args){ 9 String

Android java传递string类型数据给C

本文接着实现<Android java传递int类型数据给C>的还未实现的方法: public native String sayHelloInC(String s); 先贴一个工具方法,具体意义后面的文章将介绍 /** * 把一个jstring转换成一个c语言的char* 类型. */ char* _JString2CStr(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = (*env)->FindCla

Java String类型数据的字节长度

问题描述: 向Oracle数据库中一varchar2(64)类型字段中插入一条String类型数据,程序使用String.length()来进行数据的长度校验,如果数据是纯英文,没有问题,但是如果数据中包含中文,校验可以通过,但是在数据入库时经常会报数据超长. 问题分析: 既然问题是数据超长,那么问题应该就是出在数据长度校验上,也就是出在String.length()这个方法上,来看看JDK是如何描述这个方法的: [plain] view plain copy length public int

Java中的字符串类型(String)

String 字符串是一个引用数据类型,字符串都是对象. String特性:1.在程序中出现的字符串字面量(常量),在程序中运行时会以对象的形式保存在JVM内存的字符串池中,并且所有的这些字符串字面量对象都是共享的: 2.(不变特性)字符串一旦定义就永远不会被改变了: 不变模式 - 23种设计模式之一: StringBuffer StringBuffer - 可变的字符序列,它在日常工作中就是做字符串连接的,线程安全的: - appand()方法; StringBuilder 与 StringB