深入研究java对String字符串对象的创建以及管理

【优化求解】基于matlab粒子群算法PSO确定最佳开关角对称级联多层逆变器【含Matlab源码 13080期】 粒子群算法PSO确定最佳开关角对称级联多层逆变器 完整代码,包运行;可提供运行操作视频!适合小白! 阅读详情
此文为转载,原文请看这里: http://topic.youkuaiyun.com/u/20080929/02/4e0ef626-98ee-4d6d-96ed-fe40afe8290b.html

为了排版方便,局部内容作了调整。

(原创)深入研究java对String字符串对象的创建以及管理

(
2008-9-27 bbh)
新手初学,不对的地方大家多多指教
~

经常看到很多人讨论java中关于String的问题,我也就有点兴趣了,鉴于网上很多人写的都差别很大,
同样的问题都是不同的说法,我很迷糊,花了一晚上读了Java Virtual Machine Specification和
The Java Language Specification的一些章节,做了很多试验,总结了一下关于String的内容,还
有很多内容我也不确定,在下面也都提出来了,希望高手能指正.


Constant Pool常量池的概念:

在讲到String的一些特殊情况时,总会提到String Pool或者Constant Pool,但是我想很多人都不太
明白Constant Pool到底是个怎么样的东西,运行的时候存储在哪里,所以在这里先说一下Constant Pool的内容.
String Pool是对应于在Constant Pool中存储String常量的区域.习惯称为String Pool,也有人称为
String Constant Pool.好像没有正式的命名
??

在java编译好的class文件中,有个区域称为Constant Pool,他是一个由数组组成的表,类型
为cp_info constant_pool[],用来存储程序中使用的各种常量,包括Class
/String/Integer等各
种基本Java数据类型,详情参见The Java Virtual Machine Specification
4.4章节.


对于Constant Pool,表的基本通用结构为:
cp_info {
u1 tag;
u1 info[];
}


tag是一个数字,用来表示存储的常量的类型,例如8表示String类型,5表示Long类型,info[]根据
类型码tag的不同会发生相应变化.

对于String类型,表的结构为:
CONSTANT_String_info {
u1 tag;
u2 string_index;
}

tag固定为8,string_index是字符串内容信息,类型为:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}

tag固定为1,length为字符串的长度,bytes[length]为字符串的内容.

代码样例

(以下代码在jdk6中编译)
为了详细理解Constant Pool的结构,我们参看一些代码:
String s1
= "sss111";
String s2
= "sss222";
System.out.println(s1
+ " " + s2);

由于
"sss111"和"sss222"都是字符串常量,在编译期就已经创建好了存储在class文件中.
在编译后的class文件中会存在这2个常量的对应表示:
08 00 11 01 00 06 73 73 73 31 31 31 08 00 13 01 ; ......sss111....
00 06 73 73 73 32 32 32 ; ..sss222

根据上面说的String常量结构,我们分析一下
开始的08为CONSTANT_String_info结构中的tag,而11应该是它的相对引用,01为
CONSTANT_Utf8_info的tag,06为对应字符串的长度,
73 73 73 31 31 31为字符串对
应的编码,接着分析,会发现后面的是对应
"sss222"的存储结构.


经过上面分析,我们知道了11和13是两个字符串的相对引用,就可以修改class文件
来修改打印的内容,把class文件中的
00 6E 00 04 00 03 00 00 00 24 12 10 4C 12 12 4D
改成
00 6E 00 04 00 03 00 00 00 24 12 10 4C 12 10 4D
程序就会输出sss111 sss111,而不是和原程序一样输出sss111 sss222,因为我
们把对
"sss222"的相对引用12改成了对"sss111"的相对引用10.


------------分割线
public class Test {
public static void main(String[] args) {
String s1
= "sss111";
String s2
= "sss111";
}
}

在上面程序中存在2个相同的常量
"sss111",对于n个值相同的String常量,在Constant Pool中
只会创建一个,所以在编译好的class文件中,我们只能找到一个对
"sss111"的表示:
000000abh:
08 00 11 01 00 06 73 73 73 31 31 31 ; ......sss111


在程序执行的时候,Constant Pool会储存在Method Area,而不是heap中.

另外,对于
""内容为空的字符串常量,会创建一个长度为0,内容为空的字符串放到Constant Pool中,
而且Constant Pool在运行期是可以动态扩展的.


关于String类的说明

1.String使用private final char value[]来实现字符串的存储,也就是说String对象创建之后,就不能
再修改此对象中存储的字符串内容,就是因为如此,才说String类型是不可变的(immutable).

2.String类有一个特殊的创建方法,就是使用""双引号来创建.例如new String("i am")实际创建了2个
String对象,一个是
"i am"通过""双引号创建的,另一个是通过new创建的.只不过他们创建的时期不同,
一个是编译期,一个是运行期
!

3.java对String类型重载了+操作符,可以直接使用+对两个字符串进行连接.

4.运行期调用String类的intern()方法可以向String Pool中动态添加对象.


String的创建方法

一般有如下几种
1.直接使用""引号创建.
2.使用new String()创建.
3.使用new String("someString")创建以及其他的一些重载构造函数创建.
4.使用重载的字符串连接操作符+创建.

例1
/*
* "sss111"是编译期常量,编译时已经能确定它的值,在编译
* 好的class文件中它已经在String Pool中了,此语句会在
* String Pool中查找等于"sss111"的字符串(用equals(Object)方法确定),
* 如果存在就把引用返回,付值给s1.不存在就会创建一个"sss111"放在
* String Pool中,然后把引用返回,付值给s1.
*
*/
String s1
= "sss111";

//此语句同上
String s2 = "sss111";

/*
* 由于String Pool只会维护一个值相同的String对象
* 上面2句得到的引用是String Pool中同一个对象,所以
* 他们引用相等
*/
System.out.println(s1
== s2); //结果为true


例2
/*
* 在java中,使用new关键字会创建一个新对象,在本例中,不管在
* String Pool中是否已经有值相同的对象,都会创建了一个新的
* String对象存储在heap中,然后把引用返回赋给s1.
* 本例中使用了String的public String(String original)构造函数.
*/
String s1
= new String("sss111");

/*
* 此句会按照例1中所述在String Pool中查找
*/
String s2
= "sss111";

/*
* 由于s1是new出的新对象,存储在heap中,s2指向的对象
* 存储在String Pool中,他们肯定不是同一个对象,只是
* 存储的字符串值相同,所以返回false.
*/
System.out.println(s1
== s2); //结果为false


例3
String s1
= new String("sss111");
/*
* 当调用intern方法时,如果String Pool中已经包含一个等于此String对象
* 的字符串(用 equals(Object)方法确定),则返回池中的字符串.否则,将此
* String对象添加到池中,并返回此String对象在String Pool中的引用.
*/
s1
= s1.intern();

String s2
= "sss111";

/*
* 由于执行了s1 = s1.intern(),会使s1指向String Pool中值为"sss111"
* 的字符串对象,s2也指向了同样的对象,所以结果为true
*/
System.out.println(s1
== s2);


例4
String s1
= new String("111");
String s2
= "sss111";

/*
* 由于进行连接的2个字符串都是常量,编译期就能确定连接后的值了,
* 编译器会进行优化直接把他们表示成"sss111"存储到String Pool中,
* 由于上边的s2="sss111"已经在String Pool中加入了"sss111",
* 此句会把s3指向和s2相同的对象,所以他们引用相同.此时仍然会创建出
* "sss"和"111"两个常量,存储到String Pool中.

*/
String s3
= "sss" + "111";

/*
* 由于s1是个变量,在编译期不能确定它的值是多少,所以
* 会在执行的时候创建一个新的String对象存储到heap中,
* 然后赋值给s4.
*/
String s4
= "sss" + s1;

System.out.println(s2
== s3); //true
System.out.println(s2 == s4); //false
System.out.println(s2 == s4.intern()); //true


例5
这个是The Java Language Specification中3.
10.5节的例子,有了上面的说明,这个应该不难理解了
package testPackage;
class Test {
public static void main(String[] args) {
String hello
= "Hello", lo = "lo";
System.out.print((hello
== "Hello") + " ");
System.out.print((Other.hello
== hello) + " ");
System.out.print((other.Other.hello
== hello) + " ");
System.out.print((hello
== ("Hel"+"lo")) + " ");
System.out.print((hello
== ("Hel"+lo)) + " ");
System.out.println(hello
== ("Hel"+lo).intern());
}
}
class Other { static String hello = "Hello"; }

package other;
public class Other { static String hello = "Hello"; }

输出结果为true
true true true false true,请自行分析!


总结
结果上面分析,总结如下:
1.单独使用""引号创建的字符串都是常量,编译期就已经确定存储到String Pool中.
2.使用new String("")创建的对象会存储到heap中,是运行期新创建的.
3.使用只包含常量的字符串连接符如"aa" + "aa"创建的也是常量,编译期就能确定,已经确定存储到String Pool中.
4.使用包含变量的字符串连接符如"aa" + s1创建的对象是运行期才创建的,存储在heap中.
6.使用"aa" + s1以及new String("aa" + s1)形式创建的对象是否加入到String Pool中我不太确定,可能是必须
调用intern()方法才会加入,希望高手能回答 @_@



还有几个经常考的面试题:

1.
String s1
= new String("s1") ;
String s2
= new String("s1") ;
上面创建了几个String对象
?
答案:3个 ,编译期Constant Pool中创建1个,运行期heap中创建2个.


2.
String s1
= "s1";
String s2
= s1;
s2
= "s2";
s1指向的对象中的字符串是什么
?
答案:
"s1"

Yocto系列讲解[入门篇] 1 - 快速入门熟悉Yocto的构建 yocto quick start up带大家快速上手 阅读详情

相关推荐

远程桌面报‘数据加密错误‘?可能是网卡设置惹的祸(附TCP校验关闭教程)

本文深入分析远程桌面连接中出现的'数据加密错误'问题,指出网卡设置可能是根本原因,并提供详细的TCP校验关闭教程。通过禁用网卡高级属性中的'TCP校验和IPv4'与'IPv4承载卸载'功能,可有效解决RDP协议与硬件加速的兼容性问题,适用于Windows和Linux系统。

view3的博客 280

JavaScript字符串拼接变量名及赋值

某些时候需要通过传参的形式来改变或获取有规律的变量值,就可以用到变量名拼接 传参拼接变量名获取值方法一:(适用于全局变量) //通过中括号[] var str_a = '你好'; var str_b = 'hello'; function test(s){ return window['str_' + s]; } console.log(test('a')); //你好 conso...

lihefei_coder的博客 2万+

别再死记硬背了!用Wireshark抓包实战,5分钟搞懂GRE、L2TP、IPsec的区别

本文通过Wireshark抓包实战,详细解析GRE、L2TP和IPsec三大隧道协议的核心差异与应用场景。从报文结构到配置示例,帮助网络工程师快速掌握这些协议的本质,提升VPN部署与故障排查能力。特别适合准备HCIP认证的工程师深化理解。

weixin_30352645的博客 439

Java面试题(1) 判断创建了多少个字符串对象(常量池)

/* * String : 保存值到常量池 * 常见笔试题:判断指定代码创建了多少个字符串对象 */ public class Demo{ public static void main(String[] args) { // TODO Auto-generated method stub /* * 1.在堆内存中创建String类型的对象 * 2.在常量池中查找是...

郑清 2963

Claude Code VSCode集成开发指南:AI编程助手完整配置

Claude Code与Visual Studio Code的深度集成为开发者提供了强大的AI编程支持。本文详细介绍Claude Code VSCode扩展的安装配置、功能特性和实际应用方法。通过完整的配置流程和代码示例,帮助开发者充分利用Claude AI的智能编程能力。文章涵盖从环境搭建到高级功能应用的完整指南,包括代码自动补全、智能重构、实时代码分析等核心功能的使用技巧,以及与现有开发工作流的整合方案。

2501_93058131的博客 5990

reCAPTCHA v3隐形验证码实战:5分钟教你用Capsolver获取0.9高分令牌

本文提供了一份reCAPTCHA v3隐形验证码的实战指南,详细解析其浏览器评分机制,并手把手教你使用Capsolver自动化获取0.9高分令牌。内容涵盖从环境侦察、关键参数(sitekey与pageAction)提取,到API调用、结果轮询及令牌使用的完整流程,旨在帮助开发者高效绕过自动化交互障碍。

weixin_29314405的博客 348

深入研究java对String字符串对象的创建以及管理【转帖】

 深入研究java对String字符串对象的创建以及管理(2008-9-27 bbh)新手初学,不对的地方大家多多指教~经常看到很多人讨论java中关于String的问题,我也就有点兴趣了,鉴于网上很多人写的都差别很大,同样的问题都是不同的说法,我很迷糊,花了一晚上读了Java Virtual Machine Specification和The Java Language Specificatio

幸旺 702

ESP32蓝牙广播数据自定义指南:让你的设备更容易被手机发现

本文详细介绍了如何通过自定义ESP32蓝牙广播数据提升设备在手机端的可发现性。从广播数据结构、扫描响应优化到跨平台兼容性设置,提供了全面的技术指南和实战代码示例,帮助开发者解决智能硬件开发中的蓝牙连接难题。

weixin_29172201的博客 273
上一篇: 继承内部类的构造器问题
下一篇: 新建了三个QQ群:入门、提高和老手群
老紫竹
博客等级 码龄19年 1万+粉丝 1252原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值