string三问:Java中如何给字符串分配内存

zrd 2008-08-20 06:05:38
今天研究了一下Java中字符串内存分配的问题,但仍然没有弄清楚,希望有高手指教一下

String是一个特殊的包装类数据。可以用:
String str = new String("abc");
String str = "abc";
两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。而第二种是先在栈中创建一个对String类的对象引用

变量str,然后查找栈中有没有存放"abc",如果没有,则将"abc"存放进栈,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。

比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。
代码片段(一)
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true 这里
System.out.println(str1.equals(str2)); //true too.
可以看出str1和str2是指向同一个对象的。
代码片段(二)
String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1.equals(str2)); //true 毫无疑问,这里证明他们是堆中的两个对象。
System.out.println(str1==str2); // false
用new的方式是生成不同的对象。每一次生成一个。
因此用第二种方式创建多个”abc”字符串,在内存中其实只存在一个对象而已. 这种写法有利与节省内存空间. 同时它可以在一定程度上提高程序的运行速度,因

为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符

串值是否相等,是否有必要创建新对象,从而加重了程序的负担。

以上转自网络

疑问1:
String str1 = "abc";这样创建的 字符串 str1和str2,适用 ==可以理解为指向栈中的同一块内存,适用equals()方法又可以理解为堆中的对象。那么它到底是

在堆中的呢,还是在栈中的呢。难道这就是他的特殊之处吗?
代码片段(三)
Integer str1 = new Integer(12345);
Integer str2 = new Integer(12345);
if(str1.equals(str2))
System.out.println("equals right"); //这里输出正确
if(str1==str2)
System.out.println("== right"); //这里没有输出

当测试两个包装类的引用是否指向同一个对象时,应用equals()方法而非==,可见包装类创建的是堆中的对象。

疑问2:
代码片段(四)
String str1 = "string";
String str3 = "str";
String str4 = "ing";
String str2 = str3+str4;
if(str1.equals(str2))
System.out.println("equals right"); //这里输出正确
if(str1==str2)
System.out.println("== right"); //这里没有输出
由此可见,(一)中虽然定义了两个字符串str1 and str2,但实际上在栈中只有一个"abc",而str1 和 str2两个引用变量同时指向这个字符串,所以可以用“==”

来判断是否相等。而(四)中的str1和str2并不是指向共同的字符串,所以不能用“==”来判断。那么为什么可以用equals()方法判断呢,如果理解为String

str1 ="string"; 就是在堆里面创建的,就可以了,但是堆里的对象又不能用“==”判断,所以还是解释不清楚。

疑问3:
代码片段(五)
String str1 = new String( "string");
String str3 = "str";
String str4 = "ing";
String str2 = str3+str4;
if(str1.equals(str2))
System.out.println("equals right"); //这里输出正确
if(str1==str2)
System.out.println("== right"); //这里没有输出

两种不同方式创建的字符串竟然存在着某种联系!看来问题越来越复杂了!

补充一个问题:
equals方法,默认实现就是用"=="来直接比较内存地址:
Object.equals:
public boolean equals(Object obj) {
return (this == obj);
}
那么str1==str2 与str1.equals(str2) 的不同之处又是什么呢?

请高手指教!
...全文
1261 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
sijishujuku337 2011-10-12
  • 打赏
  • 举报
回复
不错,很好,让我完全理解了字符串内存分配的原理
wazhl 2011-05-30
  • 打赏
  • 举报
回复
其实都一样。
在做项目的时候应该注意区别
string 和 stringbuffer
Intboy 2011-05-29
  • 打赏
  • 举报
回复
学习了,深刻……
hepeng_8 2011-05-29
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 bianmazi 的回复:]
csdn类似的帖子:http://topic.csdn.net/t/20021025/11/1124320.html

equals 方法(是String类从它的超类Object中继承的)被用来检测两个对象是否相等,即两个对象的内容是否相等。
==用于比较引用和比较基本数据类型时具有不同的功能:
比较基本数据类型,如果两个值相同,则结果为true
而在比较引用时,如果引用指向内存中的同一……
[/Quote]
回答很具体哦
qybao 2011-05-29
  • 打赏
  • 举报
回复
首先要先了解JVM的内存模型
JVM运行时数据区的内存模型由五部分组成:
1 方法区
2 堆
3 JAVA栈
4 PC寄存器
5 本地方法栈

String str = "abc"创建对象的过程
1 首先在常量池中查找是否存在内容为"abc"字符串对象
2 如果不存在则在常量池中创建"abc",并让str引用该对象
3 如果存在则直接让str引用该对象

至于"abc"是怎么保存,保存在哪?常量池属于类信息的一部分,而类信息反映到JVM内存模型中是对应存在于JVM内存模型的方法区,也就是说这个类信息中的常量池概念是存在于在方法区中,而方法区是在JVM内存模型中的堆中由JVM来分配的,所以"abc"可以说存在于堆中(而有些资料,为了把方法区的堆区别于JVM的堆,把方法区称为栈)。一般这种情况下,"abc"在编译时就被写入字节码中,所以class被加载时,JVM就为"abc"在常量池中分配内存,所以和静态区差不多。

String str = new String("abc")创建实例的过程
1 首先在堆中(不是常量池)创建一个指定的对象"abc",并让str引用指向该对象
2 在字符串常量池中查看,是否存在内容为"abc"字符串对象
3 若存在,则将new出来的字符串对象与字符串常量池中的对象联系起来
4 若不存在,则在字符串常量池中创建一个内容为"abc"的字符串对象,并将堆中的对象与之联系起来
intern 方法可以返回该字符串在常量池中的对象的引用,可以通过下面代码简单的测试
class StringTest {
public static void main(String[] args) {
String str1 = "abc";
String str2 = new String("abc").intern();
System.out.println(str1==str2);
}
}


所以String str1 = "abc",str1引用的是常量池(方法区)的对象,而String str2 = new String("abc"),str2引用的是堆中的对象,所以内存地址不一样,但是内容一样,所以==为false,而equals是true
而String str1 = "abc"; String str2 = "ab" + "c"; str1==str2是ture,是因为String str2 = "ab" + "c"会查找常量池中时候存在内容为"abc"字符串对象,如存在则直接让str2引用该对象,显然String str1 = "abc"的时候,上面说了,会在常量池中创建"abc"对象,所以str1引用该对象,str2也引用该对象,所以str1==str2
而String str1 = "abc"; String str2 = "ab"; String str3 = str2 + "c"; str1==str3是false,是因为String str3 = str2 + "c"涉及到变量(不全是常量)的相加,所以会生成新的对象,其内部实现是先new一个StringBuilder,然后append(str2),append("c");然后让str3引用toString()返回的对象。LZ如果想了解更多的细节,可以自己查看反编译的代码,查看反编译代码可以用javap,即
javap -c -verbose 要查看的类文件(.class不要)
比如上面的代码的示例
javac StringTest.java //编译
javap -c -verbose StringTest //反编译
changtianshuiyue 2011-05-28
  • 打赏
  • 举报
回复
对于疑问1:字符串都是在同一个内存地址的相同的内容,也就是不同的变量名是对同一个内存空间的引用,equal()方法比较的是内存地址中存储的内容,所以不存在"abc”是在堆中和栈中的问题,只有一个存有"abc"的内存空间,是在常量池中
小米很好吃 2011-05-28
  • 打赏
  • 举报
回复
String str =new String("abc");
引用变量str 存放在stack中, “abc”是常量字符串 放在常量池
new的string对象放在heap中, 对象的内容是abc
换句话说,刚创建的对象是abc的一个副本
wenjunsu 2008-11-02
  • 打赏
  • 举报
回复

学习了。。。。

mark。。。

up
zrd 2008-08-29
  • 打赏
  • 举报
回复
String s1 = "string";
为什么说它是在栈中的呢,既然它能调用各种方法,它应该是对象而不是简单的常量啊,既然是对象就应该是在堆中才对啊。那么s1作为对象的引用存储在栈中倒是可以接受。

String毕竟不是基本类型也不是包装类,不能用8种基本类型的思路来看待吧
zrd 2008-08-27
  • 打赏
  • 举报
回复
String s1 = "string";
毕竟String并不是基本类型也不是包装类,既然s1能调用equals()方法,那么我就认为创建了一个对象,而不是一个简单的常量。既然是对象就应该是存储在堆heap中,而不是stack里。

而且下面的代码输出false,也说明str2没有直接指向已经存在的、s1指向的堆里的字符串
String str1 = new String("string");
String str2 = "string";
System.out.println(str1==str2);

那么,是不是说这两种方式创建的对象不在内存的同一块区域,那到底是在哪里呢,这让我很迷惑,请高人指教。
yanhan0615 2008-08-27
  • 打赏
  • 举报
回复
错了,是楼上的楼上
yanhan0615 2008-08-27
  • 打赏
  • 举报
回复
楼上的,我当然是说String s1 = "string"; 这一整条语句的情况下是作为静态变量存储在栈中的,你别断章取义行么?

另:你说的“因为==操作符要比较的不仅仅是是否是同一个对象,还要比较hashcode”就错了,==只是比较内存地址,equals()方法才是判断对象是否相等!!
清风月明 2008-08-27
  • 打赏
  • 举报
回复
有一点很重要 String 是一个常量 
String s = "ssss";//可以简单理解是值 值和值之间可以通过 == 判读
String s = new String("ssss");//这个就是对象,对象之间不能通过 == 判断。

marf_cn 2008-08-27
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 zrd 的回复:]
String s1 = "string";
毕竟String并不是基本类型也不是包装类,既然s1能调用equals()方法,那么我就认为创建了一个对象,而不是一个简单的常量。既然是对象就应该是存储在堆heap中,而不是stack里。

而且下面的代码输出false,也说明str2没有直接指向已经存在的、s1指向的堆里的字符串
String str1 = new String("string");
String str2 = "string";
System.out.println(str1==str2);

那么,是不是说这两种…
[/Quote]
这个结果当然是false,因为==操作符要比较的不仅仅是是否是同一个对象,还要比较hashcode
marf_cn 2008-08-27
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 yanhan0615 的回复:]
String s1 = "string"; 是作为静态变量在栈中;
其他但凡是new出来的,都在堆里
[/Quote]
不完全同意。实际上如果内存中没有“string”这个串的话,这条语句会new一个在heap中,而是s1作为变量在stack中
marf_cn 2008-08-27
  • 打赏
  • 举报
回复
建议lz仔细找找stack、heap的区别,一句简单的话:变量存在stack里,对象存在heap里
yanhan0615 2008-08-27
  • 打赏
  • 举报
回复
String s1 = "string"; 是作为静态变量在栈中;
其他但凡是new出来的,都在堆里
zrd 2008-08-26
  • 打赏
  • 举报
回复
的确,最让我不解的是字符串在内存中是如何存储的。

String s1 = "string";
String s2 = "string";
if(s1==s2)
System.out.println("1 true");
if (s1.equals(s2))
System.out.println("2 true");

两个语句都输出,这说明s1和s2是两个存储在栈(stack)中的引用,而且他们指向同一个字符串,对吗.那么这个字符串是对象还是常量,是存放在堆(heap)中,还是在栈(stack)中,还是其他什么地方呢?
String str1 = new String("string");
String str2 = "string";
if(str1==str2)
System.out.println("1 true");
if (str1.equals(str2))
System.out.println("2 true");
为什么 1 没输出而 2 有输出,说明str1 和 str2 内容相同而地址不同,对吗

如果他们在内存的同一块区域内,那么str2 就会自动指向str1,而实际上却没有。所以我认为这两种方式创建的字符串并不是存储在内存中在同一块区域,不知道对不对。那么他们又是分别怎样存储的呢?

String str1 = new String("string");
这样创建的对象应该在堆内存中,有人说这一条语句创建了两个对象,那么这两个分别是什么样的呢,存储在哪呢?

请高人赐教!
hanzsim 2008-08-21
  • 打赏
  • 举报
回复
你问的问题根本是在常量字符串和构造字符串的存储问题。解决了这个问题,你的三个疑问都解决了。
关键在哪?new关键字!
String str1="abc"这里没有new,那么str1是引用已经存在的字符串对象,引用的就是已存放好的"abc",java把字符串常量也使用String类处理的,这是自动的,所以,你用这种方式创建10000个引用,引用的还是同一个,就是原来存储好的"abc",就像下面的代码一样:
String strObj=new String("abc");
String str1=strObj;
String str2=strObj;
......
只不过,字符串常量是自动创建的,这几行代码是手动创建的,仅仅是对对象的引用而已。
而明确使用new 关键字则是要新建对象,那么每new一次,对象必然是新的一个,所以,equals(比较内容)是相同的,而==不同,不是同一对象!
bianmazi 2008-08-20
  • 打赏
  • 举报
回复
csdn类似的帖子:http://topic.csdn.net/t/20021025/11/1124320.html

equals 方法(是String类从它的超类Object中继承的)被用来检测两个对象是否相等,即两个对象的内容是否相等。
==用于比较引用和比较基本数据类型时具有不同的功能:
比较基本数据类型,如果两个值相同,则结果为true
而在比较引用时,如果引用指向内存中的同一对象,结果为true
Eg:s1 = new String("sony"); //创建的是字符串对象
s1.equals("sony"); //返回
trues1 == "sony" //返回false
//如果
s1 = "sony";
s1 == "sony" //返回true

很简单的事情嘛.
加载更多回复(1)

13,100

社区成员

发帖
与我相关
我的任务
社区描述
Java J2ME
社区管理员
  • J2ME社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧