String对象问题

String是Java中使用频率第一的类,以前在CSDN论坛上,至少每个月都有相关的贴子,讨论==和equals()。

本节介绍这一部分的内容,也有一个重要更正


String文字在程序中被大量地使用。String文字作为引用,指向一个String对象。例如"baby"指向的一个String对象,该对象保存的数据主要有一个char[]引用和3个int值

字符串拘留

为了有效地利用堆,加快字符串处理效率(以==比较替代equals(Object)比较),多种语言如Java、C#和Python等,都支持字符串拘留/集中营(string interning)技术,即对每一个不同的字符串值仅保存一个拷贝(前提是它必须是不变对象)。

Java中两种拘留方式:

编译器将一个类中所有的String文字和常量表达式(如"ba"+"by"、"ba"+3+2等)加以分析,求出常量表达式的结果——String文字,然后仅仅将不同的String文字表示为class文件的各个CONSTANT_String_info项(相同的这时就统一了)。在类载入时,按照它的符号引用CONSTANT_Utf8_info,提取二进制表示的各字符并在“堆”中创建String对象,并将该对象的引用在一个HashMap中注册。

在HashMap中注册过的所有String对象的集合,有时候称为字符串池(string interning pool)。该HashMap驻留在方法区,而字符串池在堆中(注意,如同Java不在栈中分配对象空间一样,仅仅具有逻辑上的含义)。

[java]  view plain copy
  1. package jvm.internedStrings;  
  2. public class OnlyOneCopy{  
  3.     static String str1 = "abc";   
  4.     String str2 = "a"+"bc";   
  5.     public void foo(){  
  6.         String str3 = "a"+"b"+'c'// 'c'不是"c"  
  7.         System.out.println(str1==str3);  
  8.     }  
  9. }  

载入OnlyOneCopy 时,JVM按照class文件的常量池中CONSTANT_String_info项创建一个String对象。因为编译器自动支持字符串拘留技术,因而将刚才创建的String对象的引用"abc"在HashMap中注册并交给不知名变量(假定为#2)保存。类的初始化阶段,静态变量str1被初始化,即取出#2的引用赋值"abc"给str1;当在某处创建OnlyOneCopy对象时,将初始化其实例域str2,即将#2的值赋值给str2;当某个程序调用方法foo()时,将#2的值赋值给str3。【引用变量可以在各种位置


最后的结果是:通过==可以判断str1、str2和str3三者指向同一个对象

但要注意,如果字符串的连接操作符中包含变量,则编译器无法足够聪明地确定该表达式的值。例如String str1 = "abc";String str2 =str1+ "";

str1和 str2 指向不同的对象。

如果要减少多个字符串拷贝,有两个手段达到该目的:以final修饰str1;使用String的intern()方法,如str2= (str1+""). intern()。

xxx.intern()意味着将xxx的内容通过equals(Object)方法,判断HashMap中是否存在一个可用的引用。如果存在则将该引用作为xxx自己的引用;如果不存在,则将xxx的值在HashMap中注册,本String对象入池。字符串池中的String对象能否被垃圾回收?在现代的JVM实现中,一个被拘String对象,如果它不是编译时常量而且它不再被引用,则可以被垃圾回收。


字符子串


String 类的substring(int begin,intend)返回消息接受对象的范围为[begin.. end)的子字符串对象,而且该String对象是没有被拘留的。

我们知道,String对象保存的数据主要有一个char[]引用和几个int值。JVM创建子字符串对象本身的代价极小,代价主要在char[]变量v指向的char[]。

String str =  "abc……十万个为什么".substring(0,1);

创建的子字符串对象时,并不需要复制任何字符,子字符串和原字符串对象共享一个底层char[]对象,子字符串不过对原String对象的几个int成员变量(偏移量、长度等)加以更改。

这句话在Java 7u6之前是对的,但是之后已经被修改。原因是str只需要一个字符但是整个char[]-10W长度的空间得不到释放。

[java]  view plain copy
  1. //JDK 6  
  2. String(int offset, int count, char value[]) {  
  3.     this.value = value;  
  4.     this.offset = offset;  
  5.     this.count = count;  
  6. }  
  7.    
  8. public String substring(int beginIndex, int endIndex) {  
  9.     //check boundary  
  10.     return  new String(offset + beginIndex, endIndex - beginIndex, value);  
  11. }  

[java]  view plain copy
  1. //JDK 7  
  2. public String(char value[], int offset, int count) {  
  3.     //check boundary  
  4.     this.value = Arrays.copyOfRange(value, offset, offset + count);  
  5. }  
  6.    
  7. public String substring(int beginIndex, int endIndex) {  
  8.     //check boundary  
  9.     int subLen = endIndex - beginIndex;  
  10.     return new String(value, beginIndex, subLen);  
  11. }  

【图 7-10 子字符串对象】从上图更正为下图。



练习7-1.:没有其他"abc"干扰时,String str = new String("abc") 会在堆中创建几个String对象

练习7-2.:介绍字符串拘留 (string interning)技术的意义。提示:空间和时间

练习7-3.:String文字可以被垃圾回收吗?

练习7-4.:编程:大量拘留String,观察在字符串较小如"1"、"2"和较大如"interninterninterninternintern1"时PermGen space能够拘留String的个数。

  练习7-x1.:

String str1 = "abc"; 
String str2 = str1.substring(0,1); // "a" 
System.out.println("a"==str2);

输出:_____ ;原因_____________。

练习7-x2.:为什么在Java7u6之后,程序员不需要写这样的代码:

String str =  "abc……十万个为什么";

str =  str.substring(0,1)+"";

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值