首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > JAVA > J2SE开发 >

String 应用new 方法时池中到底有没有生成对象

2013-04-21 
String 使用new 方法时池中到底有没有生成对象老问题 String s new String(abc)池中到底有没有 “abc”

String 使用new 方法时池中到底有没有生成对象
老问题 
String s =new String("abc");

池中到底有没有 “abc”

比如

1 String s = new String("abc");
2 String a = "abc";

结果肯定为false;

有一种答案认为  1会在 堆中创建1个对象 然后去看池 发现池中没有“abc” 然后将“abc”添加到池中
所以运行2 的时候 先去搜索池 发现池中有“abc” 所以返回地址 1的引用指向堆中的对象 2中的引用指向池中的对象

但是 网上搜资料有

原理2:
Java中,只要使用new关键字来创建对象,则一定会(在堆区或栈区)创建一个新的对象。(jvm会马上在heap中创建一个String对象,然后将该对象的引用返回给用户。使用这种方法创建对象时,jvm是不会主动把该对象放到strings pool里面的,除非程序调用 String的intern方法。)
//例子1

String str1 = new String("abc");
                  //jvm 在堆上创建一个String对象   

String str2 = "abc";
//jvm 在strings pool中找不到值为“abc”的字符串,因此    
//在堆上创建一个String对象,并将该对象的引用加入至strings pool中    
//此时堆上有两个String对象 

//例子2

String str11 = new String("abc"); //jvm 在堆上创建一个String对象    
   
str11 = str11.intern();
//程序显式将str1放到strings pool中,intern运行过程是这样的:首先查看strings pool    
//有没“abc”对象的引用,没有,则在堆中新建一个对象,然后将新对象的引用加入至
strings pool中。执行完该语句后,str1原来指向的String对象已经成为垃圾对象了,随时会被GC收集。      
//此时,jvm发现strings pool中已有“abc”对象了,因为“abc”equels “abc”    
//因此直接返回str1指向的对象给str2,也就是说str2和str1引用着同一个对象,    
//此时,堆上的有效对象只有一个。    
String str22 = "abc";    
   
 if(str11 == str22){    
         System.out.println("str11 == str22");    
 }else{    
         System.out.println("str11 != str22");    
 }    
  //打印结果是 str11 == str22 

希望高手解答
另外 引用不叫对象 说什么生成3个 5个对象的就歇歇吧。。。

[解决办法]
果然是不朽的问题
通过反编译来看看吧
public class StringTest {
    public static void main(String[] args) {
        new String("abc");
    }
}
javac StringTest.java //编译
javap -verbose StringTest //反编译

反编译结果
D:\Test>javap -verbose StringTest
Compiled from "StringTest.java"
public class StringTest extends java.lang.Object
  SourceFile: "StringTest.java"
  minor version: 0
  major version: 50
  Constant pool:
const #1 = Method       #6.#15; //  java/lang/Object."<init>":()V
const #2 = class        #16;    //  java/lang/String
const #3 = String       #17;    //  abc
const #4 = Method       #2.#18; //  java/lang/String."<init>":(Ljava/lang/String
;)V
const #5 = class        #19;    //  StringTest


const #6 = class        #20;    //  java/lang/Object
const #7 = Asciz        <init>;
const #8 = Asciz        ()V;
const #9 = Asciz        Code;
const #10 = Asciz       LineNumberTable;
const #11 = Asciz       main;
const #12 = Asciz       ([Ljava/lang/String;)V;
const #13 = Asciz       SourceFile;
const #14 = Asciz       StringTest.java;
const #15 = NameAndType #7:#8;//  "<init>":()V
const #16 = Asciz       java/lang/String;
const #17 = Asciz       abc;
const #18 = NameAndType #7:#21;//  "<init>":(Ljava/lang/String;)V
const #19 = Asciz       StringTest;
const #20 = Asciz       java/lang/Object;
const #21 = Asciz       (Ljava/lang/String;)V;

{
public StringTest();
  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=1, Args_size=1
   0:   new     #2; //class java/lang/String
   3:   dup
   4:   ldc     #3; //String abc
   6:   invokespecial   #4; //Method java/lang/String."<init>":(Ljava/lang/Strin
g;)V
   9:   pop
   10:  return
  LineNumberTable:
   line 3: 0
   line 4: 10


}

通过反编译代码可以看出,常量池存在abc (红色地方)
再看代码执行蓝色地方
代码执行过程,先执行new指令,在堆中分配内存,并对象的内存地址压栈
再执行dup指令,把new出来的对象的地址复制一份压入栈顶
然后ldc指令,从常量池入口查找"abc"对象,并把它压入操作数栈
接着invokespecial,就是用ldc找到的"abc"对象初始化dup指令压栈的对象地址
pop,这里因为是没有变量引用对象,所以new时压栈的对象的内存地址出栈
最后return,程序结束

好了,来看看,到底有几个对象(注意是有几个,而不是生成几个),首先常量池中一个"abc",然后是堆中new出来的一个,所以是2个。
那么常量池的"abc"算不算是代码执行时生成的呢,有人说算,有人说不算,说算,是因为如果没有这条代码,常量池就不会存在"abc",也就说常量池就不会创建"abc",说不算,是因为"abc"在代码执行前就被创建了,也就是java被编译生成字节码时,"abc"就已经被编译为常量池的信息,所以类加载的时候,它就生成了。所以这个地方有争议。就看从哪个角度来看问题。


热点排行