原子与分子的区别联系:Java中的String:水深几许?

来源:百度文库 编辑:中财网 时间:2024/04/30 11:39:31

  对Java中String的理解一直源于C++中的String,但越来越不对劲。

  从字符串常量到String对象,到地址池的引出,再到Java中内存的分类,再到上一篇中讲到的Java编译器的优化,最后到《java语言规范》。

  刚刚开始学Java,遇到这种问题,不知道是该庆幸呢,还是愈发感到忧烦。但个人还是倾向于前者,任何疑惑都有可能是一个起点,引向一片新的未知世界。从这个起点开始,可能会有更多的疑惑,但要解决这些疑惑,给自己一个基本满意的交代(最起码把疑惑给干掉),一直顺藤摸瓜下去,终究会有一个尽头,而这个过程,对于刚刚开始学的我来说将是一个很好的学习契机。

  ————————————————————————————————

  C++中的String:

  字符串常量:本质上有一个const char* pstr就可以表征了;

  String类:自定义类,通过重载+,=等运算符,实现其功能;

  一切很清晰明了。

  Java中的String:

  (某本教材)所有用双引号括起的字符串常量(又称作字面常数)都被认为是对象。

  在Java中引用类型变量间用==,判别2个引用类型变量是否是同一对象的引用。Java中没有运算符的重载(只有对于String的+,从不同层面上看可以理解是运算符的重载与否)。

  于是乎,关于String ,==,+,=的各种问题混乱不堪。

  ————————————————————————————————

  Process:

  不清楚String str="abc";时看到了地址池的概念,于是乎寻找有关Java内存分配的内容。找到一篇还不错的文章:

  http://zy19880423.javaeye.com/blog/434179

  Java内存分配:

  1. 寄存器:我们在程序中无法控制;

  2. 栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而

  是存放在堆中;

  3. 堆:存放用new产生的数据;

  4. 静态域:存放在对象中用static定义的静态成员;

  5. 常量池:存放常量;

  6. 非RAM存储:硬盘等永久存储空间。

  Tips:

  引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。引用变量就相当于是为数组或者对象起的一个名称。引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。 (其实就是Java中的指针)。

  数组和对象本身在堆中分配,即使程序运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。这也是Java比较占内存的原因。(不像C++中那样,临时对象在程序运行到代码段外时会被立即析构掉,所以,返回临时对象的指针是危险的!)(Java则例外,可以返回临时对象,如果赋值给一个外部变量,则该对象仍然继续存在,不会被回收,只是其临时引用变量被干掉而已)。

  常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。虚拟机必须为每个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集和,包括直接常量(string,integer和 floating point常量)和对其他类型,字段和方法的符号引用。对于String常量,它的值是在常量池中的。而JVM中的常量池在内存当中是以表的形式存在的,对于String类型,有一张固定长度的CONSTANT_String_info表用来存储文字字符串值。在程序执行的时候,常量池会储存在 Method Area,而不是堆中。

  String常量池可以再运行时填充,需要调用String.intern();存在于.class文件中的常量池,在运行期被JVM装载,并且可以扩充。当一个String实例str调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用;

  如果原先str引用的是堆中的对象:

  str=str.intern();//原先堆中的对象会变成垃圾。

  结论:

  a.栈中用来存放一些原始数据类型的局部变量数据和对象的引用(String,数组.对象等等)但不存放对象内容

  b.堆中存放使用new关键字创建的对象.

  c.字符串是一个特殊包装类,其引用是存放在栈里的,而对象内容必须根据创建方式不同定(常量池和堆).有的是编译期就已经创建好,存放在字符串常量池中,而有的是运行时才被创建.使用new关键字(或隐含使用new),存放在堆中。

  ————————————————————————————————

  什么时候编译器时就创建好,什么时候只会在运行时创建?

  像"a"+"b"这种属于《java语言规范》中的编译时常量的概念

  Compile-time constants of type String are always "interned" so as to share unique instances, using the method String.intern.

  关于字符串+:

  If only one operand expression_r_r is of type String, then string conversion is performed on the other operand to produce a string at run time. The result is a reference to a String object (newly created, unless the expression_r_r is a compile-time constant expression_r_r (§15.28))that is the concatenation of the two operand strings.

  1.String str="a"+"b";//OK!Compile-Time constants!

  2.String str1="b";

  String str2="a"+str1;//No!

  //str1是可变的,如果这样支持,那么必须支持

  String str1="b";

  ...//一堆语句,str1可能根据运行时内容跳转,得到不同的值等

  String str2="a"+str1;

  3.final String str1="b";

  String str2="a"+str1;//OK!

  4.final String str1=fun();//可能返回不同内容,只有运行时才明了

  String str2="a"+str1;//No!