Java String类型剖析及其JVM内存分配详解

前提:先了解下什么是声明,什么时候才算是产生了对象实例

其中x并未看到内存分配,变量在使用前必须先声明,再赋值,然后才可以使用。java基础数据类型会用对应的默认值进行初始化

 

一、首先看看Java虚拟机JVM的内存块及其变量、对象内存空间是怎么存储分配的

       1、栈存放基本数据类型及对象变量的引用,对象本身不存放于栈中而是存放于堆中

             1)、基础类型 byte (8位)、boolean (1位)、char (16位)、int (32位)、short (16位)、float (32位)、double (64位)、long (64位)

             2)、java代码作用域中定义一个变量时,则java就在栈中为这个变量分配内存空间,当该变量退出该作用域时,java会自动释放该变量所占的空间

       2、堆new操作符的对象

             1)、new创建的对象和数组

             2)、在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理

       3、静态域static定义的静态成员变量

       4、常量池存放常量

二、Java String类型

        Java中String不是基本数据类型,而是一种特殊的类。String代表的是不可变的字符序列,为不可变对象,一旦被创建,就不能修改它的值,对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去

三 、String实例代码分析

[java]  view plain copy
  1. package terry.java.base;  
  2.   
  3. public class StringTest {  
  4.      public static void main(String[] args) {  
  5.       String a = "hello";  
  6.       String b = "hello";  
  7.         
  8.       String newA = new String("hello");  
  9.       String newB = new String("hello");  
  10.         
  11.       System.out.println("****** Testing Object == ******");  
  12.       System.out.println("a==b ? :" + (a==b));  
  13.       System.out.println("newA==newB ? :" +(newA==newB));  
  14.       System.out.println("a==newA ? :" + (a==newA));  
  15.         
  16.       System.out.println("***** Testing String Object intern method******");  
  17.       System.out.println("a.intern()==b.intern() ? : " + (a.intern()==b.intern()));  
  18.       System.out.println("newA.intern()==newB.intern() ? :" + (newA.intern()==newB.intern()));  
  19.       System.out.println("a.intern()==newA.intern() ? :" + (a.intern()==newA.intern()));  
  20.       System.out.println("a=a.intern() ? :" + (a==a.intern()));  
  21.       System.out.println("newA==newA.intern() ? : " + (newA==newA.intern()));  
  22.         
  23.       System.out.println("****** Testing String Object equals method******");  
  24.       System.out.println("equals() method :" + a.equals(newA));  
  25.         
  26.       String c = "hel";  
  27.       String d = "lo";  
  28.       final String finalc = "hel";  
  29.       final String finalgetc = getc();  
  30.         
  31.       System.out.println("****** Testing Object splice ******");  
  32.       System.out.println("a==\"hel\"+\"lo\" ? :" + (a=="hel"+"lo"));  
  33.       System.out.println("a==c+d ? : " + (a==c+d));  
  34.       System.out.println("a==c+\"lo\" ? : " + (a==c+"lo"));  
  35.       System.out.println("a==finalc+\"lo\" ? :" + (a==finalc+"lo"));  
  36.       System.out.println("a==finalgetc+\"lo\" ? :" + (a==finalgetc+"lo"));  
  37.         
  38.      }  
  39.      private static String getc(){  
  40.       return "hel";  
  41.      }   
  42. }  

Run As Java Application -- 输出结果:

[java]  view plain copy
  1. ****** Testing Object == ******  
  2. a==b ? :true  
  3. newA==newB ? :false  
  4. a==newA ? :false  
  5. ***** Testing String Object intern method******  
  6. a.intern()==b.intern() ? : true  
  7. newA.intern()==newB.intern() ? :true  
  8. a.intern()==newA.intern() ? :true  
  9. a==a.intern() ? :true  
  10. newA==newA.intern() ? : false  
  11. ****** Testing String Object equals method******  
  12. equals() method :true  
  13. ****** Testing Object splice ******  
  14. a=="hel"+"lo" ? :true  
  15. a==c+d ? : false  
  16. a==c+"lo" ? : false  
  17. a==finalc+"lo" ? :true  
  18. a==finalgetc+"lo" ? :false  


内存分析:

上述各个变量及引用在JVM分配的内存情况

String类型对象实例直接赋值和new操作符产生的结果在JVM内存分配过程是不同的,如下注释说明

[java]  view plain copy
  1. String a = "hello"//先在栈中创建一个对String类的对象引用变量a,然后通过符号引用去字符串常量池里找有没有"hello",如果没有,则将"hello"存放进字符串常量池 ,并令a指向"hello",如果已经有"hello"则直接将a指向"hello"    -->  产生1个对象及1个引用  
  2. String b = "hello"//先在栈中创建一个对String类的对象引用变量b,然后通过符号引用去字符串常量池里找有没有"hello",因为之前在常量池中已经有"hello",所以直接将b指向"hello"    -->  因为不需要在常量池产生"hello",所以只是在栈中产生1个引用  
  3. String newA = new String("hello"); //先在栈中创建一个对String类的对象引用变量newA,然后new()操作会在heap堆中产生一个新的对象"hello",并将newA指向堆中的"hello",同时检查String pool常量池中是否有对象"hello",如果没有也产生一个对象"hello",如果有则不产生,因为这里之前已经在常量池中产生过了,所以   -->  只需要产生1个对象及1个引用  
  4. String newB = new String("hello");  //因为new每次都会保证在heap堆内存中产生新的对象,并将栈中的引用指向对应的堆中的地址,所以此语句同上一条的处理  

 

String常量+的拼接 及 String常量与引用实例+的拼接 的区别

[java]  view plain copy
  1. System.out.println("a==\"hel\"+\"lo\" ? :" + (a=="hel"+"lo"));  //JVM对于字符串常量的"+"号连接,在程序编译期,JVM就将常量字符串的"+"连接优化为连接后的值,因此"hel" + "lo"优化后完全等同于"hello"  
  2. System.out.println("a==c+d ? : " + (a==c+d));   
  3. System.out.println("a==c+\"lo\" ? : " + (a==c+"lo"));  //JVM对于字符串引用,由于在字符串的"+"连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,所以c+\"lo\"实际是c在栈中保存的地址+字符串"lo"于常量池中指向的地址 所指向的在堆中新分配的一块内存空间  
  4. System.out.println("a==finalc+\"lo\" ? :" + (a==finalc+"lo"));  //对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量 池中或嵌入到它的字节码流中,在编译期就已经确定了内存空间地址,所以此类似于2个字符串常量的+  
  5. System.out.println("a==finalgetc+\"lo\" ? :" + (a==finalgetc+"lo"));  


关于String对象的intern()方法的说明

一个初始时为空的字符串池,它由类 String 私有地维护

当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法确定),则返回池中的字符串。否则,将此String 对象添加到池中,并且返回此String 对象的引用,因此a.intern(),b.intern(),newA.intern(),newB.intern()隐含的各自在栈中分配了各自的内存区域,同时都将栈中的应用全部指向了String pool常量池中的同一块区域"hello"

 

 以上完全是自己的理解,如有错误,恳请朋友们指正交流!


原文地址:http://blog.csdn.net/yihuiworld/article/details/13511925

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值