怎么样理解String类型对象和字符串池?

重返IT路 2008-11-26 09:19:11
今天学到String类型 和 池的概念把我给弄糊涂了

1.String tmp1 = "abc";
//这样在池中创建了"abc"的String对象,然后tmp1指向"abc"
tmp1--->"abc"

2.String tmp2 = new String("abc");
//tmp2指向在堆中新创建的String对象。该对象是保存abc的值 还是再指向池中的"abc"?

tmp2--->堆中对象(值为abc)

or

tmp2--->堆中对象--->池中"abc"


3.String s1 = "1";
String s2 = "2";
String s3 = s1+s2;
//网上说在池中创建了"1"和"2"的对象 然后通过StringBuffer进行+处理再用.toString()放回给s3

那么s3就是指向堆中的一个String对象。那那个String对象的值为"12"还是再在池中创个"12"对象,让堆中对象指向"12"。 还是上面2的那个问题

4.什么情况下String对象需要放到池中,什么时候不要?
比如 S1.substring(); s1.toUpperCase(); 产生的新值都要放到池中?
...全文
305 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
bigbro001 2008-12-22
  • 打赏
  • 举报
回复
今天又回来看了一通这个帖子,受益匪浅,总结总结,还请ZangXT等各位大侠指正
1. String str1 = "abc";
System.out.println(str1 == "abc");
步骤:
1) 棧中开辟一块空间存放引用str1,
2) String池中开辟一块空间,存放String常量"abc",
3) 引用str1指向池中String常量"abc",
4) str1所指代的地址即常量"abc"所在地址,输出为true

2. String str2 = new String("abc");
System.out.println(str2 == "abc");
步骤:
1) 棧中开辟一块空间存放引用str2,
2) 堆中开辟一块空间存放一个新建的String对象"abc",该对象与池中的String常量"abc"是否共享char数组,并不重要
3) 引用str2指向堆中的新建的String对象"abc",
4) str2所指代的对象地址为堆中地址,而常量"abc"地址在池中,输出为false

3. String str3 = new String("abc");
System.out.println(str3 == str2);
步骤:
1) 棧中开辟一块空间存放引用str3,
2) 堆中开辟一块新空间存放另外一个(不同于str2所指)新建的String对象,该对象与池中的String常量"abc"是否共享char数组,并不重要
3) 引用str3指向另外新建的那个String对象
4) str3和str2指向堆中不同的String对象,地址也不相同,输出为false

4. String str4 = "a" + "b";
步骤:
1) 棧中开辟一块空间存放引用str4,
2) 堆中开辟一块空间存放一个新建的String对象"ab",String池中并未开辟一块新的空间存放String常量"ab",
3) 引用str4指向堆中新建的String对象,该对象与池中的String常量"abc"是否共享char数组,并不重要

5. String str5 = "abc".substring(0, 2);
步骤:
1) 棧中开辟一块空间存放引用str5,
2) 堆中开辟一块空间存放一个新建的String对象"ab"(不同于str4所指),池中仍然并未开辟新的空间存放String常量"ab",
3) 引用str5指向堆中的新建String对象,同上,该对象与池中的String常量"abc"是否共享char数组,并不重要

6. String str6 = "abc".toUpperCase();
步骤:
1) 棧中开辟一块空间存放引用str6,
2) 堆中开辟一块空间存放一个新建的String对象"ABC",池中并未开辟新的空间存放String常量"ABC",
3) 引用str6指向堆中的新建String对象
cainiao228 2008-11-27
  • 打赏
  • 举报
回复
要学的东西还很多
oyto12o 2008-11-27
  • 打赏
  • 举报
回复
学习了……
重返IT路 2008-11-27
  • 打赏
  • 举报
回复
谢谢ZangXT了!
rainzoo 2008-11-27
  • 打赏
  • 举报
回复
String tmp2 = new String("abc");
如果是main方法第一行的话将创建两个String对象,一个是在pool里面,一个是堆里面的。

String temp1 = "abc";
String tmp2 = new String("abc"); //这样的话这句话就只创建一个String对象,是指向堆里的,java里面new出来的东西总是放堆里的。
ZangXT 2008-11-26
  • 打赏
  • 举报
回复

public class Test
{
public static void main(String[] args)
{

String a="a";
String b="b";
String c=a+b;
}

}


javac Test.java
javap -c Test

Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return

public static void main(java.lang.String[]);
Code:
0: ldc #2; //String a
2: astore_1
3: ldc #3; //String b
5: astore_2
6: new #4; //class java/lang/StringBuilder
9: dup
10: invokespecial #5; //Method java/lang/StringBuilder."<init>":()V
13: aload_1
14: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: aload_2
18: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: invokevirtual #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
24: astore_3
25: return

}



看里面出现的StringBuilder.append
1.4同样分析
重返IT路 2008-11-26
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 ZangXT 的回复:]
你给点那个网页上说法,你自己先把所有的代码测试一下,看他说的对吗[/Quote]

恩 我基本理解了

但还是有一点搞不通,就是那片文章上说对于JDK1.5作的优化

我测试了,结果都是一样的。


不知道1.4用不用buffer 作处理。

我实在是看不出来 请您指点!
ZangXT 2008-11-26
  • 打赏
  • 举报
回复
所谓优化就是使用StringBuilder来处理链接运算而已 。
ZangXT 2008-11-26
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 yuchujin 的回复:]
引用 6 楼 ZangXT 的回复:
4.什么情况下String对象需要放到池中,什么时候不要?
常量值,也就是使用""括起来的字面常量值会放入常量池 。
或者你通过String的 intern()方法把一个字符串主动放入常量池。
比如  S1.substring(); s1.toUpperCase(); 产生的新值都要放到池中,它们产生的串不会放入常量池。

您的意思是说 s1.substring();s1.toUpperCase();方法体里最后由this.intern()将产生的新字符串加到池中? 什么叫新值…
[/Quote]
比如 S1.substring(); s1.toUpperCase(); 产生的新值都要放到池中,
前面半句是引用楼主的话,是错误的,我的话是后面半句“它们产生的串不会放入常量池”。
你想放进去,也好自己通过 s1=s1.intern();这样处理。

你给点那个网页上说法,你自己先把所有的代码测试一下,看他说的对吗
重返IT路 2008-11-26
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 ZangXT 的回复:]
4.什么情况下String对象需要放到池中,什么时候不要?
常量值,也就是使用""括起来的字面常量值会放入常量池 。
或者你通过String的 intern()方法把一个字符串主动放入常量池。
比如 S1.substring(); s1.toUpperCase(); 产生的新值都要放到池中,它们产生的串不会放入常量池。[/Quote]

您的意思是说 s1.substring();s1.toUpperCase();方法体里最后由this.intern()将产生的新字符串加到池中? 什么叫新值放到池中,新串不放。。。。

我在网上看到一遍文章说 编译器对s=s1+s2的优化的。用StringBuffer
但只有jdf1.5对次做优化 以前的不用
链接:http://tech.sina.com.cn/s/2008-07-04/0950720255.shtml
ZangXT 2008-11-26
  • 打赏
  • 举报
回复
字面常量String会放入池中。比如“abc”,“1”,“2”这些常量会放进去
1.String tmp1 = "abc";
//这样在池中创建了"abc"的String对象,然后tmp1指向"abc"
tmp1--->"abc"

是的
2.String tmp2 = new String("abc");
//tmp2指向在堆中新创建的String对象。该对象是保存abc的值 还是再指向池中的"abc"?
首先确定一点,tmp2指向的堆中的对象,它是通过new创建的。现在有2个对象了。
然后去看String类的构造方法,通过看String(String orig)这个构造方法的实现可以知道原理。String中使用字符数组保存实际的字符。

public String(String original) {
int size = original.count;
char[] originalValue = original.value;
char[] v;
if (originalValue.length > size) {
// The array representing the String is bigger than the new
// String itself. Perhaps this constructor is being called
// in order to trim the baggage, so make a copy of the array.
int off = original.offset;
v = Arrays.copyOfRange(originalValue, off, off+size);
} else {
// The array representing the String is the same
// size as the String, so no point in making a copy.
v = originalValue;
}
this.offset = 0;
this.count = size;
this.value = v;
}

看这个处理可以知道,两中情况,一种是为这个新的串新创建一个字符数组,一种是直接让这两个串来共享数组。

结论:
tmp2--->堆中对象(值为abc)

当然,两个对象(new得到的堆里的对象,和池里的“abc”对象)是否共享char数组并不重要,关键知道是两个不同的对象即可。


3.String s1 = "1";
String s2 = "2";
String s3 = s1+s2;
//网上说在池中创建了"1"和"2"的对象 然后通过StringBuffer进行+处理再用.toString()放回给s3
在jdk1.5之前是使用StringBuffer来处理的,1.5开始是使用的StringBuilder.
String s3=s1+s2;
实际处理为
StringBuilder builder = new StringBuilder();
builder.append(s1);
builder.append(s2);
String s3=builder.toString();
去看StringBuilder的 toString()方法:

public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}

可以发现是通过new String(..)返回了一个String对象,也就是说在堆中创建了对象。这时候不会在池中出现“12”这个对象。

//
那么s3就是指向堆中的一个String对象。

4.什么情况下String对象需要放到池中,什么时候不要?
常量值,也就是使用""括起来的字面常量值会放入常量池 。
或者你通过String的 intern()方法把一个字符串主动放入常量池。
比如 S1.substring(); s1.toUpperCase(); 产生的新值都要放到池中,它们产生的串不会放入常量池。
重返IT路 2008-11-26
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 C0150 的回复:]
1 是指向池中的一个地址
2 是在池中创建一个新的空间,里面存放abc
3 stringBuffer不用创建新的空间,只是指向一个地址。
对于4的问题,我也说不清楚,以上是我的理解,不知道对不对,期待紫竹大哥给出正解,和楼主一起学习中。
up
[/Quote]

对于你的2,3我认为是错的

2我的认为跟楼上一样

3 编译后是: s3 = (new StringBuffer()).append(s1)append(s2).toString();
这样还能没创建对象????至少是两个对象 一个是临时的SB 一个是toString转后出来的对象
oyto12o 2008-11-26
  • 打赏
  • 举报
回复
tmp2指向在堆中新创建的String对象。该对象是保存abc的值 还是再指向池中的"abc"?
---------在堆中另开辟空间存储"abc”,tmp2引用指向“在堆中另开辟空间存储的"abc”。”而不是池中的!
C0150 2008-11-26
  • 打赏
  • 举报
回复
1 是指向池中的一个地址
2 是在池中创建一个新的空间,里面存放abc
3 stringBuffer不用创建新的空间,只是指向一个地址。
对于4的问题,我也说不清楚,以上是我的理解,不知道对不对,期待紫竹大哥给出正解,和楼主一起学习中。
up
oyto12o 2008-11-26
  • 打赏
  • 举报
回复
2.String tmp2 = new String("abc");
//tmp2指向在堆中新创建的String对象。该对象是保存abc的值 还是再指向池中的"abc"?

tmp2--->堆中对象(值为abc) 本人认为是这个。

or

tmp2--->堆中对象--->池中"abc"

---------------------要new String对象时在堆中分配,直接赋字符串(如String tmp1 = "abc";)则分配在池中。
shenjie1989 2008-11-26
  • 打赏
  • 举报
回复
先占个位置

62,616

社区成员

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

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