`

JAVA面试题解惑系列(二)——到底创建了几个String对象?

阅读更多
removed.

请到博文下载PDF文件:http://zangweiren.iteye.com/blog/241218
82
7
分享到:
评论
100 楼 xunke515 2013-09-09  
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没被加入字符串池中,新建了对象");
}
}
}

运行结果:
1. b没被加入字符串池中,新建了对象
----------------------------------

我这里运行给出的是   "b被加入了字符串池中,没有新建对象"  . 楼主做过测试用例么,可否指点下迷津.
99 楼 woaiyingyu123 2010-05-21  
LZ写得很详细。以后多写点哦。楼主。谢谢!
98 楼 wzju64676266 2010-03-04  
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对象,实际上这也是我们在编程中常采用的。


我不知道你解释的是哪个版本的jdk,在jdk1.5下你的解释是有问题的
         public static final String A;

// 常量B
public static final String B;

static {
A = "ab";
B = "cd";
}
这么定义的话jdk会把代码优化成
public static final String A= "ab";

// 常量B
public static final String B= "cd";

省去了
static {
A = "ab";
B = "cd";
}
这个环节。。。。
而下面的
String s = A + B;
会变成String s = (new StringBuilder(String.valueOf(A))).append(B).toString();


最终编译器会把你的代码编译成
String s =  (new StringBuilder(String.valueOf(A))).append(B).toString();
String t = "abcd";
if (s == t) {
System.out.println("s等于t,它们是同一个对象");
} else {
System.out.println("s不等于t,它们不是同一个对象");
}



试问这怎么会相等呢,您写得相当不错,有很多知识是从您这里学过来的,^_^

97 楼 netscript 2009-05-07  
不知道博主还能不能看到我的回复,我想对你说你是个挺负责任的博主,帮很多人回复并解释了问题,所以我看完你的帖子后也有个自己的理解,想请你指教一下我理解的对不对,String str1 = new String("abcd");这行代码创建了2个对象,一个是str1指向的那个对象,一个是"abcd",它们都放在了堆里,并且把"abcd"这个对象的引用又放到了栈(字符串池)里,当再执行一次String str2 = new String("abcd");这行代码,那么这次就只创建了一个对象也就是str2指向的那个对象,因为"abcd"已经在栈里了,当执行String str3 = "abcd";这行代码时并没有创建对象,只是从栈(字符串池)中取出了已经存在的"abcd"对象的引用给了str3,先说这些,不确定对不对,我是初学者,在找工作,多请博主和大家指教了,谢谢。
96 楼 Ronald9 2008-12-13  
1)看JDK帮助文档
2)看JDK源码(在JDK中包含有,可以通过阅读源码借鉴别人的思路,并了解JDK的实现)
3)看SCJP相关文档

4)百度或google
95 楼 Ronald9 2008-12-13  
1)
String str1 = "hello";
这句话执行时,先会在 字符串常量池 中看有没有字符串"hello",如果有,则将引用变量str1指向字符串"hello";如果没有,则创建"hello"然后将str1指向它;

内存分布情况:
引用变量str1在栈
"hello"在字符串常量池


System.out.println(str1.equals("hello"));

输出结果:true
因为他们都指向的是字符串常量池中的字符串"hello"

2)String str2 = new String("hello");

这句话执行时,先会在 字符串常量池 中看有没有字符串"hello",如果有,则将复制一份并在堆中创建一个对象,引用变量str2指向堆中的字符串对象,其值为:字符串"hello";如果没有,则先在字符串常量池中创建"hello"然后执行上面的步骤.

内存分布情况:
引用变量str1在栈
"hello"在字符串常量池
new String("hello")在堆中

System.out.println(str1==str2);
输出结果:false

str1指向的是字符串常量池中的"hello"
str2指向的是堆中的字符串对象"hello"

3)String str3 = new String("hello");
str3 = str3.intern();
System.out.println(str1==str3);
输出结果:true

str3 = str3.intern();
str3将引用字符串常量池中的"hello"
具体解释请参看JDK帮助文档相关内容.



94 楼 jxausea 2008-12-09  
楼主:我想知道String str=new String("abc");  这行代码在内存中具体的操作。
你能帮我画一下在内存中的过程吗?(把堆和栈中的数据也表示出来)
谢谢LZ
93 楼 ZangXT 2008-10-07  
java 虚拟机规范里面有对常量池的介绍.
92 楼 lkjust08 2008-09-18  
请问楼主,对象的引用保存干什么地方? 字符串池中又是保有存的什么东东?
91 楼 magical4061 2008-09-01  
回复 simple is power :
     其实,咱们可以有另外一种更加简单的理解方法(这种方法只是便于大家理解,并非就是正确的),就是说
     String s = "abc";
    这个语句,是直接在字符串池中创建了一个对象,而 s 就是指向 字符串池中 这个对象的引用。
     String s1 = new String("abc");
    这个语句,在上一个语句的基础上(即字符串池中已经存在了一个abc对象),然后new 操作符 在堆中又创建了一个对象 ,这个对象的引用就是 s1.
    而 s1.intern() 方法 ,就是在字符串池中查找 abc 对象,如果存在,就返回 字符串池中abc的引用(这里就是s)。
    这样理解起来,是不是更容易一点呢。
90 楼 simple is power 2008-09-01  
如magical4061所说,如果下面的论述成立:

"String s = “abc”;
这里只是在堆中创建了一个对象,字符串池中并没有任何对象,只是存放了一个对于 “abc”对象的引用。 "

那么
String s = new String("abc");
单独这样一条语句也只是在堆中创建了一个对象,字符串池中并没有创建任何对象。

可不可以这样理解呢
89 楼 magical4061 2008-09-01  
让我试着来回复一下 simple is power 吧,看看我解释的对不对。

引用
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");
为什么上面两种方式的输出结果一样,都是输出第二个语句呢?

你所说的这2段代码,str1 都是 "string" 这个对象的引用,而str2 是 new操作符新创建的对象的引用。 而 “==”是比较两个对象的引用的,自然不会输出 "1 true" 。而equals 是比较两个String对象的字面值的,他们都是 "string" ,所以会输出 "2 true"来。


88 楼 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");
为什么上面两种方式的输出结果一样,都是输出第二个语句呢?
请解答。

87 楼 yu_xian81 2008-08-25  
看起头在转.看来只有在头最清醒的时候看,呵呵
86 楼 H_eaven 2008-08-05  
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" 就不是同一个对象了.

85 楼 臧圩人 2008-08-03  
回复jiakechong:

你好,后续的文章会有介绍集合类的部分的,请保持关注
84 楼 jiakechong 2008-08-03  
楼主可以介绍1篇

 java.util.这个包这个经常用,如map(hashmap,hashtable)
 list(arraylist.vector..) ,set等等
83 楼 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.
== 基本型比值, 对象比引用.
 
82 楼 臧圩人 2008-08-02  
回复naff:

关于字符串池请参考这个回复:

臧圩人 写道
回复nbawukun:

对象总是被保存在堆中,而引用总是被保存在栈中。

实际上是字符串池搅乱了我们的思路。我觉得可以把它看作一个数组,字符串被保存池中,实际上只是在字符串池中保存了这个字符串的引用,实际的字符串对象仍像其它对象一样被保存在堆中了。只不过通过new创建的字符串对象在被保存在堆中的同时,并没有在字符串池中保存它的引用,因此字符串池并不知道它的存在,也就无法将它共享使用。


不过遗憾的是,关于字符串池,我还没有找到专门的深入介绍其内部结构与实现的资料。
81 楼 naff 2008-07-31  
接下来我们再来看看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没被加入字符串池中,新建了对象







=====================
以上是您文章内的一段。
能否请您详细的解说下代码的分析.
小弟新学,请赐教.
想问下字符串池它的方式和属性之类的.
谢谢.

相关推荐

Global site tag (gtag.js) - Google Analytics