首先,让我们来看一下下边这两种定义String的区别。 Stringstr1 ='abc';Stringstr2 =newString('abcd'); 那么,它们两个在内存中是怎么分配的呢。 str1和str2是两个对象的引用,存放在Java栈中。第一条语句没有在堆中分配内存,而是将“abc”保存在常量池中。对于第二条语句,同样会在常量池中保存一个“abcd”的字符串,当new时,会拷贝一份该字符串存放到堆中,于是str2指向了堆中的那个“abcd”字符串。同理,如果我们再定义一句String str3 = “abc”;那么,栈中的引用str3会指向跟str1相同的那个常量池中的”abc”。 接下来,让我们通过几段代码来分析一下String。 1、 publicstaticvoidmain(String args) { String str1 ='abc'; String str2 ='abc'; System.out.println(str1 == str2);// true} 分析:这里结果为true。当程序加载String str1 = “abc”; 这句代码时,会去常量池中检查有没有”abc”这个对象,如果没有,则在常量池中创建一个”abc”对象。同样,当加载String str2 = “abc”; 这句代码时,因为常量池中已经存在了”abc”这个对象,则str2直接指向常量池中的”abc”对象,它们两个指向的是同一个常量池中的对象,所以输出为true。 2、 publicstaticvoidmain(String args) { String str1 ='abc'; String str2 =newString('abc'); String str3 =newString('abc'); System.out.println(str1 == str2);// falseSystem.out.println(str2 == str3);// false} 分析:当程序加载String str1 = “abc”;这句的时候,在常量池里会创建”abc”这个对象,str1指向常量池中的”abc”对象。当程序加载String str2 = new String(“abc”);这句的时候,因为常量池里已经有了”abc”这个对象,所以在常量池中不新建对象,但是在java堆中会新建一个”abc”对象,str2指向Java堆中新建的”abc”对象。当程序加载String str3 = new String(“abc”);这句的时候,同样因为常量池中已经有”abc”这个对象,所以不在常量池中新建”abc”对象,但是也会在Java堆中新建一个”abc”对象,str3指向Java堆中新建的这个”abc”对象,注意,这个”abc”对象跟str2指向的”abc”对象不是同一个,str1、str2和str3指向的都是不同的对象,所以输出结果都为false。 3、 publicstaticvoidmain(String args) { String str1 ='hello'; System.out.println(str1 =='he'+'llo');// true} 分析:这里输出为true。”he” + “llo” 两个双引号形式的字符串常量相加, 在编译的时候直接会被转为一个字符串常量”hello”并保存在常量池中。 4、 publicstaticvoidmain(String args) { String str1 ='hello'; String str2 ='he'; System.out.println(str1 == str2 +'llo');// false} 分析:这里输出为false。str2 + “llo” 字符串变量和字符串常量相加的时候,内部是使用StringBuilder类的append方法和toString方法来实现的。而StringBuilder类toString方法返回的字符串是通过构造函数创建的。所以生成的”hello”对象是在JAVA堆里。 5、 publicstaticvoidmain(String args) { String str1 ='hello'; String str2 =newString('hello'); System.out.println(str1 == str2.intern);// true} 分析:这里输出为true。String的intern方法是扩充常量池的一个方法,当String实例str2调用intern方法时,Java会在常量池中查找是否有相同Unicode的字符串常量对象,如果有,则返回其引用,如果没有,则在常量池中增加一个Unicode等于str2的字符串常量对象并返回它的引用。这里str2.intern方法返回的是指向常量池中”hello”的引用,所以输出为true。 6、 publicstaticvoidmain(String args) { String str1 ='hello'; str1.concat(' world'); System.out.println(str1);// hello} 分析:这里输出为”hello”。因为String是不可变(final)的,所以输出为”hello”。如果想可变,可以使用StringBuilder或StringBuffer类,或者定义一个新的变量接收一下方法的返回值String str2 = str1.concat(” world”);。 通过上边几个例子,我们应该能对Java中的字符串String有了一个很好的了解。下边来介绍一些在笔试面试中,经常遇到的问题。 1、String s = new String(“xyz”); 创建了几个String Object? 答案是1个或2个。如果之前在常量池中已经创建过'xyz'对象的话,那么只会在Java堆中创建一个'xyz'对象, 如果常量池中没有的话,也会在常量池中创建一个'xyz'对象,所以说是1个或2个。 2、是否可以继承String类? String类是final类故不可以被继承。 3、数组有没有length这个方法? String有没有length这个方法? 数组没有length这个方法,有length属性。String有length这个方法。 4、Java中判断字符串值是否相等是用”==”还是”equals”? Java中字符串的'equals'方法是判断两个字符串的值是否相等,'=='是判断两个字符串的地址是否相同。 5、Java的String,StringBuffer,StringBuilder有什么区别? String是不可变(final)类,每次在String对象上的操作都会生成一个新的对象; StringBuffer和StringBuilder是可变的,它允许在原来对象上进行操作,而不用每次增加对象; StringBuffer是线程安全的,但效率较低,而StringBuilder则不是线程安全的,效率最高。 6、下面这段代码的运行结果是什么? 代码如下:publicstaticvoidmain(String args) { String strs =newString[10]; System.out.println(strs[1]); System.out.println(strs[10]); } 解答: 第一个打印输出为null,因为字符串默认值为null; 第二个打印会报ArrayIndexOutOfBoundsException,因为数组的下标是从0开始的。 7、下面这段代码的运行结果是什么? 代码如下:publicstaticvoidmain(String args) { String str; System.out.println(str); } 解答: 编译器中System.out.println(str);这一行会报错,因为局部变量str必须要初始化才能输出。
|
|