题目: 阅读下面代码段,给出以下代码的输出结果。
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】