深度解析String

先搬砖,后砌墙

感谢原创


可能很多java的初学者对String的存储和赋值有迷惑,以下是一个很简单的测试用例,你只需要花几分钟时间便可理解。

1.在看例子之前,确保你理解以下几个术语:

 

:由JVM分配区域,用于保存线程执行的动作和数据引用。栈是一个运行的单位,Java中一个线程就会相应有一个线程栈与之对应。主要保存基本类型(或者叫内置类型)(char、byte、short、int、long、float、double、boolean)和对象的引用,数据可以共享,速度仅次于寄存器(register),快于堆。

:由JVM分配的,用于存储对象等数据的区域。用于存储对象。

 

常量池 :在堆中分配出来的一块存储区域,用于存储显式的String,float或者integer.例如String str="abc"; abc这个字符串是显式声明,所以存储在常量池。

 

String str1 = "abc";引用的对象在栈(或者叫String池)中。
String str1 =new String ("abc"); 引用的对象在内存/堆中。

"ab".concat("c")与"ab"+"c"创建的方式不同。前者是在堆中新创建一个字符串对象。

String str2 = str3+str4; 在堆中,因为+号的作用是返回另外一个新建的String对象,而不是在String pool中找string这个值。如果是String str2 = "str"+"ing";那最后的结果就在String pool中。str1==str2为true

public static final String A = "ab"; // 常量A

    public static final String B = "cd"; // 常量B

String s = A + B;  // 将两个常量用+连接对s进行初始化 

看这个例子,用JDK5+junit4.5写的例子,完全通过测试

    1. public class StringTest{  
    2.   
    3.     @Test  
    4.     public void testTheSameReference1(){  
    5.         String str1="abc";  
    6.         String str2="abc";  
    7.         String str3="ab"+"c";  
    8.         String str4=new String(str2);  
    9.           
    10.         //str1和str2引用自常量池里的同一个string对象  
    11.         assertSame(str1,str2);  
    12.         //str3通过编译优化,与str1引用自同一个对象  
    13.         assertSame(str1,str3);  
    14.         //str4因为是在堆中重新分配的另一个对象,所以它的引用与str1不同  
    15.         assertNotSame(str1,str4);  
    16.     }  
  • 第一个断言很好理解,因为在解析的时候,"abc"被存储在常量池中,str1和str2的引用都是指向常量池中的"abc"。所以str1和str2引用是相同的。
  • 第二个断言是由于编译器做了优化,编译器会先把字符串拼接,再在常量池中查找这个字符串是否存在,如果存在,则让变量直接引用该字符串。所以str1和str3引用也是相同的。
  • str4的对象不是显式赋值的,编译器会在堆中重新分配一个区域来存储它的对象数据。所以str1和str4的引用是不一样的。

 

 

有几点问题请大家注意:
1.String a; 与String a=null在作为类变量时候是等价的,在局部变量则不同.null表示一个空引用,String a=null意思是在栈中声明了a,但是这个a没有指向任何地址.此时我们注意到String a 在栈中声明了a,但是也没有指向任何地址,但是java的语法检查如果在局部变量中,String a;是不能直接使用的,String a=null中的这个a可以直接使用.因为成员变量会被系统默认初始化,局部变量没这功能,所以必须自己初始化。
2.单独使用""引号创建的字符串都是常量,编译期就已经确定存储到String Pool中.
3.使用new String("")创建的对象会存储到堆中,是运行期新创建的.
4.使用只包含常量的字符串连接符如"aa"+"aa"创建的也是常量, 编译期就能确定,已经确定存储到String Pool中.
5.使用包含变量的字符串连接符如"aa"+ s1创建的或使用concat连接的对象是运行期才创建的,存储在堆中.
6.String类有一个特殊的创建方法,就是使用""双引号来创建.例如new String("i am")实际创建了2个
String对象,一个是"i am"通过""双引号创建的,另一个是通过new创建的.只不过他们创建的时期不同,
一个是编译期,一个是运行期!
7.对于""内容为空的字符串常量,会创建一个长度为0,内容为空的字符串放到String Pool中,
而且String Pool在运行期是可以动态扩展的.

 

final只对引用的“值”(也即它所指向的那个对象的内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指向的对象的变化,final是不负责的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值