您的位置:首页 > 其它

String对象内存分配和基本数据类型的默认值与初始化相关问题

2015-03-02 15:57 621 查看
题目: 阅读下面代码段,给出以下代码的输出结果。

public class TestFunction {

static int i;

public static void main(String[] args) {
String str1 = "abc";
String str = "abc";
String str2 = new String("abc");

System.out.println(str1 == str2);
System.out.println(str1.equals(str2));
System.out.println(str == str1);
System.out.println(i);
}
}


分析:

       从本题的内容可以直接明白,是考察我们对String对象的使用和int(基本数据类型)的默认值与初始化相关问题。更深入的分析,考察String对象内存分配的知识。

【转载使用,请注明出处:http://blog.csdn.net/mahoking
解答:

        针对对String对象的考察
,翻开String的源码,注释中有“Strings are constant; their values cannot be changed after they are created. String buffers support mutable strings.Because String objects are immutable they can be shared.”这句话总结归纳了String的一个重要特点:String是值不可变(immutable)的常量,是线程安全的(can
be shared)。

       接下来,String类使用了final修饰符,表明了String类的第二个特点:String类是不可继承的。下面是String类的成员变量定义,从类的实现上阐明了String值是不可变的(immutable)。

private final char value[];
private final int count;

        因此,我们看String类的concat方法。实现该方法第一步要做的肯定是扩大成员变量value的容量,扩容的方法重新定义一个大容量的字符数组buf。第二步就是把原来value中的字符copy到buf中来,再把需要concat的字符串值也copy到buf中来,这样子,buf中就包含了concat之后的字符串值。下面就是问题的关键了,如果value不是final的,直接让value指向buf,然后返回this,则大功告成,没有必要返回一个新的String对象。但是。。。可惜。。。由于value是final型的,所以无法指向新定义的大容量数组buf,那怎么办呢?“return
new String(0, count + otherLen, buf);”,这是String类concat实现方法的最后一条语句,重新new一个String对象返回。

       在此需要引入常量池的概念,常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。常量池还具备动态性,运行期间可以将新的常量放入池中,String类的intern()方法是这一特性的典型应用。

        本例中String的定义方法有一下两种:

 •直接定义: String str1 = "abc"; String str = "abc";

 •使用关键字new:String str2 = new String("abc");

       第一种方式直接定义过程:在程序编译期,编译程序先去字符串常量池检查,是否存在“abc”,如果不存在,则在常量池中开辟一个内存空间存放“abc”;如果存在的话,则不用重新开辟空间。然后在栈中开辟一块空间,命名为“str1”,存放的值为常量池中“abc”的内存地址。

       第二种方式通过关键字new定义过程:在程序编译期,编译程序先去字符串常量池检查,是否存在“abc”,如果不存在,则在常量池中开辟一个内存空间存放“abc”;如果存在的话,则不用重新开辟空间,保证常量池中只有一个“abc”常量,节省内存空间。然后在内存堆中开辟一块空间存放new出来的String实例,在栈中开辟一块空间,命名为“str”,存放的值为堆中String实例的内存地址,这个过程就是将引用str指向new出来的String实例。

       针对这两种的关系,有不同的猜想。本例提供一种理解较为合理的猜想:因为直接定义的字符串也可以调用String对象的各种方法,那么可以认为其实在常量池中创建的也是一个String实例(对象)。String str2 = new String("abc");先在编译期的时候在常量池创建了一个String实例,然后clone了一个String实例存储在堆中,引用str2指向堆中的这个实例。此时,池中的实例没有被引用。当接着执行String str = "abc";时,因为池中已经存在“abc”的实例对象,则str直接指向池中的实例对象;否则,在池中先创建一个实例对象,str再指向它。



 

        针对对int(基本数据类型)的默认值与初始化的考察, JVM将为类的instance和static变量赋上缺省值(默认值),包括数组array中的每一个元素--而不用再写初始化赋值语句。
切记:局部变量是没有缺省值的,必须手动初始化!

        这一缺省赋值过程是在对象的构造函数调用之前完成的,如果程序写了对instance和static变量的赋初值语句,且给的值就是JVM默认的值,那么无疑是画蛇添足,重复劳动了一遍。

【转载使用,请注明出处:http://blog.csdn.net/mahoking
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: