JAVA 关于String str1 = “123“ 和 String str2 = new String(“123“)内存分析

最近在自学java基础,由于嵌入式以后的就业前景不是很好,加上自己本学期学习了51单片机发现自己对硬件不是很在行,可能是因为初中以来物理一直不是很好吧,导致自己现在一看到电路板,电压电阻电流都会产生一种恐惧感,就像大三现在的我对与数据结构也有一种畏难情绪(不愿意花很多时间去研究,很多时候数据结构都和数学逻辑有关,但数据结构真的很重要,大家在大学一定要好好学数据结构,以后对你自己编程会有很大帮助)。由于自己学了很长时间的C(记得那时候对指针很感兴趣花了很多课外时间去研究)加上自己本学期学过汇编,涉及到底层。所以每学一门新的语言就会分析它的内存分配问题。

好吧,闲扯了几句,我最近碰到一个很有趣的问题,关于String str = “123” 和String str =new String(“123”) 分析它的内存分配。

在分析上面那个问题之前我们先来补充下JAVA的一些预备知识:

  JAVA中的内存区划分:

    栈区:保存基本数据类型(引用),对象的引用

    堆区:保存每个对象的具体属性

    全局数据区:保存static类型的属性

    全局代码区:保存所有方法的定义

    常量区:字符常量区(“111”、”112”...)和基本数据常量区(1、2.2...)及其他

接下来举个简单的例子,贴代码:

Class Test
{
    Public static int num = 0;  //统计实例化了多少该类的对象
    Private int a; Public Test(int a){   This.a = a; Num++;   }  
    Public void printA(){ System.out.println(a); }
}

Public class TestDemo
{
    Public static void main(String arg[])
    {
         int a = 1;
         int b = 1;
         Test t1= new Test(1);
         t1.printA();
    }
}

  

为了更直观我画了几个图,可能比文字描述更直观。。

在画图之前,我们先来说下java文件的编译过程。。

.java(java文件)经过编译(在dos界面通过javac.exe借助于jdk编译,或者一些IDE自带的编译器编译)  ----》》》》.class字节码文件 在通过jdk中的java虚拟机(jvm)对.class字节码文件在不同的操作系统上  ------》》》运行(关于虚拟机jvm的作用这里不做具体解释,自行百度哈哈哈)

好了上图吧(由于是画图板请点击放大查看)

好了,认真看完上文(扯了这么多。。)

就来分析下String str = “123” 和String str =new String(“123”)的内存分配问题吧。

想要了解一个问题,我们得进入到String内中看它的原型(关于如何看源码一种是看API还一种是自己在调试的时候进入String内后面这种方式得自行百度有详细的步骤)

 public final class String  implements java.io.Serializable, Comparable<String>, CharSequence 
{ /*进入到这是不是有点想起C了,莫名的亲切 */ private final char value[];//加了final关键字说明不可string字符串不可改变 private int hash;// Default to 0 private static final long serialVersionUID = -6849794470754667710L; private static final ObjectStreamField[]serialPersistentFields = new ObjectStreamField[0];   public String() { this.value ="".value; } public String(Stringoriginal) { this.value =original.value; this.hash =original.hash; } public String(char value[]) { this.value = Arrays.copyOf(value,value.length); } }

这里重点说两个问题:

一个是private final char value[]说明了String类中并没有给字符串的内存分配空间只是分配了一个final char* 指针

二是String类的构造函数 public String(String original)

public String(char value[]),当String str = new String(“xxx”);时是调用那个构造函数呢。

接下来上代码

按F11进行调试,F5进入String构造函数

 

注意看调用了public String(Stringoriginal)构造函数,而且是value值之间的相互赋值...

所以到了这里可以简单画一个内存分析图了

分析:

string str1 =“123”,如果string池之前没有”abc”对象就现在str池实例化一个”abc”对象,然后str1引用指向它(假设这时候在来一句string copyStr1=”abc”, 就同时指向了对象”abc”了)

思考一下这样做的目的其实是java为了节约内存,只在第一次的时候分配内存,后面如果有其他的引用指向相同的字符串对象就只需要在栈区分配一个引用的空间了。是不是很赞(可以用System.out.println(str1==copyStr1))验证为True,说明指向的是同一块空间

  String str2 = new String(“abc”);在堆区分配对象的内存然后在调用构造函数将”abc”.value的值赋值给str2对象的value,它们是不同的对象(可以用System.out.println(str1==str2)验证为False),但它们的value值是相同的都是指向同一个字符串常量”abc”;

综上所述:String类虽然叫字符类,但String只是一个包装类,并没有为字符串分配空间去存取它们,只留了一个value值去指向字符串常量

所以:虽然string str1 = “123” 和String str2 = new String(“abc”)能达到相同的目的,但是出于内存节约的原则,建议用前者,给堆区节省内存

补充一点关于String池:这部分内存应该在堆区jvm自动分配的,有些书上会写成匿名(String类)对象,这部分内存应该是有java自己的(GC- java回收机制)自己去回收。。。

留一个问题:假如你去面试java有关的工作,技术官问你

string str1 = “123” 和String str2 = new String(“abc”);分别分配了几次内存???仔细想想

最后关于补充两个小问题:

1> JAVA中int a = 1和int b = 1的内存分配我发表一下自己看法(可能不正确)

我们在C 语言中我们知道这肯定是分配了两个int的内存然后值都为1,而且我们输出a和b的地址 并且a和b的地址不相同。(自己用vs、vc++试验一下)这里就不截图了

但是由于java中没有指针的概念不能与内存直接沟通,所以这里没法验证地址。

但是这里有一个很有趣问题或许可以给我们启发:在C语言中比如直接定义int a;然后输出a结果是个垃圾值。。。但是在java中如果没有赋初值输出的时候会编译报错

 我们是不是可以这样想,既然要输出a的值,编译器可能要去常量区找值,结合开头我们提到,a指向常量值(保存某个常量值的地址)这里未初始化,也就是说a没有存地址,这时候编译运行的时候发现a没有地址就不知道怎么去常量区取值了(这只是个人猜想,有待我自己翻阅资料验证)。

2> 关于str1 = str1 +“xxx” 及字符串比较的问题

我们不是说String类一旦确定就不能改变的么(string语言中有一个value加了final关键字,代表value的指向字符串不可以改变)

上一个代码:

输出结果如下,(自己可以尝试画画内存图,分析一下)

我们进入"123abc".equals(str1)的源码:

红色框框里面的是不是很熟悉?其实就是我们C语言里很简单的字符串比较。

但是为什么第一个输出为false呢?我们可以推测如果直接==的话应该是比较对象地址的值,也就是引用的值,你想一下str1和匿名对象”123abc”在堆栈不同的内存块中,肯定不相等是吧。

附上我自己画的很丑的图。

基于上图的内存分析图:str1 = str1 + “abc”,在堆中又重新分配了一块堆内存(虽然后面GC会自动回收但还是浪费了堆内存)。

所以我们不建议这么做(自己可以看java书,用StringBuffer类比较合适)。

结尾:

由于这是本人的第一篇博客,语言组织可能有点杂乱(个人觉得写博客是一件很酷的事情,可以把自己想法传到博客和别人分享)所以一定会有很多地方写的不够专业,如果各位读者看到了有什么错误,或者觉得有疑问,和自己平时的认知有冲突的,可以在下面留言板给我留言,大家一起交流,一起进步  -小蜗牛 2017年

原文地址:https://www.cnblogs.com/stephenzengx/p/13679637.html