锁定老帖子 主题:从其他地方移来的Java面题,欢迎讨论……
该帖已经被评为新手帖
|
|
---|---|
作者 | 正文 |
发表时间:2010-08-04
最后修改:2010-08-04
【来源】:http://topic.csdn.net/u/20100802/13/17463a72-9207-424a-94e8-7ef70d98f6ae.html?78349 【题目】 1.String a = new String("1"+"2")共建了几个对象 我相信,绝对是个经典兼考倒一堆人的题目。经典,Java的面、笔试上都会有,可又总是有人会犯晕。呵呵! 看构造器里面("1"+"2"),这个是在编译期就已经做了处理,即代表一个字符串:"12"。 当使用new的方法创建字符串时,注意这个”new“,就表示直接开辟了内存空间,VM在对其对象的值”12“;然后再把值放到VM的常量池中,并引用其本身。所以就创建了两个对象,看反编译后的代码: String a = new String("1"+"2");
System.out.println(a);
反编译后的:
Code:
0: new #2; //class java/lang/String 在堆上创建了一个对象
3: dup
4: ldc #3; //String 12最终的字符串值
在API中对使用new创建String的建议 : 初始化一个新创建的 String 对象,使其表示一个与参数相同的字符序列; 换句话说,新创建的字符串是该参数字符串的副本。 既然写到这了,就扩展开来,说说其他的情况,先说一个与题目中类似的,唯一区别是,不使用new创建 看代码: String str1 = "ABC" + "CD";
String str2 = new String("ABC" + "CD");
再看其反编译后的: Code:
0: ldc #2; //String ABCCD
2: astore_1
3: new #3; //class java/lang/String
6: dup
7: ldc #2; //String ABCCD
对比一下,我想大伙都能看出了两者的区别,首先是,直接定义字符串常量,VM不会自动调用new创建,而是在VM维护的常量池中先查找,如果发现,直接返回一个对其的引用,若不存在,把其加入,再返回一个对其的引用。 此外,再总结一下与String 有关的三种加法:
看看: Code:
START-第一种情况String str1 = "ABC" + "CD":
0: ldc #2; //String ABCCD
2: astore_1
END-第一种情况
START-第二种情况:String str2 = null;str2 += "ABC";str2 += "CDS";str2 += "DSS";
3: aconst_null
4: astore_2
5: new #3; //class java/lang/StringBuilder: 创建了一个StringBuilder对象
8: dup
9: invokespecial #4;
12: aload_2
13: invokevirtual #5;
16: ldc #6; //String ABC : 追加第一个字符串"ABC",调用了append方法,下看
18: invokevirtual #5; //Method append:
21: invokevirtual #7; //Method toString:
24: astore_2
25: new #3; //class java/lang/StringBuilder: 再创建了一个StringBuilder对象
28: dup
29: invokespecial #4;
32: aload_2
33: invokevirtual #5; 先追加前面的字符串 "ABC"
36: ldc #8; //String CDS : 追加第二个字符串"CDS",调用了append方法,下看
38: invokevirtual #5; //Method java/lang/StringBuilder.append:
41: invokevirtual #7; //Method java/lang/StringBuilder.toString:
44: astore_2
45: new #3; //class java/lang/StringBuilder:再创建了一个StringBuilder对象
48: dup
49: invokespecial #4;
52: aload_2
53: invokevirtual #5; //Method java/lang/StringBuilder.append: 先追加前面的字符串"ABC CDS"
56: ldc #9; //String DSS:追加第三个字符串"CDS",调用了append方法,下看
58: invokevirtual #5; //Method java/lang/StringBuilder.append:
61: invokevirtual #7; //Method java/lang/StringBuilder.toString:
END-第二种情况
START-第三种情况
64: astore_2
65: new #10; //class java/lang/StringBuffer:创建StringBuffer对象
68: dup
69: invokespecial #11; //Method java/lang/StringBuffer."<init>":()V
72: astore_3
73: aload_3
74: ldc #6; //String ABC : 调用append方法
76: invokevirtual #12; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
79: pop
80: aload_3
81: ldc #13; //String DDD:调用append方法
83: invokevirtual #12; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
86: pop
END-第三种情况
小结一把:在JVM中,出现了第二种情况的,会默认创建一个StringBuilder对象来对字符串进行追加,注意,每一个"+="操作符,就会创建一个新的StringBuilder对象。这里就会造成挺大的内存浪费。 而用StringBuffer/StringBuilder这个对象来实现追加,在没调用其toString方法时,其对象还是可变的,一旦调用了toString的方法,就变成了不可变的String。
这里有某位仁兄( charles_wang8888)的解答,个人感觉其答得好: 第一题,创建了2个对象,由于编译器会吧等号右边的进行合并,所以他在常量区会建立一个 "12’的字符串,然后,在堆上创建一个对象叫a . 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-08-04
"12"的时候就已经在常量区了
|
|
返回顶楼 | |
发表时间:2010-08-04
跟便便器有关的怎么能当问题。。。 不是扯淡么,请问在java 1.2年代有几个对象啊
|
|
返回顶楼 | |
发表时间:2010-08-04
ray_linn 写道 跟便便器有关的怎么能当问题。。。 不是扯淡么,请问在java 1.2年代有几个对象啊
这位兄台真风趣,难道你现在用1.2编码啊!既然是曾经的了,又何必在这里讨论呢! |
|
返回顶楼 | |
发表时间:2010-08-04
daqing15 写道 String a = new String("1"+"2"); System.out.println(a); 反编译后的: Code: 0: new #2; //class java/lang/String 在堆上创建了一个对象 3: dup 4: ldc #3; //String 12最终的字符串值 楼主大大好忽悠哦! public class Bugmenot { public static void main(String[] args) { String a = new String("1" + "2"); System.out.println(a); } } 编译结果明明是 Compiled from "Bugmenot.java" public class Bugmenot extends java.lang.Object SourceFile: "Bugmenot.java" minor version: 0 major version: 50 Constant pool: const #1 = Method #8.#17; // java/lang/Object."<init>":()V const #2 = class #18; // java/lang/String const #3 = String #19; // 12 const #4 = Method #2.#20; // java/lang/String."<init>":(Ljava/lang/String;)V const #5 = Field #21.#22; // java/lang/System.out:Ljava/io/PrintStream; const #6 = Method #23.#24; // java/io/PrintStream.println:(Ljava/lang/String;)V const #7 = class #25; // Bugmenot const #8 = class #26; // java/lang/Object const #9 = Asciz <init>; const #10 = Asciz ()V; const #11 = Asciz Code; const #12 = Asciz LineNumberTable; const #13 = Asciz main; const #14 = Asciz ([Ljava/lang/String;)V; const #15 = Asciz SourceFile; const #16 = Asciz Bugmenot.java; const #17 = NameAndType #9:#10;// "<init>":()V const #18 = Asciz java/lang/String; const #19 = Asciz 12; const #20 = NameAndType #9:#27;// "<init>":(Ljava/lang/String;)V const #21 = class #28; // java/lang/System const #22 = NameAndType #29:#30;// out:Ljava/io/PrintStream; const #23 = class #31; // java/io/PrintStream const #24 = NameAndType #32:#27;// println:(Ljava/lang/String;)V const #25 = Asciz Bugmenot; const #26 = Asciz java/lang/Object; const #27 = Asciz (Ljava/lang/String;)V; const #28 = Asciz java/lang/System; const #29 = Asciz out; const #30 = Asciz Ljava/io/PrintStream;; const #31 = Asciz java/io/PrintStream; const #32 = Asciz println; { public Bugmenot(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 public static void main(java.lang.String[]); Code: Stack=3, Locals=2, Args_size=1 0: new #2; //class java/lang/String 3: dup 4: ldc #3; //String 12 6: invokespecial #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V 9: astore_1 10: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream; 13: aload_1 14: invokevirtual #6; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 17: return LineNumberTable: line 3: 0 line 4: 10 line 5: 17 } main的行号表说, LineNumberTable: line 3: 0 line 4: 10 line 5: 17 源码行号为3的Java代码在main的字节码里是 0: new #2; //class java/lang/String 3: dup 4: ldc #3; //String 12 6: invokespecial #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V 9: astore_1 ldc #3把String构造器的参数压到栈上而已,没创建新对象。 大大把代码从中间截断来忽悠人啊 |
|
返回顶楼 | |
发表时间:2010-08-04
bugmenot 写道 daqing15 写道 String a = new String("1"+"2");
System.out.println(a); 反编译后的: Code: 0: new #2; //class java/lang/String 在堆上创建了一个对象 3: dup 4: ldc #3; //String 12最终的字符串值 楼主大大好忽悠哦! public class Bugmenot { public static void main(String[] args) { String a = new String("1" + "2"); System.out.println(a); } } 编译结果明明是 Compiled from "Bugmenot.java" public class Bugmenot extends java.lang.Object SourceFile: "Bugmenot.java" minor version: 0 major version: 50 Constant pool: const #1 = Method #8.#17; // java/lang/Object."<init>":()V const #2 = class #18; // java/lang/String const #3 = String #19; // 12 const #4 = Method #2.#20; // java/lang/String."<init>":(Ljava/lang/String;)V const #5 = Field #21.#22; // java/lang/System.out:Ljava/io/PrintStream; const #6 = Method #23.#24; // java/io/PrintStream.println:(Ljava/lang/String;)V const #7 = class #25; // Bugmenot const #8 = class #26; // java/lang/Object const #9 = Asciz <init>; const #10 = Asciz ()V; const #11 = Asciz Code; const #12 = Asciz LineNumberTable; const #13 = Asciz main; const #14 = Asciz ([Ljava/lang/String;)V; const #15 = Asciz SourceFile; const #16 = Asciz Bugmenot.java; const #17 = NameAndType #9:#10;// "<init>":()V const #18 = Asciz java/lang/String; const #19 = Asciz 12; const #20 = NameAndType #9:#27;// "<init>":(Ljava/lang/String;)V const #21 = class #28; // java/lang/System const #22 = NameAndType #29:#30;// out:Ljava/io/PrintStream; const #23 = class #31; // java/io/PrintStream const #24 = NameAndType #32:#27;// println:(Ljava/lang/String;)V const #25 = Asciz Bugmenot; const #26 = Asciz java/lang/Object; const #27 = Asciz (Ljava/lang/String;)V; const #28 = Asciz java/lang/System; const #29 = Asciz out; const #30 = Asciz Ljava/io/PrintStream;; const #31 = Asciz java/io/PrintStream; const #32 = Asciz println; { public Bugmenot(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 public static void main(java.lang.String[]); Code: Stack=3, Locals=2, Args_size=1 0: new #2; //class java/lang/String 3: dup 4: ldc #3; //String 12 6: invokespecial #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V 9: astore_1 10: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream; 13: aload_1 14: invokevirtual #6; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 17: return LineNumberTable: line 3: 0 line 4: 10 line 5: 17 } main的行号表说, LineNumberTable: line 3: 0 line 4: 10 line 5: 17 源码行号为3的Java代码在main的字节码里是 0: new #2; //class java/lang/String 3: dup 4: ldc #3; //String 12 6: invokespecial #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V 9: astore_1 ldc #3把String构造器的参数压到栈上而已,没创建新对象。 大大把代码从中间截断来忽悠人啊 不知道是不是我使用的命令有问题,我编译出来并没有你如此之多的结果,呵呵!而且我只是截取了main里面的code,并没有全贴上去,而是去掉了一些不想关的一些代码(可能这个过程有误)我使用的命令是(Javap -c)!不知如此是否有误?请教了!等我检查过我自己当时所编译的代码再修改! 欢迎拍砖哈哈哈! |
|
返回顶楼 | |
发表时间:2010-08-05
daqing15 写道 不知道是不是我使用的命令有问题,我编译出来并没有你如此之多的结果,呵呵!而且我只是截取了main里面的code,并没有全贴上去,而是去掉了一些不想关的一些代码(可能这个过程有误)我使用的命令是(Javap -c)!不知如此是否有误?请教了!等我检查过我自己当时所编译的代码再修改! 欢迎拍砖哈哈哈! 楼主大大请用javap -verbose 大大的确截取了main的code,但把一个表达式对应的字节码从中间给截断了。 new String("12") 这个表达式是: 0: new #2; //class java/lang/String 3: dup 4: ldc #3; //String 12 6: invokespecial #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V 字节码的new只创建了一个未初始化的对象,要到invokespecial指令调用构造器后才算是构造完成。"12"是这个新建对象的构造器的参数,来源是常量池,是该类被加载的时候就创建好的。 String a = 这个变量定义是: 9: astore_1 System.out 10: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream; a 13: aload_1 println调用 14: invokevirtual #6; //Method java/io/PrintStream.println:(Ljava/lang/String;)V |
|
返回顶楼 | |
发表时间:2010-08-05
daqing15 写道 ray_linn 写道 跟便便器有关的怎么能当问题。。。 不是扯淡么,请问在java 1.2年代有几个对象啊
这位兄台真风趣,难道你现在用1.2编码啊!既然是曾经的了,又何必在这里讨论呢! 它说的并不是没有道理,不同版本的编译器优化不同。 |
|
返回顶楼 | |
发表时间:2010-08-05
bugmenot 写道 daqing15 写道 不知道是不是我使用的命令有问题,我编译出来并没有你如此之多的结果,呵呵!而且我只是截取了main里面的code,并没有全贴上去,而是去掉了一些不想关的一些代码(可能这个过程有误)我使用的命令是(Javap -c)!不知如此是否有误?请教了!等我检查过我自己当时所编译的代码再修改!
欢迎拍砖哈哈哈! 楼主大大请用javap -verbose 大大的确截取了main的code,但把一个表达式对应的字节码从中间给截断了。 new String("12") 这个表达式是: 0: new #2; //class java/lang/String 3: dup 4: ldc #3; //String 12 6: invokespecial #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V 字节码的new只创建了一个未初始化的对象,要到invokespecial指令调用构造器后才算是构造完成。"12"是这个新建对象的构造器的参数,来源是常量池,是该类被加载的时候就创建好的。 String a = 这个变量定义是: 9: astore_1 System.out 10: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream; a 13: aload_1 println调用 14: invokevirtual #6; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 呵呵,小生受教啦!用过那个java -verbose命令了,在main方法里面生成的应该是一样的吧!这次献丑咯,看来要下点功夫去熟悉一些VM 字节码!欢迎拍砖!!! |
|
返回顶楼 | |
发表时间:2010-08-05
最后修改:2010-08-05
daqing15 写道 ray_linn 写道 跟便便器有关的怎么能当问题。。。 不是扯淡么,请问在java 1.2年代有几个对象啊
这位兄台真风趣,难道你现在用1.2编码啊!既然是曾经的了,又何必在这里讨论呢! 请问我信口回答是3个,你如果是那个傻蛋考官,评什么来判定我错???JRE也不是就1.6一个版本呢,也不是只有Sun一家。 |
|
返回顶楼 | |