今天学到String类型 和 池的概念把我给弄糊涂了1.String tmp1 = "abc";
//这样在池中创建了"abc"的String对象,然后tmp1指向"abc"
tmp1--->"abc"2.String tmp2 = new String("abc");
//tmp2指向在堆中新创建的String对象。该对象是保存abc的值 还是再指向池中的"abc"?tmp2--->堆中对象(值为abc)ortmp2--->堆中对象--->池中"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(); 产生的新值都要放到池中?
//这样在池中创建了"abc"的String对象,然后tmp1指向"abc"
tmp1--->"abc"2.String tmp2 = new String("abc");
//tmp2指向在堆中新创建的String对象。该对象是保存abc的值 还是再指向池中的"abc"?tmp2--->堆中对象(值为abc)ortmp2--->堆中对象--->池中"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(); 产生的新值都要放到池中?
//tmp2指向在堆中新创建的String对象。该对象是保存abc的值 还是再指向池中的"abc"? tmp2--->堆中对象(值为abc) 本人认为是这个。or tmp2--->堆中对象--->池中"abc" ---------------------要new String对象时在堆中分配,直接赋字符串(如String tmp1 = "abc";)则分配在池中。
2 是在池中创建一个新的空间,里面存放abc
3 stringBuffer不用创建新的空间,只是指向一个地址。
对于4的问题,我也说不清楚,以上是我的理解,不知道对不对,期待紫竹大哥给出正解,和楼主一起学习中。
up
---------在堆中另开辟空间存储"abc”,tmp2引用指向“在堆中另开辟空间存储的"abc”。”而不是池中的!
对于你的2,3我认为是错的2我的认为跟楼上一样3 编译后是: s3 = (new StringBuffer()).append(s1)append(s2).toString();
这样还能没创建对象????至少是两个对象 一个是临时的SB 一个是toString转后出来的对象
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(); 产生的新值都要放到池中,它们产生的串不会放入常量池。
您的意思是说 s1.substring();s1.toUpperCase();方法体里最后由this.intern()将产生的新字符串加到池中? 什么叫新值放到池中,新串不放。我在网上看到一遍文章说 编译器对s=s1+s2的优化的。用StringBuffer
但只有jdf1.5对次做优化 以前的不用
链接:http://tech.sina.com.cn/s/2008-07-04/0950720255.shtml
前面半句是引用楼主的话,是错误的,我的话是后面半句“它们产生的串不会放入常量池”。
你想放进去,也好自己通过 s1=s1.intern();这样处理。你给点那个网页上说法,你自己先把所有的代码测试一下,看他说的对吗
恩 我基本理解了但还是有一点搞不通,就是那片文章上说对于JDK1.5作的优化我测试了,结果都是一样的。
不知道1.4用不用buffer 作处理。我实在是看不出来 请您指点!
public class Test
{
public static void main(String[] args)
{
String a="a";
String b="b";
String c=a+b;
}}
javac Test.java
javap -c TestCompiled 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: returnpublic 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同样分析
如果是main方法第一行的话将创建两个String对象,一个是在pool里面,一个是堆里面的。String temp1 = "abc";
String tmp2 = new String("abc"); //这样的话这句话就只创建一个String对象,是指向堆里的,java里面new出来的东西总是放堆里的。
1. String str1 = "abc";
System.out.println(str1 == "abc");
步骤:
1) 棧中开辟一块空间存放引用str1,
2) String池中开辟一块空间,存放String常量"abc",
3) 引用str1指向池中String常量"abc",
4) str1所指代的地址即常量"abc"所在地址,输出为true2. 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"地址在池中,输出为false3. 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对象,地址也不相同,输出为false4. 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对象