String 内在分配解析
1.String类概念
(1)String是final的,不可被继承。public final class String。String是的本质是字符数组char[], 并且其值不可改变。private final char value[];
(2)Java运行时会维护一个String Pool(String池)。String池用来存放运行时中产生的各种字符串,并且池中的字符串的内容不重复。而一般对象不存在这个缓冲池,仅仅存在于方法的堆栈区。
(3)创建字符串的方式很多,归纳起来有三类:(1)使用new关键字创建字符串-->String s1 = new String("abc");(2)直接指定-->String s2 = "abc";(3)用串联生成新的字符串-->String s3 = "ab" + "c";
2.String对象创建的机制
原理1:当使用任何方式来创建一个字符串对象s时,Java运行时(运行中JVM)会拿着这个s在String池中找是否存在内容相同的字符串对象,如果不存在,则在池中创建一个字符串s,否则,不在池中添加。例如:String str="abc";这行代码被执行的时候,JAVA虚拟机首先在字符串池中查找是否已经存在了值为"abc"的这么一个对象,它的判断依据是String 类equals(Object obj)方法的返回值。如果有,则不再创建新的对象,直接返回已存在对象的引用;如果没有,则先创建这个对象,然后把它加入到字符串池中,再将它的引用返回。
原理2:Java中,只要使用new关键字来创建对象,则一定会(在堆区或栈区)创建一个新的对象。而不在常量池中创建String对象,只有使用了str.intern(),才会在常量池中创建一个String对象。
原理3:使用直接指定或者使用纯字符串串联来创建String对象,则仅仅会检查维护String池中的字符串,池中没有就在池中创建一个,有则罢了!但绝不会在堆栈区再去创建该String对象。
原理4:使用包含变量的表达式来创建String对象,则不仅会检查维护String池,而且还会在堆栈区创建一个String对象。
最后,有几点问题请大家注意:String a; 与String a=null在作为类变量时候是等价的,在局部变量则不同。null表示一个空引用,String a=null意思是在栈中声明了a,但是这个a没有指向任何地址。此时我们注意到String a在栈中声明了a,但是也没有指向任何地址,但是java的语法检查如果在局部变量中,String a是不能直接使用的,String a=null中的这个a可以直接使用。
3.经典面试题
(1)String s = new String("abc");创建了几个String Object? 答:[两个,pool中1个,heap中1个]
(2)String s0 = new String("abc");String s1 = new String("abc");创建了几个String Object?答:[三个,pool中1个,heap中2个]
(3)解释
涉及概念:字符串池[pool of literal strings]的字符串对象和堆[heap]中的字符串对象。
字符串对象的创建:由于字符串对象的大量使用[它是一个对象,一般而言普通对象总是在heap分配内存],Java中为了节省内存空间和运行时间,在编译阶段就把所有的字符串文字放到一个字符串池[pool of literal strings]中,而运行时字符串池成为常量池的一部分。字符串池的好处,就是该池中所有相同的字符串常量被合并,只占用一个空间。另一种解释:在JAVA虚拟机(JVM)中存在着一个字符串池,其中保存着很多String对象,并且可以被共享使用,因此它提高了效率。由于String类是final的,它的值一经创建就不可改变,因此我们不用担心String对象共享而带来程序的混乱。字符串池由String类维护,我们可以调用intern()方法来访问字符串池。
现在看String s = new String("abc")语句,在执行new String()时,先检查pool中有没有"abc"对象,如果没有,则先在pool中创建一个"abc"对象,再将其复制一份放到heap中,并且把heap中的这个对象的引用交给s持有,这条语句就创建了2个String对象;如果有则将pool中的对象复制一份放到heap中,并且把heap中的这个对象的引用交给s持有,这条语句就创建了1个String对象(也可以理解为:如果有则新创建的"abc"对象将原有pool中的"abc"对象覆盖,这样可以勉强说创建了2个String对象)。
补充:常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。jdk编译时会将字符串池并入常量池。
4.举例
- public static void main(String[] args) {
- String str_0 = "forrest";
- String str_1 = "forrest";
- if (str_0 == str_1) System.out.println("pool中值创建了一个String对象:/"forrest/", str_0和str_1分别对其进行了引用");
- else System.out.println("Trouble");
- String str_2 = "vivian";
- String str_3 = new String("vivian");
- String str_4 = new String("vivian");
- if (str_2 != str_3) System.out.println("str_2是对pool中String对象引用, str_3是对heap中String对象引用, 所以二者不是引用的同一个对象");
- else System.out.println("Trouble");
- if (str_2 != str_4) System.out.println("str_2是对pool中String对象引用, str_4是对heap中String对象引用, 所以二者不是引用的同一个对象");
- else System.out.println("Trouble");
- if (str_3 != str_4) System.out.println("str_3和str_4都是对heap中String对象引用, 但二者引用对象的内存地址不同, 所以二者不是引用的同一个对象");
- else System.out.println("Trouble");
- String str_5 = str_3.intern(); //把str_3在heap中的String对象复制到loop中(虽然在String str_3 = new String("vivian")时已经在loop中创建了一份, 但intern()方法会要求重新复制),并将loop中这个对象引用到str_5
- if (str_5 != str_3) System.out.println("str_5是对pool中String对象引用, str_3是对heap中String对象引用, 所以二者不是引用的同一个对象");
- else System.out.println("Trouble");
- if (str_5 == str_2) System.out.println("str_5和str_2都是对loop中某一String对象引用, 所以二者引用的是同一个对象");
- else System.out.println("Trouble");
- str_4.intern();
- if (str_4 != str_2) System.out.println("str_4虽然执行了str_4.intern(), 但它的返回值没有赋给str_4, 所以str_4和str_2不是引用的同一个对象");
- else System.out.println("Trouble");
- if (str_4.intern() == str_2) System.out.println("str_4.intern()和str_2都是对loop中某一String对象引用, 所以二者引用的是同一个对象");
- else System.out.println("Trouble");
- if (str_4.intern() != str_4) System.out.println("str_4.intern()是对pool中String对象引用, str_4是对heap中String对象引用, 所以二者不是引用的同一个对象");
- else System.out.println("Trouble");
- }
- /*
- * 输出为:
- * pool中值创建了一个String对象:"forrest", str_0和str_1分别对其进行了引用
- * str_2是对pool中String对象引用, str_3是对heap中String对象引用, 所以二者不是引用的同一个对象
- * str_2是对pool中String对象引用, str_4是对heap中String对象引用, 所以二者不是引用的同一个对象
- * str_3和str_4都是对heap中String对象引用, 但二者引用对象的内存地址不同, 所以二者不是引用的同一个对象
- * str_5是对pool中String对象引用, str_3是对heap中String对象引用, 所以二者不是引用的同一个对象
- * str_5和str_2都是对loop中某一String对象引用, 所以二者引用的是同一个对象
- * str_4虽然执行了str_4.intern(), 但它的返回值没有赋给str_4, 所以str_4和str_2不是引用的同一个对象
- * str_4.intern()和str_2都是对loop中某一String对象引用, 所以二者引用的是同一个对象
- * str_4.intern()是对pool中String对象引用, str_4是对heap中String对象引用, 所以二者不是引用的同一个对象
- *
- * */
- public static void main(String[] args) {
- String a = "ab";
- String b = "cd";
- String c = "abcd";
- String d = "ab" + "cd";
- // 如果d和c指向了同一个对象,则说明d被加入字符串池
- if (c == d) System.out.println("/"ab/"+/"cd/" 创建的对象 /"加入了/" 字符串池中");
- else System.out.println("/"ab/"+/"cd/" 创建的对象 /"没加入/" 字符串池中");
- String e = a + "cd";
- // 如果e和c指向了同一个对象,则说明e也被加入了字符串池
- if (e == c) System.out.println("a +/"cd/" 创建的对象 /"加入了/" 字符串池中");
- else System.out.println("a +/"cd/" 创建的对象 /"没加入/" 字符串池中");
- String f = "ab" + b;
- // 如果f和c指向了同一个对象,则说明f也被加入了字符串池
- if (f == c) System.out.println("/"ab/"+ b 创建的对象 /"加入了/" 字符串池中");
- else System.out.println("/"ab/"+ b 创建的对象 /"没加入/" 字符串池中");
- String g = a + b;
- // 如果g和c指向了同一个对象,则说明g也被加入了字符串池
- if (g == c) System.out.println("a + b 创建的对象 /"加入了/" 字符串池中");
- else System.out.println("a + b 创建的对象 /"没加入/" 字符串池中");
- }
- /*
- * 输出为:
- * "ab"+"cd" 创建的对象 "加入了" 字符串池中
- * a +"cd" 创建的对象 "没加入" 字符串池中
- * "ab"+ b 创建的对象 "没加入" 字符串池中
- * a + b 创建的对象 "没加入" 字符串池中
- *
- * */
从第二段代码结果中我们不难看出,只有使用引号包含文本的方式创建的String对象之间使用“+”连接产生的新对象才会被加入字符串池中。对于所有包含new方式新建对象(包括null)的“+”连接表达式,它所产生的新对象都不会被加入字符串池中;对于类似str=1+"AAA"+2+3这种强制类型转化形成的String对象也不会放到pool中的。对此我们不再赘述。因此我们提倡大家用引号包含文本的方式来创建String对象以提高效率,实际上这也是我们在编程中常采用的。
String 内在分配解析的更多相关文章
- String内存分配
Java 把内存划分成两种:一种是栈内存,另一种是堆内存.在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的 栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存 ...
- C# 由范式编程==运算符引发对string内存分配的思考
今天在看C#编程指南时(类型参数的约束http://msdn.microsoft.com/zh-cn/library/d5x73970.aspx)看到一段描述: 在应用 where T : class ...
- JavaScript (JS)基础:DOM 浅析 (含数组Array、字符串String基本方法解析)
①文本对象document: 例如:document.getElementById() 只获取一个对象 document.getElementsByTagName() 获取 ...
- 对String的内存解析
@Test public void stringTest(){ /* * str1和str2地址指向字符串常量池 * 解析: str1 在字符串常量池中创建出java 地址例如为:0x456 * st ...
- STM32 内存分配解析及变量的存储位置
内存映射 在一些桌面程序中,整个内存映射是通过虚拟内存来进行管理的,使用一种称为内存管理单元(MMU)的硬件结构来将程序的内存映射到物理RAM.在对于 RAM 紧缺的嵌入式系统中,是缺少 MMU 内存 ...
- Java String 演进全解析
前言 String 是我们使用最频繁的对象,使用不当会对内存.程序的性能造成影响,本篇文章全面介绍一下 Java 的 String 是如何演进的,以及使用 String 的注意事项. 下面的输出结果是 ...
- P2066 机器分配 解析
小日记: 1.今天新学的字体颜色,尽管不熟悉,但玩的666,卡星(开心) ╰( ̄▽ ̄)╮╰( ̄▽ ̄)╮╰( ̄▽ ̄)╮╰( ̄▽ ̄)╮╰( ̄▽ ̄)╮╰( ̄▽ ̄)╮ 2.今天油腔滑调,谅解亿下 P2066 ...
- String源码解析(二)
方法的主要功能看代码注释即可,这里主要看函数实现的方式. 1.getChars(char dst[], int dstBegin) /** * Copy characters from this st ...
- java基础(六)-----String性质深入解析
本文将讲解String的几个性质. 一.String的不可变性 对于初学者来说,很容易误认为String对象是可以改变的,特别是+链接时,对象似乎真的改变了.然而,String对象一经创建就不可以修改 ...
随机推荐
- JAVA(int...i)问题
第一次看到这种写法,弄不明白到底会是神马结果,运行后才发现是这么的神奇. 无论你调用的方法中带几位参数,“hello”一直可以被输出.
- hdu 1203 I NEED A OFFER (0-1背包)
题意分析:0-1背包变形 递推公式:dp[i] = max(dp[i], 1-(1-dp[i-C])*(1-p)) /* I NEED A OFFER! Time Limit: 2000/1000 ...
- 解析XML文档之一:使用SAX解析
使用sax解析xml方法总结 解析的的xml文档格式如下 <?xml version="1.0" encoding = "UTF-8"?> < ...
- mongodb gdal 矢量数据格式驱动
写了个mongodb的gdal driver,放在了github上,如果你需要,欢迎加入mongogis group. 直接的效果是使得QGIS, GeoServer, MapServer, ArcG ...
- activiti系列导读
此用于管理activiti系列标签文章,activiti的分析是建立在目前最新的版本5.21之上. 官方指导手册链接:http://www.activiti.org/userguide/index.h ...
- Java中String类型的不可变性和驻留池
一 基本概念 可变类和不可变类(Mutable and Immutable Objects)的初步定义: 可变类:当获得这个类的一个实例引用时,可以改变这个实例的内容. 不可变类:不可变类的实例一但创 ...
- virtualbox安装centos6.5碰到的问题
今天无聊用virtualbox安装centos6.5 , 自己笔记本vm撑不住, 用公司的试试virtualbox先 安装快完成时 没有足够的内存配置kdump”(在英文界面下提示的是“insuffi ...
- 计算 sql查询语句所花时间
--1:下面这种是SQL Server中比较简单的查询SQL语句执行时间方法,通过查询前的时间和查询后的时间差来计算的: declare @begin_date datetimedeclare @en ...
- jQuery ui datepicker 日历转中文
做个笔记,以后详解 jQuery(function($){ $.datepicker.regional['zh-CN'] = { closeText: '关闭', prevText: '<上月' ...
- mac OS X下制定ll指令
ll作为ls -l的快捷方式,但系统本身没有,需要通过如下方法生成 1.在用户目录下新建.bash_profile文件 # vim .bash_profile 2.添加内容 alias ll = 'l ...