仔细阅读了以往ZangXT大侠关于这个问题的几个回复,总结了8种情况,不知理解是否正确,恳请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",
3) 引用str2指向堆中的新建的String对象"abc",
4) str2所指代的对象地址为堆中地址,而常量"abc"地址在池中,输出为false
3. String str3 = new String("abc");
System.out.println(str3 == str2);步骤:
1) 棧中开辟一块空间存放引用str3,
2) 堆中开辟一块新空间存放另外一个(不同于str2所指)新建的String对象,
3) 引用str3指向另外新建的那个String对象
4) str3和str2指向堆中不同的String对象,地址也不相同,输出为false
4. String str4 = "a" + "b";
System.out.println(str4 == "ab"); 步骤:
1) 棧中开辟一块空间存放引用str4,
2) 根据编译器合并已知量的优化功能,池中开辟一块空间,存放合并后的String常量"ab",
3) 引用str4指向池中常量"ab",
4) str4所指即池中常量"ab",输出为true
5. final String s = "a";
String str5 = s + "b";
System.out.println(str5 == "ab");步骤:
同4
6. String s1 = "a";
String s2 = "b";
String str6 = s1 + s2;
System.out.println(str6 == "ab");步骤:
1) 棧中开辟一块中间存放引用s1,s1指向池中String常量"a",
2) 棧中开辟一块中间存放引用s2,s2指向池中String常量"b",
3) 棧中开辟一块中间存放引用str5,
4) s1 + s2通过StringBuilder的最后一步toString()方法还原一个新的String对象"ab",因此堆中开辟一块空间存放此对象,
5) 引用str6指向堆中(s1 + s2)所还原的新String对象,
6) str6指向的对象在堆中,而常量"ab"在池中,输出为false
7. String str7 = "abc".substring(0, 2);
步骤:
1) 棧中开辟一块空间存放引用str7,
2) substring()方法还原一个新的String对象"ab"(不同于str6所指),堆中开辟一块空间存放此对象,
3) 引用str7指向堆中的新String对象,
8. String str8 = "abc".toUpperCase();
步骤:
1) 棧中开辟一块空间存放引用str6,
2) toUpperCase()方法还原一个新的String对象"ABC",池中并未开辟新的空间存放String常量"ABC",
3) 引用str8指向堆中的新String对象
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",
3) 引用str2指向堆中的新建的String对象"abc",
4) str2所指代的对象地址为堆中地址,而常量"abc"地址在池中,输出为false
3. String str3 = new String("abc");
System.out.println(str3 == str2);步骤:
1) 棧中开辟一块空间存放引用str3,
2) 堆中开辟一块新空间存放另外一个(不同于str2所指)新建的String对象,
3) 引用str3指向另外新建的那个String对象
4) str3和str2指向堆中不同的String对象,地址也不相同,输出为false
4. String str4 = "a" + "b";
System.out.println(str4 == "ab"); 步骤:
1) 棧中开辟一块空间存放引用str4,
2) 根据编译器合并已知量的优化功能,池中开辟一块空间,存放合并后的String常量"ab",
3) 引用str4指向池中常量"ab",
4) str4所指即池中常量"ab",输出为true
5. final String s = "a";
String str5 = s + "b";
System.out.println(str5 == "ab");步骤:
同4
6. String s1 = "a";
String s2 = "b";
String str6 = s1 + s2;
System.out.println(str6 == "ab");步骤:
1) 棧中开辟一块中间存放引用s1,s1指向池中String常量"a",
2) 棧中开辟一块中间存放引用s2,s2指向池中String常量"b",
3) 棧中开辟一块中间存放引用str5,
4) s1 + s2通过StringBuilder的最后一步toString()方法还原一个新的String对象"ab",因此堆中开辟一块空间存放此对象,
5) 引用str6指向堆中(s1 + s2)所还原的新String对象,
6) str6指向的对象在堆中,而常量"ab"在池中,输出为false
7. String str7 = "abc".substring(0, 2);
步骤:
1) 棧中开辟一块空间存放引用str7,
2) substring()方法还原一个新的String对象"ab"(不同于str6所指),堆中开辟一块空间存放此对象,
3) 引用str7指向堆中的新String对象,
8. String str8 = "abc".toUpperCase();
步骤:
1) 棧中开辟一块空间存放引用str6,
2) toUpperCase()方法还原一个新的String对象"ABC",池中并未开辟新的空间存放String常量"ABC",
3) 引用str8指向堆中的新String对象
System.out.println(str2 == "abc"); 上面这段代码,字符串池中有一个常量String对象"abc",堆中也有一个String对象"abc",那么堆中的这个对象是不是指向池中的那个对象呢?
String s1 = "a";
String s2 = "b";
String str6 = s1 + s2;
System.out.println(str6 == "ab"); 以上代码中,s1+s2的实际操作是通过StringBuilder完成的,引用臧大侠的在该帖中6楼的话:StringBuilder builder = new StringBuilder();
builder.append(s1);
builder.append(s2);
String str6=builder.toString();
去看StringBuilder的 toString()方法:
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
可以发现是通过new String(..)返回了一个String对象,也就是说在堆中创建了对象。这时候不会在池中出现"ab"这个对象。 问题是,同样根据以上API代码,toString()方法内部返回一个新String对象时,调用了String的构造函数;再根据最上面第一段代码的引申,使用String构造函数时,池中会先产生一个新的String对象"ab",然后这个值再拷贝给堆中的一个新String对象,最后返回堆中的String对象"ab"。这样一来,池中其实是有"ab"这个对象的。这个理解肯定是错误的,可是问题出在哪里呢?恳请臧啸天大侠指点!
就是有问题的。一个String对象有3个重要的成员 /** The value is used for character storage. */
private final char value[]; /** The offset is the first index of the storage that is used. */
private final int offset; /** The count is the number of characters in the String. */
private final int count;offset,count都是值变量,而value是指针String s="abc"首先s是一个指针变量(JAVA中的引用),它指向一个String对象。
而这个String对象中,value成员变量指向了一个字符串数组,而这个字符串数组里面就是abc。
String s1="abc";
String s2=new String("abc");s1==s2肯定是false
但假设我们能(我是说“假设能”)s1.value==s2.value,那么结果肯定是true,也就是s1和s2里面的value都指向了同一个字符串数组。========
问题是,同样根据以上API代码,toString()方法内部返回一个新String对象时,调用了String的构造函数;再根据最上面第一段代码的引申,使用String构造函数时,池中会先产生一个新的String对象"ab",然后这个值再拷贝给堆中的一个新String对象,最后返回堆中的String对象"ab"。这样一来,池中其实是有"ab"这个对象的。这个理解肯定是错误的,可是问题出在哪里呢?恳请臧啸天大侠指点!
========你的问题就出在--没有区分字符数组和String对象String s1 = "a";
String s2 = "b";
String str6 = s1 + s2;
s1和s2都用的是常量池中的String对象,所以没有在堆中建立新对象。
而str6,除了在堆中new了一个String对象,其实还new了一个char数组,这个字符数组的长度就是2,放的是'a'和'b'。
而str6.value指向的是这个char[],而不是常量池里面的那个char[]。
不是的,不可能一个String对象指向另一个String对象,这么说的话你还是没有分清楚"引用"和"对象"的区别.
自己去看String的构造方法public String(String o)这个的实现,有两种情况,一个情况是两个对象共享char数组,一个情况是自己建立一个新的char数组.
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;
}怎么看出来,一个情况是两个对象共享char数组,一个情况是自己建立一个新的char数组.
我认为是错误的,s1和s2确实都指向字符串数组
但是s1是常量池中的字符数组。而s2是由本身对象value[]这个引用指向另一个数组对象。
看下string类的带string参数的构造方法/*
* @(#)String.java 1.204 06/06/09
*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
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;
}当我们执行String s2=new String("abc"); 时候
在栈里应该分配一个空间,这个空间是这个构造方法的String original(original的引用)
这个引用就跟string original="abc";效果一样
这个时候看构造方法中的代码就行了.
因此我说:而s2是由本身对象value[]这个引用指向另一个数组对象。
ps:本人也刚学习Java,这是我看书和看API的总结,还希望各位多多指点,在这里先谢谢各位!
return (obj == null) ? "null" : obj.toString();
}
public String toString() {
return this;
}
// 然后调用StringBuffer的构造器
public StringBuffer(String str) {
super(str.length() + 16);// super class AbstractStringBuilder
append(str);
}abstract class AbstractStringBuilder implements Appendable, CharSequence {
char value[];
int count;
AbstractStringBuilder() {
} AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
...最终调用StringBuffer#toString() public synchronized String toString() {
return new String(value, 0, count);
}本来对兄台的这段解释有点疑问,仔细看看源码果真如此,佩服!
在栈里应该分配一个空间,这个空间是这个构造方法的String original(original的引用) --这句是对的
这个引用就跟string original="abc";效果一样 --这句是对的
问题在哪里呢?在你没认真看构造方法中的代码。char[] originalValue = original.value;--这句把常量池中String对象指向的常量数组"abc"的指针给了originalValue临时变量
if (originalValue.length > size)--显然是false,因为对于一个String常量,显然original.count==original.value.length!
那么会执行
else {
v = originalValue;
}
this.value = v;
看到没有?绕了一圈,其实还是:
this.value = [v = originalValue =] original.value
这就是我说的“也就是s1和s2里面的value都指向了同一个字符串数组”。
但是:
这里您说而str6,除了在堆中new了一个String对象,其实还new了一个char数组,这个字符数组的长度就是2,放的是'a'和'b'。
这个new了一个char数组,是怎么回事呢?在堆里申请了空间,str6.value指向这个char[]?
不是说在常量池中有一个s1+s2的值,再由str6.value指向常量池中的这个s1+s2吗?
这种问题别深究下去了。
有兴趣自己看一下jvm规范
http://java.sun.com/docs/books/jvms/
答:常量池里面没有s1+s2的值,因为编译器无法判定s1或者s2会不会变。除非你把它们都申明为final,编译器才敢确定a+b的值,出现你说的情况。
但是即使使用final,也并非你说的“str6.value指向常量池中的这个s1+s2吗”,String对象是不能+的,这个+号被理解为字符串相连。因此如果使用final,常量池里面应该有3个字符数组:['a'],['b'],['a','b'],str6的value是指向第3个的。
而str6,除了在堆中new了一个String对象,其实还new了一个char数组,这个字符数组的长度就是2,放的是'a'和'b'。 这里能不能解释清楚一下?
或者您能不能解释一下,String str6 = s1 + s2; 在内存中的执行过程。我只是这点糊涂..其他情况,我想我已经都掌握了。
或许您可以提供我一些资料,我自己查阅。google并没有找到类似的问题。
实际sun的jvm这样执行:
StringBuilder builder = new StringBuilder();
builder.append(s1);
builder.append(s2);
str6=builder.toString();可以看一下Thinking In Java中分析问题的方法
老师说StringBuffer是放到缓冲区,内存中专门的缓冲区-。-?
快放假回家啦~回家就有Thinking In Java看咯~想必到时候会看的更精细一点!
有关String的这么多名堂,是java所独有的吗?还是别的编程语言把这些都封装了?
老顾客了!照顾一下!
PS:
我的目标是 ----> ^_^