黑马程序员技术交流社区
标题:
一个String类产生对象的问题不太清楚
[打印本页]
作者:
吴扬
时间:
2012-6-26 02:48
标题:
一个String类产生对象的问题不太清楚
本帖最后由 吴扬 于 2012-6-27 01:10 编辑
下面的代码:
string i = "a ";
string j = "b ";
string k = i+j+ "c ";//这里i + j是不是会产生一个新的对象?
在内存中到底产生了几个对象呢?是3个还是4个?
作者:
余银桂
时间:
2012-6-26 03:08
本帖最后由 余银桂 于 2012-6-26 12:55 编辑
string k = i+j+ "c ";//这里i + j是不是会产生一个新的对象? i+j没有创建新的对象 "c" 如果常量池中没有就会创建新的对象
楼主这题答案应该是四个对象。
有个例子可以看看:
String s1 = "a";
String s2 = s1 + "b";
String s3 = "a" + "b";
System.out.println(s2 == "ab");
System.out.println(s3 == "ab");
第一条语句打印的结果为false,第二条语句打印的结果为true,这说明javac编译可以对字符串常量直接相加的表达式进行优化,不必要等到运行期去进行加法运算处理,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果。
题目中的第一行代码被编译器在编译时优化后,相当于直接定义了一个”abcd”的字符串,所以,上面的代码应该只创建了一个String对象。写如下两行代码,
String s = "a" + "b" + "c" + "d";
System.out.println(s == "abcd");
最终打印的结果应该为true。
作者:
王涛
时间:
2012-6-26 03:15
string i = "a ";
string j = "b ";
string k = i+j+ "c ";//这里的i+j+"c"应该会产生一个一个对象
如果常量池中没有这几个字符串常量的话,那么应该会产生3个
作者:
王涛
时间:
2012-6-26 03:19
你看看下面这个我摘抄别人的,讲的很仔细,看过应该就明白了
String str=”kvill”;
String str=new String (“kvill”);的区别:
常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。
看例1:
String s0=”kvill”;
String s1=”kvill”;
String s2=”kv” + “ill”;
System.out.println( s0==s1 );
System.out.println( s0==s2 );
结果为:
true
true
首先,我们要知道Java会确保一个字符串常量只有一个拷贝。
因为例子中的s0和s1中的”kvill”都是字符串常量,它们在编译期就被确定了,所以s0==s1为true;而”kv”和”ill”也都是字符串常量,当一个字符串由多个字符串常量连接而成时,它自己肯定也是字符串常量,所以s2也同样在编译期就被解析为一个字符串常量,所以s2也是常量池中”kvill”的一个引用。
所以我们得出s0==s1==s2;
用new String() 创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。
看例2:
String s0=”kvill”;
String s1=new String(”kvill”);
String s2=”kv” + new String(“ill”);
System.out.println( s0==s1 );
System.out.println( s0==s2 );
System.out.println( s1==s2 );
结果为:
false
false
false
例2中s0还是常量池中”kvill”的应用,s1因为无法在编译期确定,所以是运行时创建的新对象”kvill”的引用,s2因为有后半部分new String(“ill”)所以也无法在编译期确定,所以也是一个新创建对象”kvill”的应用;明白了这些也就知道为何得出此结果了。
4. String.intern():
再补充介绍一点:存在于.class文件中的常量池,在运行期被JVM装载,并且可以扩充。String的intern()方法就是扩充常量池的一个方法;当一个String实例str调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用;看例3就清楚了
例3:
String s0= “kvill”;
String s1=new String(”kvill”);
String s2=new String(“kvill”);
System.out.println( s0==s1 );
System.out.println( “**********” );
s1.intern();
s2=s2.intern(); //把常量池中“kvill”的引用赋给s2
System.out.println( s0==s1);
System.out.println( s0==s1.intern() );
System.out.println( s0==s2 );
结果为:
false
**********
false //虽然执行了s1.intern(),但它的返回值没有赋给s1
true //说明s1.intern()返回的是常量池中”kvill”的引用
true
作者:
孙飞
时间:
2012-6-26 06:58
给你个资料,讲的很详细
s = new String("xyz");创建了几个String Object?两个对象,一个是“xyx”,一个是指向“xyx”的引用对象s。
String s="你好";int i=3; s=i+s; 这个表达式对吗?在java中会提示数据类型不匹配。因为string是类!正确做法: s+="3" 或者 s+='3'或者 s+=(char)i;
我们要引入另外一种创建String对象的方式的讨论——引号内包含文本。这种方式是String特有的,并且它与new的方式存在很大区别。
在JAVA虚拟机(JVM)中存在着一个字符串池,其中保存着很多String对象,并且可以被共享使用,因此它提高了效率。String a="abc";,这行代码被执行的时候,JAVA虚拟机首先在字符串池中查找是否已经存在了值为"abc"的这么一个对象,判断依据是String类equals(Object obj)方法的返回值。如果有,则不再创建新的对象,直接返回已存在对象的引用;如果没有,则先创建这个对象,然后把它加入到字符串池中,再将它的引用返回。
字符串对象的创建:由于字符串对象的大量使用[它是一个对象,一般而言对象总是在heap分配内存],Java中为了节省内存空间和运行时间[如比较字符串时,==比equals()快],在编译阶段就把所有的字符串文字放到一个文字池中,而运行时文字池成为常量池的一部分。文字池的好处,就是该池中所有相同的字符串常量被合并,只占用一个空间。我们知道,对两个引用变量,使用==判断它们的值[引用]是否相等,即指向同一个对象:
现在看String s = new String("abc");语句,这里"abc"本身就是pool中的一个对象,而在运行时执行new String()时,将pool中的对象复制一份放到heap中,并且把heap中的这个对象的引用交给s持有。ok,这条语句就创建了2个String对象。
String s1 = new String("abc") ;String s2 = new String("abc") ;if( s1 == s2 ){ //不会执行的语句}
//创建了几个String Object? [三个,pool中一个,heap中2个。]
只有使用引号包含文本的方式创建的String对象之间使用“+”连接产生的新对象才会被加入字符串池中。对于所有包含new方式新建对象(包括null)的“+”连接表达式,它所产生的新对象都不会被加入字符串池中。
1.==表示引用自同一对象,equals()表示值相等。
String str1 = "abc";引用的对象在栈(或者叫String池)中。
String str1 =new String ("abc"); 引用的对象在内存/堆中。
2.String str1 = "string";在栈中
String str3 = "str";在栈中
String str4 = "ing";在栈中
String str2 = str3+str4; 在堆中,因为+号的作用是返回另外一个新建的String对象,而不是在栈中找string这个值。如果是String str2 = "str"+"ing";那最后的结果就在栈中。str1==str2为true。
但是有一种情况需要引起我们的注意。请看下面的代码:
public class StringStaticTest {
public static final String A = "ab"; // 常量A
public static final String B = "cd"; // 常量B
public static void main(String[] args) {
String s = A + B; // 将两个常量用+连接对s进行初始化
String t = "abcd";
if (s == t) {
System.out.println("s等于t,它们是同一个对象");
} else {
System.out.println("s不等于t,它们不是同一个对象");
}
}
}
这段代码的运行结果如下:
s等于t,它们是同一个对象
原因是在上面的例子中,A和B都是常量,值是固定的,因此s的值也是固定的,它在类被编译时就已经确定了。也就是说:String s=A+B; 等同于:String s="ab"+"cd";
我对上面的例子稍加改变看看会出现什么情况:
public class StringStaticTest {
public static final String A; // 常量A
public static final String B; // 常量B
static {
A = "ab";
B = "cd";
}
public static void main(String[] args) {
// 将两个常量用+连接对s进行初始化
String s = A + B;
String t = "abcd";
if (s == t) {
System.out.println("s等于t,它们是同一个对象");
} else {
System.out.println("s不等于t,它们不是同一个对象");
}
}
}
它的运行结果是这样:
s不等于t,它们不是同一个对象
只是做了一点改动,结果就和刚刚的例子恰好相反。我们再来分析一下。A和B虽然被定义为常量(只能被赋值一次),但是它们都没有马上被赋值。在运算出s的值之前,他们何时被赋值,以及被赋予什么样的值,都是个变数。因此A和B在被赋值之前,性质类似于一个变量。那么s就不能在编译期被确定,而只能在运行时被创建了。
最后我们再来说说String对象在JAVA虚拟机(JVM)中的存储,以及字符串池与堆(heap)和栈(stack)的关系。我们首先回顾一下堆和栈的区别:
栈(stack):主要保存基本类型(或者叫内置类型)(char、byte、short、int、long、float、double、boolean)和对象的引用,数据可以共享,速度仅次于寄存器(register),快于堆。
堆(heap):用于存储对象。
我们查看String类的源码就会发现,它有一个value属性,保存着String对象的值,类型是char[],这也正说明了字符串就是字符的序列。当执行String a="abc";时,JAVA虚拟机会在栈中创建三个char型的值'a'、'b'和'c',然后在堆中创建一个String对象,它的值(value)是刚才在栈中创建的三个char型值组成的数组{'a','b','c'},最后这个新创建的String对象会被添加到字符串池中。
如果我们接着执行String b=new String("abc");代码,由于"abc"已经被创建并保存于字符串池中,因此JAVA虚拟机只会在堆中新创建一个String对象,但是它的值(value)是共享前一行代码执行时在栈中创建的三个char型值值'a'、'b'和'c'。
作者:
张华廷
时间:
2012-6-26 08:50
关于String类创建对象的问题,:String str = new String("java")一共创建了几个对象这类问题。要弄清楚java虚拟机的运行机制:
JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。
栈 stack :用于保存线程执行的动作和数据引用。函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。
堆 heap:用于用来存放由new创建的对象和数组
常量池constant pool :在堆中分配出来的一块存储区域,其中保存着很多String对象,并且可以被共享使用,因此它提高了效率。由于String类是final的,它的值一经创建就不可改变,因此不用担心String对象共享而带来程序的混乱。
String str = "java";和String str = new String ("java");的区别。
在使用String str = "java";的格式定义对象时,总是想当然地认为,创建了String类的对象str。错!对象可能并没有被创建!唯一能确定的是,指向 String类的引用被创建了。至于这个引用到底是否指向了一个新的对象,必须根据上下文来考虑。因此,更为准确的说法是,只是创建了一个指向String类的对象的引用变量str,这个对象引用变量指向了某个值为"java"的String类。使用String str = "java";的方式,可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。
如果使用String str = new String("java");那么,会有两个String被创建,一个是Class被CLassLoader加载时,"java"被作为常量读入,在constant pool里创建了一个共享的"java",。 然后,当调用到new String("java")的时候,会在heap里创建这个new String("java");
[java] view plaincopy
01.String s1 = new String("java"); //创建二个对象
02.String s2 = new String("java"); //创建一个对象,并且以后每执行一次创建一个对象
03.String s1 = "java"; //创建一个对象
04.String s2 = "java"; //不创建对象,只是创建一个新的引用
因为String是唯一可以用字面量形式表示的对象,所以String字面量是一种特殊的字面量,它本身是一个对象
作者:
葛奎
时间:
2012-6-26 11:12
是4个对象,因为+号的作用 ,每加一次 就得出一个结果 而这个结果就是一个在内存中的string对象
只是没有引用指向这个对象
作者:
周朋飞
时间:
2012-6-26 11:54
string i = "a "; 栈中找
string j = "b "; 栈中找
string k = i+j+ "c "; 堆中
+运算符会创建一个新的对象在内存中,并且返回一个对象的引用
所以实际上是创建了两个对象 ,一个该对象 ,一个是对该对象的一个引用
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2