- 浏览: 213336 次
- 性别:
- 来自: 广州
文章分类
- 全部博客 (139)
- java (37)
- web (14)
- database (9)
- compute Net (1)
- design patten (4)
- 生活 (14)
- 求职 (5)
- j2me (1)
- 娱乐 (0)
- 漫画 (0)
- struts (5)
- hibernate (1)
- spring (2)
- ajax (3)
- oracle (3)
- UML&面向对象 (3)
- ffmpeg (2)
- eclipse (8)
- 技巧 (11)
- jsp (2)
- javascript (7)
- flex (2)
- xx (0)
- xxx (0)
- android (0)
- webservice (1)
- 博客 (1)
- jQuery (1)
- split (1)
- iss rewrite (1)
- 踩顶功能ajax (1)
- ext (2)
最新评论
-
longfu2012:
学习了
方法参数前加final的作用 -
irisAndKevin:
你的方法对整除有问题!
java 两数相除 四舍五入 精确 保留2位小数点、任意位小数点 -
wpf523:
...
方法参数前加final的作用 -
wpf523:
不错,解决了我的疑惑
方法参数前加final的作用 -
zhq426:
嗯,挺好用的吧
JAVASCRIPT 取得当前时间,包括农历时间 时间格式:2011年11月8日 16:54 星期二 农历辛卯年(兔) 十月十三 申时
JAVA面试题解惑系列(二)——到底创建了几个String对象?
关键字: java 面试题 string 创建几个对象
作者:臧圩人(zangweiren)
网址:http://zangweiren.iteye.com
>>>转载请注明出处!<<<
我们首先来看一段代码:
紧接着这段代码之后的往往是这个问题,那就是这行代码究竟创建了几个String对象呢?相信大家对这道题并不陌生,答案也是众所周知的,2个。接下来我们就从这道题展开,一起回顾一下与创建String对象相关的一些JAVA知识。
我们可以把上面这行代码分成String str、=、"abc"和new String()四部分来看待。String str只是定义了一个名为str的String类型的变量,因此它并没有创建对象;=是对变量str进行初始化,将某个对象的引用(或者叫句柄)赋值给 它,显然也没有创建对象;现在只剩下new String("abc")了。那么,new String("abc")为什么又能被看成"abc"和new String()呢?我们来看一下被我们调用了的String的构造器:
大家都知道,我们常用的创建一个类的实例(对象)的方法有以下两种:
我们正是使用new调用了String类的上面那个构造器方法创建了一个对象,并将它的引用赋值给了str变量。同时我们注意到,被调用的构造器 方法接受的参数也是一个String对象,这个对象正是"abc"。由此我们又要引入另外一种创建String对象的方式的讨论——引号内包含文本。
这种方式是String特有的,并且它与new的方式存在很大区别。
毫无疑问,这行代码创建了一个String对象。
那这里呢?答案还是一个。
再看看这里呢?答案仍是一个。有点奇怪吗?说到这里,我们就需要引入对字符串池相关知识的回顾了。
在JAVA虚拟机(JVM)中存在着一个字符串池,其中保存着很多String对象,并且可以被共享使用,因此它提高了效率。由于String类 是final的,它的值一经创建就不可改变,因此我们不用担心String对象共享而带来程序的混乱。字符串池由String类维护,我们可以调用 intern()方法来访问字符串池。
我们再回头看看String a="abc";,这行代码被执行的时候,JAVA虚拟机首先在字符串池中查找是否已经存在了值为"abc"的这么一个对象,它的判断依据是String 类equals(Object obj)方法的返回值。如果有,则不再创建新的对象,直接返回已存在对象的引用;如果没有,则先创建这个对象,然后把它加入到字符串池中,再将它的引用返 回。因此,我们不难理解前面三个例子中头两个例子为什么是这个答案了。
对于第三个例子:
由于常量的值在编译的时候就被确定了。在这里,"ab"和"cd"都是常量,因此变量a的值在编译时就可以确定。这行代码编译后的效果等同于:
因此这里只创建了一个对象"abcd",并且它被保存在字符串池里了。
现在问题又来了,是不是所有经过“+”连接后得到的字符串都会被添加到字符串池中呢?我们都知道“==”可以用来比较两个变量,它有以下两种情况:
下面我们就用“==”来做几个测试。为了便于说明,我们把指向字符串池中已经存在的对象也视为该对象被加入了字符串池:
运行结果如下:
从上面的结果中我们不难看出,只有使用引号包含文本的方式创建的String对象之间使用“+”连接产生的新对象才会被加入字符串池中。对于所有包含new方式新建对象(包括null)的“+”连接表达式,它所产生的新对象都不会被加入字符串池中,对此我们不再赘述。
但是有一种情况需要引起我们的注意。请看下面的代码:
这段代码的运行结果如下:
这又是为什么呢?原因是这样的,对于常量来讲,它的值是固定的,因此在编译期就能被确定了,而变量的值只有到运行时才能被确定,因为这个变量可以 被不同的方法调用,从而可能引起值的改变。在上面的例子中,A和B都是常量,值是固定的,因此s的值也是固定的,它在类被编译时就已经确定了。也就是说:
等同于:
我对上面的例子稍加改变看看会出现什么情况:
它的运行结果是这样:
只是做了一点改动,结果就和刚刚的例子恰好相反。我们再来分析一下。A和B虽然被定义为常量(只能被赋值一次),但是它们都没有马上被赋值。在运 算出s的值之前,他们何时被赋值,以及被赋予什么样的值,都是个变数。因此A和B在被赋值之前,性质类似于一个变量。那么s就不能在编译期被确定,而只能 在运行时被创建了。
由于字符串池中对象的共享能够带来效率的提高,因此我们提倡大家用引号包含文本的方式来创建String对象,实际上这也是我们在编程中常采用的。
接下来我们再来看看intern()方法,它的定义如下:
这是一个本地方法。在调用这个方法时,JAVA虚拟机首先检查字符串池中是否已经存在与该对象值相等对象存在,如果有则返回字符串池中对象的引用;如果没有,则先在字符串池中创建一个相同值的String对象,然后再将它的引用返回。
我们来看这段代码:
运行结果:
如果String类的intern()方法在没有找到相同值的对象时,是把当前对象加入字符串池中,然后返回它的引用的话,那么b和a指向的就是 同一个对象;否则b指向的对象就是JAVA虚拟机在字符串池中新建的,只是它的值与a相同罢了。上面这段代码的运行结果恰恰印证了这一点。
最后我们再来说说String对象在JAVA虚拟机(JVM)中的存储,以及字符串池与堆(heap)和栈(stack)的关系。我们首先回顾一下堆和栈的区别:
我们查看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'。
说到这里,我们对于篇首提出的String str=new String("abc")为什么是创建了两个对象这个问题就已经相当明了了。
网址:http://zangweiren.iteye.com
>>>转载请注明出处!<<<
我们首先来看一段代码:
String str=new String("abc");
紧接着这段代码之后的往往是这个问题,那就是这行代码究竟创建了几个String对象呢?相信大家对这道题并不陌生,答案也是众所周知的,2个。接下来我们就从这道题展开,一起回顾一下与创建String对象相关的一些JAVA知识。
我们可以把上面这行代码分成String str、=、"abc"和new String()四部分来看待。String str只是定义了一个名为str的String类型的变量,因此它并没有创建对象;=是对变量str进行初始化,将某个对象的引用(或者叫句柄)赋值给 它,显然也没有创建对象;现在只剩下new String("abc")了。那么,new String("abc")为什么又能被看成"abc"和new String()呢?我们来看一下被我们调用了的String的构造器:
public String(String original) { //other code ... }
大家都知道,我们常用的创建一个类的实例(对象)的方法有以下两种:
- 使用new创建对象。
- 调用Class类的newInstance方法,利用反射机制创建对象。
我们正是使用new调用了String类的上面那个构造器方法创建了一个对象,并将它的引用赋值给了str变量。同时我们注意到,被调用的构造器 方法接受的参数也是一个String对象,这个对象正是"abc"。由此我们又要引入另外一种创建String对象的方式的讨论——引号内包含文本。
这种方式是String特有的,并且它与new的方式存在很大区别。
String str="abc";
毫无疑问,这行代码创建了一个String对象。
String a="abc"; String b="abc";
那这里呢?答案还是一个。
String a="ab"+"cd";
再看看这里呢?答案仍是一个。有点奇怪吗?说到这里,我们就需要引入对字符串池相关知识的回顾了。
在JAVA虚拟机(JVM)中存在着一个字符串池,其中保存着很多String对象,并且可以被共享使用,因此它提高了效率。由于String类 是final的,它的值一经创建就不可改变,因此我们不用担心String对象共享而带来程序的混乱。字符串池由String类维护,我们可以调用 intern()方法来访问字符串池。
我们再回头看看String a="abc";,这行代码被执行的时候,JAVA虚拟机首先在字符串池中查找是否已经存在了值为"abc"的这么一个对象,它的判断依据是String 类equals(Object obj)方法的返回值。如果有,则不再创建新的对象,直接返回已存在对象的引用;如果没有,则先创建这个对象,然后把它加入到字符串池中,再将它的引用返 回。因此,我们不难理解前面三个例子中头两个例子为什么是这个答案了。
对于第三个例子:
String a="ab"+"cd";
由于常量的值在编译的时候就被确定了。在这里,"ab"和"cd"都是常量,因此变量a的值在编译时就可以确定。这行代码编译后的效果等同于:
String a="abcd";
因此这里只创建了一个对象"abcd",并且它被保存在字符串池里了。
现在问题又来了,是不是所有经过“+”连接后得到的字符串都会被添加到字符串池中呢?我们都知道“==”可以用来比较两个变量,它有以下两种情况:
- 如果比较的是两个基本类型(char,byte,short,int,long,float,double,boolean),则是判断它们的值是否相等。
- 如果表较的是两个对象变量,则是判断它们的引用是否指向同一个对象。
下面我们就用“==”来做几个测试。为了便于说明,我们把指向字符串池中已经存在的对象也视为该对象被加入了字符串池:
- public class StringTest {
- public static void main(String[] args) {
- String a = "ab";// 创建了一个对象,并加入字符串池中
- System.out.println("String a = \"ab\";");
- String b = "cd";// 创建了一个对象,并加入字符串池中
- System.out.println("String b = \"cd\";");
- String c = "abcd";// 创建了一个对象,并加入字符串池中
- String d = "ab" + "cd";
- // 如果d和c指向了同一个对象,则说明d也被加入了字符串池
- if (d == c) {
- System.out.println("\"ab\"+\"cd\" 创建的对象 \"加入了\" 字符串池中");
- }
- // 如果d和c没有指向了同一个对象,则说明d没有被加入字符串池
- else {
- System.out.println("\"ab\"+\"cd\" 创建的对象 \"没加入\" 字符串池中");
- }
- String e = a + "cd";
- // 如果e和c指向了同一个对象,则说明e也被加入了字符串池
- if (e == c) {
- System.out.println(" a +\"cd\" 创建的对象 \"加入了\" 字符串池中");
- }
- // 如果e和c没有指向了同一个对象,则说明e没有被加入字符串池
- else {
- System.out.println(" a +\"cd\" 创建的对象 \"没加入\" 字符串池中");
- }
- String f = "ab" + b;
- // 如果f和c指向了同一个对象,则说明f也被加入了字符串池
- if (f == c) {
- System.out.println("\"ab\"+ b 创建的对象 \"加入了\" 字符串池中");
- }
- // 如果f和c没有指向了同一个对象,则说明f没有被加入字符串池
- else {
- System.out.println("\"ab\"+ b 创建的对象 \"没加入\" 字符串池中");
- }
- String g = a + b;
- // 如果g和c指向了同一个对象,则说明g也被加入了字符串池
- if (g == c) {
- System.out.println(" a + b 创建的对象 \"加入了\" 字符串池中");
- }
- // 如果g和c没有指向了同一个对象,则说明g没有被加入字符串池
- else {
- System.out.println(" a + b 创建的对象 \"没加入\" 字符串池中");
- }
- }
- }
public class StringTest { public static void main(String[] args) { String a = "ab";// 创建了一个对象,并加入字符串池中 System.out.println("String a = \"ab\";"); String b = "cd";// 创建了一个对象,并加入字符串池中 System.out.println("String b = \"cd\";"); String c = "abcd";// 创建了一个对象,并加入字符串池中 String d = "ab" + "cd"; // 如果d和c指向了同一个对象,则说明d也被加入了字符串池 if (d == c) { System.out.println("\"ab\"+\"cd\" 创建的对象 \"加入了\" 字符串池中"); } // 如果d和c没有指向了同一个对象,则说明d没有被加入字符串池 else { System.out.println("\"ab\"+\"cd\" 创建的对象 \"没加入\" 字符串池中"); } String e = a + "cd"; // 如果e和c指向了同一个对象,则说明e也被加入了字符串池 if (e == c) { System.out.println(" a +\"cd\" 创建的对象 \"加入了\" 字符串池中"); } // 如果e和c没有指向了同一个对象,则说明e没有被加入字符串池 else { System.out.println(" a +\"cd\" 创建的对象 \"没加入\" 字符串池中"); } String f = "ab" + b; // 如果f和c指向了同一个对象,则说明f也被加入了字符串池 if (f == c) { System.out.println("\"ab\"+ b 创建的对象 \"加入了\" 字符串池中"); } // 如果f和c没有指向了同一个对象,则说明f没有被加入字符串池 else { System.out.println("\"ab\"+ b 创建的对象 \"没加入\" 字符串池中"); } String g = a + b; // 如果g和c指向了同一个对象,则说明g也被加入了字符串池 if (g == c) { System.out.println(" a + b 创建的对象 \"加入了\" 字符串池中"); } // 如果g和c没有指向了同一个对象,则说明g没有被加入字符串池 else { System.out.println(" a + b 创建的对象 \"没加入\" 字符串池中"); } } }
运行结果如下:
- String a = "ab";
- String b = "cd";
- "ab"+"cd" 创建的对象 "加入了" 字符串池中
- a +"cd" 创建的对象 "没加入" 字符串池中
- "ab"+ b 创建的对象 "没加入" 字符串池中
- a + b 创建的对象 "没加入" 字符串池中
从上面的结果中我们不难看出,只有使用引号包含文本的方式创建的String对象之间使用“+”连接产生的新对象才会被加入字符串池中。对于所有包含new方式新建对象(包括null)的“+”连接表达式,它所产生的新对象都不会被加入字符串池中,对此我们不再赘述。
但是有一种情况需要引起我们的注意。请看下面的代码:
- public class StringStaticTest {
- // 常量A
- public static final String A = "ab";
- // 常量B
- public static final String 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,它们不是同一个对象");
- }
- }
- }
public class StringStaticTest { // 常量A public static final String A = "ab"; // 常量B public static final String 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的值也是固定的,它在类被编译时就已经确定了。也就是说:
String s=A+B;
等同于:
String s="ab"+"cd";
我对上面的例子稍加改变看看会出现什么情况:
- public class StringStaticTest {
- // 常量A
- public static final String A;
- // 常量B
- public static final String 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,它们不是同一个对象");
- }
- }
- }
public class StringStaticTest { // 常量A public static final String A; // 常量B public static final String 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对象,实际上这也是我们在编程中常采用的。
接下来我们再来看看intern()方法,它的定义如下:
public native String intern();
这是一个本地方法。在调用这个方法时,JAVA虚拟机首先检查字符串池中是否已经存在与该对象值相等对象存在,如果有则返回字符串池中对象的引用;如果没有,则先在字符串池中创建一个相同值的String对象,然后再将它的引用返回。
我们来看这段代码:
- public class StringInternTest {
- public static void main(String[] args) {
- // 使用char数组来初始化a,避免在a被创建之前字符串池中已经存在了值为"abcd"的对象
- String a = new String(new char[] { 'a', 'b', 'c', 'd' });
- String b = a.intern();
- if (b == a) {
- System.out.println("b被加入了字符串池中,没有新建对象");
- } else {
- System.out.println("b没被加入字符串池中,新建了对象");
- }
- }
- }
public class StringInternTest { public static void main(String[] args) { // 使用char数组来初始化a,避免在a被创建之前字符串池中已经存在了值为"abcd"的对象 String a = new String(new char[] { 'a', 'b', 'c', 'd' }); String b = a.intern(); if (b == a) { System.out.println("b被加入了字符串池中,没有新建对象"); } else { System.out.println("b没被加入字符串池中,新建了对象"); } } }
运行结果:
- b没被加入字符串池中,新建了对象
如果String类的intern()方法在没有找到相同值的对象时,是把当前对象加入字符串池中,然后返回它的引用的话,那么b和a指向的就是 同一个对象;否则b指向的对象就是JAVA虚拟机在字符串池中新建的,只是它的值与a相同罢了。上面这段代码的运行结果恰恰印证了这一点。
最后我们再来说说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'。
说到这里,我们对于篇首提出的String str=new String("abc")为什么是创建了两个对象这个问题就已经相当明了了。
- 18:45
- 浏览 (10294)
- 评论 (92)
- 分类: JAVA面试题解惑系列
- 相关推荐
评论
magical4061
2008-09-01
回复
回复 simple is power :
其实,咱们可以有另外一种更加简单的理解方法(这种方法只是便于大家理解,并非就是正确的),就是说
String s = "abc";
这个语句,是直接在字符串池中创建了一个对象,而 s 就是指向 字符串池中 这个对象的引用。
String s1 = new String("abc");
这个语句,在上一个语句的基础上(即字符串池中已经存在了一个abc对象),然后new 操作符 在堆中又创建了一个对象 ,这个对象的引用就是 s1.
而 s1.intern() 方法 ,就是在字符串池中查找 abc 对象,如果存在,就返回 字符串池中abc的引用(这里就是s)。
这样理解起来,是不是更容易一点呢。
其实,咱们可以有另外一种更加简单的理解方法(这种方法只是便于大家理解,并非就是正确的),就是说
String s = "abc";
这个语句,是直接在字符串池中创建了一个对象,而 s 就是指向 字符串池中 这个对象的引用。
String s1 = new String("abc");
这个语句,在上一个语句的基础上(即字符串池中已经存在了一个abc对象),然后new 操作符 在堆中又创建了一个对象 ,这个对象的引用就是 s1.
而 s1.intern() 方法 ,就是在字符串池中查找 abc 对象,如果存在,就返回 字符串池中abc的引用(这里就是s)。
这样理解起来,是不是更容易一点呢。
simple is power
2008-09-01
回复
如magical4061所说,如果下面的论述成立:
"String s = “abc”;
这里只是在堆中创建了一个对象,字符串池中并没有任何对象,只是存放了一个对于 “abc”对象的引用。 "
那么
String s = new String("abc");
单独这样一条语句也只是在堆中创建了一个对象,字符串池中并没有创建任何对象。
可不可以这样理解呢
"String s = “abc”;
这里只是在堆中创建了一个对象,字符串池中并没有任何对象,只是存放了一个对于 “abc”对象的引用。 "
那么
String s = new String("abc");
单独这样一条语句也只是在堆中创建了一个对象,字符串池中并没有创建任何对象。
可不可以这样理解呢
magical4061
2008-09-01
回复
让我试着来回复一下 simple is power 吧,看看我解释的对不对。
这点你是理解错误了,这里只是在堆中创建了一个对象,字符串池中并没有任何对象,只是存放了一个对于 “abc”对象的引用。
new操作符才另外在堆中创建了一个新的对象,这个新对象的引用就是str.
你所说的这2段代码,str1 都是 "string" 这个对象的引用,而str2 是 new操作符新创建的对象的引用。 而 “==”是比较两个对象的引用的,自然不会输出 "1 true" 。而equals 是比较两个String对象的字面值的,他们都是 "string" ,所以会输出 "2 true"来。
引用
String a="abc";
一句创建了两个对象,一个在堆中,一个在字符串池中
一句创建了两个对象,一个在堆中,一个在字符串池中
这点你是理解错误了,这里只是在堆中创建了一个对象,字符串池中并没有任何对象,只是存放了一个对于 “abc”对象的引用。
引用
而如果接下来在有 String str=new String("abc");
这样的语句则只在堆中创建一个对象。
这样的语句则只在堆中创建一个对象。
new操作符才另外在堆中创建了一个新的对象,这个新对象的引用就是str.
引用
(一)
String str1 = "string";
String str2 = new String("string");
if(str1==str2)
System.out.println("1 true");
if (str1.equals(str2))
System.out.println("2 true");
(二)
String str2 = new String("string");
String str1 = "string";
if(str1==str2)
System.out.println("1 true");
if (str1.equals(str2))
System.out.println("2 true");
为什么上面两种方式的输出结果一样,都是输出第二个语句呢?
String str1 = "string";
String str2 = new String("string");
if(str1==str2)
System.out.println("1 true");
if (str1.equals(str2))
System.out.println("2 true");
(二)
String str2 = new String("string");
String str1 = "string";
if(str1==str2)
System.out.println("1 true");
if (str1.equals(str2))
System.out.println("2 true");
为什么上面两种方式的输出结果一样,都是输出第二个语句呢?
你所说的这2段代码,str1 都是 "string" 这个对象的引用,而str2 是 new操作符新创建的对象的引用。 而 “==”是比较两个对象的引用的,自然不会输出 "1 true" 。而equals 是比较两个String对象的字面值的,他们都是 "string" ,所以会输出 "2 true"来。
simple is power
2008-08-31
回复
您的文章写的很好,学习了。
文章开篇即点题,指出
String str=new String("abc");
一句创建了两个对象,然后接下来思路一直非常清晰,虽然通篇未提第二个对象在哪里。
直到最后结尾时,反倒让我迷惑了。
最后一段让我觉得是
String a="abc";
一句创建了两个对象,一个在堆中,一个在字符串池中,而如果接下来在有
String str=new String("abc");
这样的语句则只在堆中创建一个对象。
请问到底是谁创建2个对象。
还有,如果String str=new String("abc");它创建两个对象,那么引用指向的是哪一个?如果String a="abc";创建两个对象,引用指向的是哪一个?
(一)
String str1 = "string";
String str2 = new String("string");
if(str1==str2)
System.out.println("1 true");
if (str1.equals(str2))
System.out.println("2 true");
(二)
String str2 = new String("string");
String str1 = "string";
if(str1==str2)
System.out.println("1 true");
if (str1.equals(str2))
System.out.println("2 true");
为什么上面两种方式的输出结果一样,都是输出第二个语句呢?
请解答。
文章开篇即点题,指出
String str=new String("abc");
一句创建了两个对象,然后接下来思路一直非常清晰,虽然通篇未提第二个对象在哪里。
直到最后结尾时,反倒让我迷惑了。
最后一段让我觉得是
String a="abc";
一句创建了两个对象,一个在堆中,一个在字符串池中,而如果接下来在有
String str=new String("abc");
这样的语句则只在堆中创建一个对象。
请问到底是谁创建2个对象。
还有,如果String str=new String("abc");它创建两个对象,那么引用指向的是哪一个?如果String a="abc";创建两个对象,引用指向的是哪一个?
(一)
String str1 = "string";
String str2 = new String("string");
if(str1==str2)
System.out.println("1 true");
if (str1.equals(str2))
System.out.println("2 true");
(二)
String str2 = new String("string");
String str1 = "string";
if(str1==str2)
System.out.println("1 true");
if (str1.equals(str2))
System.out.println("2 true");
为什么上面两种方式的输出结果一样,都是输出第二个语句呢?
请解答。
String s1 = "ab";
String s2 = "cd";
String str = "abcd";
(s1 + s2) 是不等于 str 的. (JDK1.5以上...JDK1.5以前的没试过)
不等的原因:
例子:
public class StringTest
{
public static void main(String[] args) {
String s = "";
for (int i = 0; i < 10; i++ )
{
s += "abc";
}
System.out.println(s);
}
}
//----------------------------------
javap之后的代码:
public class StringTest extends java.lang.Object{
public StringTest();
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
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: bipush 10
8: if_icmpge 37
11: new #3; //class java/lang/StringBuilder
14: dup
15: invokespecial #4; //Method java/lang/StringBuilder."<init>":()V
18: aload_1
19: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/
String;)Ljava/lang/StringBuilder;
22: ldc #6; //String abc
24: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/
String;)Ljava/lang/StringBuilder;
27: invokevirtual #7; //Method java/lang/StringBuilder.toString:()Ljava/la
ng/String;
30: astore_1
31: iinc 2, 1
34: goto 5
37: getstatic #8; //Field java/lang/System.out:Ljava/io/PrintStream;
40: aload_1
41: invokevirtual #9; //Method java/io/PrintStream.println:(Ljava/lang/Str
ing;)V
44: return
}
注意:里面有第十一行11: new #3; //class java/lang/StringBuilder
虽然代码里全部都是用"+"号连接字符串,
但编译器还是比较聪明的,它会生成一个StringBuilder对象来进行字符串的连接工作.
经过StringBuilder这一道手,转化后的String对象就是一个新对象了.
("ab" + "cd") 与原生的 "abcd" 就不是同一个对象了.
String s2 = "cd";
String str = "abcd";
(s1 + s2) 是不等于 str 的. (JDK1.5以上...JDK1.5以前的没试过)
不等的原因:
例子:
public class StringTest
{
public static void main(String[] args) {
String s = "";
for (int i = 0; i < 10; i++ )
{
s += "abc";
}
System.out.println(s);
}
}
//----------------------------------
javap之后的代码:
public class StringTest extends java.lang.Object{
public StringTest();
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
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: bipush 10
8: if_icmpge 37
11: new #3; //class java/lang/StringBuilder
14: dup
15: invokespecial #4; //Method java/lang/StringBuilder."<init>":()V
18: aload_1
19: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/
String;)Ljava/lang/StringBuilder;
22: ldc #6; //String abc
24: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/
String;)Ljava/lang/StringBuilder;
27: invokevirtual #7; //Method java/lang/StringBuilder.toString:()Ljava/la
ng/String;
30: astore_1
31: iinc 2, 1
34: goto 5
37: getstatic #8; //Field java/lang/System.out:Ljava/io/PrintStream;
40: aload_1
41: invokevirtual #9; //Method java/io/PrintStream.println:(Ljava/lang/Str
ing;)V
44: return
}
注意:里面有第十一行11: new #3; //class java/lang/StringBuilder
虽然代码里全部都是用"+"号连接字符串,
但编译器还是比较聪明的,它会生成一个StringBuilder对象来进行字符串的连接工作.
经过StringBuilder这一道手,转化后的String对象就是一个新对象了.
("ab" + "cd") 与原生的 "abcd" 就不是同一个对象了.
jiakechong
2008-08-03
回复
楼主可以介绍1篇
java.util.这个包这个经常用,如map(hashmap,hashtable)
list(arraylist.vector..) ,set等等
java.util.这个包这个经常用,如map(hashmap,hashtable)
list(arraylist.vector..) ,set等等
jiakechong
2008-08-03
回复
引用 lggege 2008-07-03
1.
JVM 中, 线程持有Stack, Stack随线程创建而创建.
Heap 则是JVM启动时就启动, 共享, 由垃圾回收机制清理.
2.
Stack 和 Heap都是在Ram中划分.
3.
基本型 int, char...等在创建前会查看Stack中是否已经有, 有则则向, 没有则新建.
4.
String 可以想象为与char[]等同, String a= "abc", 首先在Heap中创一个对象, 再到Stack中找char[]是否存在, 有则 指向该地址, 无则在Stack中创建.
5.
String a = new("abc"); new()出来的都会有自己的独立存放, 不会像前面一样的指向, 而是指向新创建的.
6.
== 基本型比值, 对象比引用.
1.
JVM 中, 线程持有Stack, Stack随线程创建而创建.
Heap 则是JVM启动时就启动, 共享, 由垃圾回收机制清理.
2.
Stack 和 Heap都是在Ram中划分.
3.
基本型 int, char...等在创建前会查看Stack中是否已经有, 有则则向, 没有则新建.
4.
String 可以想象为与char[]等同, String a= "abc", 首先在Heap中创一个对象, 再到Stack中找char[]是否存在, 有则 指向该地址, 无则在Stack中创建.
5.
String a = new("abc"); new()出来的都会有自己的独立存放, 不会像前面一样的指向, 而是指向新创建的.
6.
== 基本型比值, 对象比引用.
回复naff:
关于字符串池请参考这个回复:
不过遗憾的是,关于字符串池,我还没有找到专门的深入介绍其内部结构与实现的资料。
关于字符串池请参考这个回复:
臧圩人 写道
回复nbawukun:
对象总是被保存在堆中,而引用总是被保存在栈中。
实际上是字符串池搅乱了我们的思路。我觉得可以把它看作一个数组,字符串被保存池中,实际上只是在字符串池中保存了这个字符串的引用,实际的字符 串对象仍像其它对象一样被保存在堆中了。只不过通过new创建的字符串对象在被保存在堆中的同时,并没有在字符串池中保存它的引用,因此字符串池并不知道 它的存在,也就无法将它共享使用。
对象总是被保存在堆中,而引用总是被保存在栈中。
实际上是字符串池搅乱了我们的思路。我觉得可以把它看作一个数组,字符串被保存池中,实际上只是在字符串池中保存了这个字符串的引用,实际的字符 串对象仍像其它对象一样被保存在堆中了。只不过通过new创建的字符串对象在被保存在堆中的同时,并没有在字符串池中保存它的引用,因此字符串池并不知道 它的存在,也就无法将它共享使用。
不过遗憾的是,关于字符串池,我还没有找到专门的深入介绍其内部结构与实现的资料。
接下来我们再来看看intern()方法,它的定义如下:
Java代码
public native String intern();
public native String intern();
这是一个本地方法。在调用这个方法时,JAVA虚拟机首先检查字符串池中是否已经存在与该对象值相等对象存在,如果有则返回字符串池中对象的引用;如果没有,则先在字符串池中创建一个相同值的String对象,然后再将它的引用返回。
我们来看这段代码:
Java代码
public class StringInternTest {
public static void main(String[] args) {
// 使用char数组来初始化a,避免在a被创建之前字符串池中已经存在了值为"abcd"的对象
String a = new String(new char[] { 'a', 'b', 'c', 'd' });
String b = a.intern();
if (b == a) {
System.out.println("b被加入了字符串池中,没有新建对象");
} else {
System.out.println("b没被加入字符串池中,新建了对象");
}
}
}
public class StringInternTest {
public static void main(String[] args) {
// 使用char数组来初始化a,避免在a被创建之前字符串池中已经存在了值为"abcd"的对象
String a = new String(new char[] { 'a', 'b', 'c', 'd' });
String b = a.intern();
if (b == a) {
System.out.println("b被加入了字符串池中,没有新建对象");
} else {
System.out.println("b没被加入字符串池中,新建了对象");
}
}
}
运行结果:
b没被加入字符串池中,新建了对象
=====================
以上是您文章内的一段。
能否请您详细的解说下代码的分析.
小弟新学,请赐教.
想问下字符串池它的方式和属性之类的.
谢谢.
Java代码
public native String intern();
public native String intern();
这是一个本地方法。在调用这个方法时,JAVA虚拟机首先检查字符串池中是否已经存在与该对象值相等对象存在,如果有则返回字符串池中对象的引用;如果没有,则先在字符串池中创建一个相同值的String对象,然后再将它的引用返回。
我们来看这段代码:
Java代码
public class StringInternTest {
public static void main(String[] args) {
// 使用char数组来初始化a,避免在a被创建之前字符串池中已经存在了值为"abcd"的对象
String a = new String(new char[] { 'a', 'b', 'c', 'd' });
String b = a.intern();
if (b == a) {
System.out.println("b被加入了字符串池中,没有新建对象");
} else {
System.out.println("b没被加入字符串池中,新建了对象");
}
}
}
public class StringInternTest {
public static void main(String[] args) {
// 使用char数组来初始化a,避免在a被创建之前字符串池中已经存在了值为"abcd"的对象
String a = new String(new char[] { 'a', 'b', 'c', 'd' });
String b = a.intern();
if (b == a) {
System.out.println("b被加入了字符串池中,没有新建对象");
} else {
System.out.println("b没被加入字符串池中,新建了对象");
}
}
}
运行结果:
b没被加入字符串池中,新建了对象
=====================
以上是您文章内的一段。
能否请您详细的解说下代码的分析.
小弟新学,请赐教.
想问下字符串池它的方式和属性之类的.
谢谢.
回复infante_yin:
请参照下面这个例子,它们的原理是相同的。在执行“String d=a+b;”之前,a和b的值都可以能被其他代码改变,因此d是一个在执行时才可以确定值的变量,它不同于“String d="ab"+"cd";”。
上面这个例子的输入结果是:s不等于t,它们不是同一个对象。
对照文中前面一个例子,就更容易理解了。
infante_yin 写道
LZ基于下面的代码可以给我解释一下吗?String类本身就是final的了!
String a = "ab";
String b = "cd";
String c = "abcd";
String d = a + b;
System.out.println(c==d);
String a = "ab";
String b = "cd";
String c = "abcd";
String d = a + b;
System.out.println(c==d);
请参照下面这个例子,它们的原理是相同的。在执行“String d=a+b;”之前,a和b的值都可以能被其他代码改变,因此d是一个在执行时才可以确定值的变量,它不同于“String d="ab"+"cd";”。
臧圩人 写道
public class StringStaticTest {
// 常量A
public static final String A;
// 常量B
public static final String 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,它们不是同一个对象");
}
}
}
// 常量A
public static final String A;
// 常量B
public static final String 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,它们不是同一个对象。
对照文中前面一个例子,就更容易理解了。
infante_yin
2008-07-30
回复
LZ基于下面的代码可以给我解释一下吗?String类本身就是final的了!
String a = "ab";
String b = "cd";
String c = "abcd";
String d = a + b;
System.out.println(c==d);
String a = "ab";
String b = "cd";
String c = "abcd";
String d = a + b;
System.out.println(c==d);
发表评论
-
java 两数相除 四舍五入 精确 保留2位小数点、任意位小数点
2011-11-03 17:53 7214java 四舍五入 精确 保留2位小数点、任意位小数点 ... -
Java中对字符串进行加密和解密
2011-08-18 17:14 17791.引言在实际工作中我们常常会遇到对数据进行加密解密的工作(如 ... -
两个日期之间的天数差
2011-08-11 11:02 1641/** * 两个日期之间的天数差 * @pa ... -
java split 特殊字符 分隔数组
2011-07-28 14:48 2158public class Test { /** ... -
JAVA 取得IP地址
2011-07-21 10:32 1051/** * 取得IP地址 * @param ... -
杯具博客设计
2011-07-08 22:41 883又是遇到了一个悲催的问题。 博客: 博客的设计是这样的: ... -
webservice 同步问题
2011-07-08 22:26 1083今天遇到了一个很郁闷个问题,webservice 同步问题。 ... -
二级栏目树构建
2011-05-30 15:15 822输出栏目树 public class Entr ... -
ttt
2009-10-10 12:50 782<script type="text/ ... -
JXL, POI操作excel文件zz
2009-09-27 11:59 16632008-11-10 JXL, POI操作excel文件z ... -
jxl 写 excel
2009-09-27 11:57 1061package net.blogjava.chenlb;imp ... -
方法参数前加final的作用
2009-07-17 11:01 2581方法参数前加final的作用 2009-06-28 ... -
java 调用ffmpeg执行dos bat命令 带有两个参数
2009-03-12 16:58 3466import java.io.IOException; pu ... -
java 调用ffmpeg执行dos命令
2009-03-12 16:10 1993import java.io.File; import ja ... -
增强Eclipse(MyEclipse)输入代码提示功能
2009-03-02 16:16 1659增强Eclipse(MyEclipse)输入代码提示功能 ... -
myeclipse性能提升技巧
2009-03-02 16:07 947myeclipse性能提升技巧 200 ... -
Java JDK :小心使用 boxing
2008-12-05 18:33 764[size=large]Java JDK :小心使用 boxi ... -
Java JDK:自动装箱和拆箱
2008-12-05 18:06 876[size=large]Java JDK:自动 ... -
JDK5.0新特性介绍
2008-11-16 19:13 803JDK5.0新特性介绍 [转贴 2006-02-23 15: ... -
java面向对象我基本特征
2008-11-04 15:13 801继承:子类继承父类成为特殊的父类。直接获得父类的属性和方法。是 ...
相关推荐
JAVA 面试题解惑系列之 String 对象创建机制 本文将深入探讨 JAVA 中 String 对象的创建机制,解答常见的面试题目,并探索 String 对象池的概念和机制。 一、String 对象的创建方式 在 JAVA 中,String 对象可以...
.3 1.2 JAVA面试题解惑系列(二)——到底创建了几个String对象? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8 1.3 JAVA面试题解惑系列(三)——变量(属性)的覆盖 . . . . . . . . . . . . ....
**1.2 JAVA面试题解惑系列(二)——到底创建了几个String对象?** - **知识点**:探讨Java中String对象的创建机制,特别是常量池的概念和String对象的内存分配策略。了解字符串字面量与`new String()`的区别,以及...
这个压缩包可能包含了两本书籍,一本是"JAVA面试题解惑系列.pdf",另一本是"臧圩人--JAVA面试题解惑系列合集.pdf"。这些资源旨在帮助求职者准备Java开发相关的面试,提升他们对Java技术的理解和应用能力。 在Java...
2、到底创建了几个String对象;3、变量(属性)的覆盖;4、final,finally,finalize;5.传了值还是传了引用;6.String杂谈;7.日期与时间的处理;8.基本类型总结;9.继承,多态,重载,重写;10.多线程;11.运算符...
首先,我们来看“JAVA面试题解惑系列(二)——到底创建了几个String对象?”这个问题。在Java中,String是不可变的,这意味着一旦创建,其内容就不能改变。因此,每当对String进行操作,如拼接或赋值,实际上可能...
《经典JAVA面试题解惑系列合集(臧圩人)》这本书很可能就是针对这些问题进行深入解析的一本指南。 首先,Java的基础知识是面试中的必考部分。这包括但不限于Java语法、数据类型、控制结构(如if语句、for循环、...
【JAVA面试题解惑系列】是一系列专门针对Java开发者面试准备的文章集合,涵盖了多个核心Java概念和面试常问问题。作者臧圩人在JavaEye社区分享了这个系列,旨在帮助求职者理解和解答面试中可能出现的疑问。 1. **类...
在Java中,类的初始化顺序是一个经常被问及的面试题目,尤其对于Java程序员和工程师来说,了解这个知识点是必须的。在Java中,类的初始化顺序是面试中的经典问题之一,它涉及到继承、静态成员以及实例成员等多个方面...
Java中的字符串(String)是编程面试中的热门话题,其特性与操作是面试官常常用来考察候选人基础知识的手段。本文将深入探讨String对象的一些关键知识点。 首先,我们要了解`String`类的`length()`方法。这个方法返回...
JAVA面试题解惑系列(一)——类的初始化顺序 JAVA 是一门面向对象的编程语言,类的初始化顺序是 JAVA 程序员和 JAVA 工程师面试中一个非常重要的知识点。本文将详细讲解类的初始化顺序,并提供了相关的测试代码,...
《JAVA面试题解惑系列——类的初始化顺序》 在Java编程中,理解类的初始化顺序是面试中常见的考察点,因为它直接关系到程序的执行逻辑。本文将深入探讨类的初始化过程,以及在继承场景下如何理解这个过程。 首先,...
Java面试题是每个Java开发者在求职过程中必须面对的挑战,涵盖范围广泛,从基础概念到高级特性和设计模式。这份终极列表包含115个Java面试题和答案,旨在帮助求职者全面准备,以期在面试中表现出色。以下是部分核心...
本文通过对Java面试题中的两个典型问题——类的初始化顺序和String对象的创建——进行了深入分析。这些知识点不仅是Java编程的基础,也是深入理解Java语言特性的关键。掌握这些内容不仅能够帮助开发者在面试中表现...