牛骨文教育服务平台(让学习变的简单)
博文笔记

创建string 对象的内存分配

创建时间:2016-12-15 投稿人: 浏览次数:276

在日常的开发中 经常会用到string 对象,而且在面试中也会问到 关于string内存分配的问题 今天我写下的文章 只是对这2天string的研究和观看别人博客的总结,发现网上对于string的说法众说纷纭,我只是通过现有的知识量来看string,错误于不到之处希望大神不吝赐教。
1 首先string 是一个包装类 包装的是 char 基础类型 。而且不可变的,下面是stirng 源码中的注释 * Strings are constant; their values cannot be changed after they * are created. String buffers support mutable strings. * Because String objects are immutable they can be shared. 这里说了string不可变得,但是到底为啥不可变呢? string 是个对象 它吧值存在了哪里呢? 下面的代码说明了上面的疑问。 public final class String implementsjava.io.Serializable,Comparable<String>,CharSequence { /** The value is used for character storage. */ private final char value[]; 注意 看 value[]的注释 -这个value 被用来存储字符-,也就是说 string 存储的是 char类型,而char类型的数据 都被存储在 value[]数组中。 而且它是的修饰符是final ,也就是不可变得。对于这个又一些性能方面的问题 string a1="abc"; for(int i=;i<10:i++){ a1=a1+"cd"; } 这种情况就会出现大量的 string 对象被创建 从而消耗性能,而用 stringbuffer 可以很好的解决这个问题,因为它是可变化的。 那么string 对象在内存中是怎么存储的呢?string a1= “abc”;和 string a2=new string("abc");有啥区别? 又有什么关系呢?请看下面 2 首先 常量池的概念 : 常量池是 方法区的一部分,它用于保存编译期 生成的各种字面量和符号引用。注意是编译期。下面看string 对象的创建方式 ,string对象比较特殊 她可以用直接声明的方式去创建 string对象。 对于string a1="abc"; 在编译期间 由于abc是字面量,所有会在常量池中创建一个可以共享的 string 对象“abc”,然后在 站中 声明了一个引用 a1 并且指向 string 对像"abc"的值。也就是说 只要看到 " " 被双引号包起来就会创建一个string 对象 存在 常量池中。又因为 常量池是可以共享的 所以 再次创建 string a2=“abc”的时候,首先去常量池中找是否存在 “abc” ,如果存在则 直接吧 “abc”的地址直接给 a2,否则创建一个新的“abc”。证明: a1==a2//true == 比较的是 地址,所以说 a1 和 a2都指向了同一个地址。 string a3=new String("abc"); 这个时候做了什么操作呢? 首先要创建一个对象 首先 要看他的构造方法,这里用了string 其中的一个构造方法。下面是源码: /** * Initializes a newly created {@codeString} object so that it represents * the same sequence of characters as the argument; in other words, the * newly created string is a copy of the argument string. Unless an * explicit copy of {@codeoriginal} is needed, use of this constructor is * unnecessary since Strings are immutable. * *@paramoriginal * A {@codeString} */ publicString(String original) { this.value= original.value; this.hash= original.hash; }
大家可以看到 这里传递过来的是个 string 对象的引用,而且上面也说过 “”用双引号的就是默认创建一个对象,但是具体创建了没有需要看常量池中有没有对象,当存在的时候吧地址给original 当不存在的时候创建一个"abc"对象并且吧地址给original 。而这个引用干了什么事情呢? 大家在上面可以看到 每个string 都有一个存储 char类型的 final的数组。这个构造方法就是吧 char类型的书组从引用对象中 复制一份到 vaue 中,构造器初始化完数据后,创建对象,然后这个对象是new 修饰的 ,所以会在堆中分配内存,然后吧 地址给 a3.所以说,这其实是两个操组,一个是在 常量池中 一个是在堆中,吧常量池中的对象复制一份存到 堆中这就是 string a3=NEW string("abc");做的事情。 验证 a3==a1//false a3.equal(a1)//true 其中 equals中 做了什么操作 请看源码 public booleanequals(Object anObject) { if(this== anObject) { return true; } if(anObjectinstanceofString) { String anotherString = (String) anObject; intn =value.length; if(n == anotherString.value.length) { charv1[] =value; charv2[] = anotherString.value; inti =0; while(n-- !=0) { if(v1[i] != v2[i]) return false; i++; } return true; } } return false; } 大家进去 debug 就会发现 new 对象 和 =“abc”直接生成的对象的地址是不一样的,当不一样的时候 判断 string 对象中的 char 类型 书组一个个的比对是否一样。 总结一下 string a3=new String("abc");
等价于 stirng a1="abc";//常量池 Stirng a3=new String(a1);//堆
3 String s1 = "Hello";String s2 = "Hello";String s3 = "Hel" + "lo"; System.out.println(s1 == s3);// true   s1 == s3这个地方有个坑,s3虽然是动态拼接出来的字符串,但是所有参与拼接的部分都是已知的字面量,在编译期间,这种拼接会被优化,编译器直接帮你拼好,因此String s3 = "Hel" + "lo";在class文件中被优化成String s3 = "Hello";,所以s1 == s3成立。
String s4 = "Hel" +new String("lo"); System.out.println(s1 == s4);// false   s1 == s4当然不相等,s4虽然也是拼接出来的,但new String("lo")这部分不是已知字面量,是一个不可预料的部分,编译器不会优化,必须等到运行时才可以确定结果,结合字符串不变定理,鬼知道s4被分配到哪去了,所以地址肯定不同。

以上是我对string对象的理解 有错误之处请留言






声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。