在看题目之前,首先来看一张图片:
该图截自JDK API,相信大家已经看到我在图中用红色圈圈,圈出来的部分,对,String类是被定义为final的,是不可变类,本文不针对这点做过多阐述,有兴趣的可以参阅这篇文章:J2SE基础夯实系列之String字符串不可变的理解,不可变类,final关键字到底修饰了什么
下面看一些具体的问题:
1.String s1 = "ab";
String s2 = new String("ab");
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
这段代码的输出会是啥呢?
答案是:
false
true
这个问题首先要注意的是==与equals的区别
==是比较是s1与s2是否是同一个对象,上面的s1与s2不是同一个对象,s1的“ab”是在String pool中,而s2的“ab”是在堆中,二者并不是同一个对象,所以输出false
equals呢是比较s1与s2中的内容是否相同,显然二者相同,所以输出true
- 2.
- String s = new String("abc");
- String s1 = "abc";
- String s2 = new String("abc");
- System.out.println(s == s1);
- System.out.println(s == s2);
- System.out.println(s1 == s2);
第一句执行后内存中有两个对象,而不是一个(可以参考这篇文章:java中String s = new String("abc")创建了几个对象?!)。一个由new String("abc")中的"abc"在String Pool里生成一个值为"abc"的对象;第二个由new在堆里产生一个值为"abc"的对象,该对象完全是String Pool里的"abc"的一个拷贝。变量s最后指向堆中产生的"abc"对象;
第二句执行时,s1先去String Pool找是否有值为"abc"的对象,很显然在上一步中java已经在String Pool里生成一个"abc"对象了,所以s1直接指向String Pool中的这个"abc";
第三句中又有一个new,在java中凡遇到new时,都会在堆里产生一个新的对象。因此,该句执行后堆里又多了一个"abc"对象,这与执行第一句后生成的"abc"是不同的两个对象,s2最后指向这个新生成的对象。
因此,执行后面的打印语句的结果是三个false
3.
String str2 = new String("abc");
if(str1 == str2)
{
System.out.println("str1 == str2");
}
if(str1.equals(str2))
{
System.out.println("str1 equals str2");
}
String str3 = "abc";
String str4 = "abc";
if(str3 == str4)
{
System.out.println("str3 == str4");
}
if(str3.equals(str4))
{
System.out.println("str3 equals str4");
}
if(str1 == str4)
{
System.out.println("str1 equals str4");
}
if(str1.equals(str4))
{
System.out.println("str1 equals str4");
}
运行结果为:
str1 equals str2
str3 == str4
str3 equals str4
str1 equals str4
为什么会出现这样的结果。。。这需要引入一些概念,其实很类似C++ 或C#之类,差别只是小小的。。。
首先了解一个对象在内存中创建的区域:
1.heap(即堆区):它是负责创建对象的,所有的创建出来的对象都是放在堆区的。所有new操作的创建的实例都存储在这里,包括用new String 创建的String。
2.stack(即栈区):它是负责存放局部变量,和成员变量的。所有的成员变量和局部变 量都放在这个区内,然后他通过一个引用指向栈区的对象或data segment(静态代码)区的静态数据。
3.data segment(静态代码区):在这个区主要存放的是静态常量,和字符串常量(字符串池)。在类一开始被加载的时候此常量就被初始化放在这个区内,而且被全局所共享,所有的访问直接指向他即可。
4.code segment(代码区):它是存放代码的区,所有的执行代码都放在此区内。通过对象的调用指向此区。
4.String s = "a" + "b" + "c" + "d" + "e";问此语句共创建了几个对象
答案是:创建了一个对象
可以尝试运行这段程序
String s1 = "a";
String s2 = s1 + "b";
String s3 = "a" + "b";
System.out.println(s2 == "ab");
System.out.println(s3 == "ab");
String s = "a" + "b" + "c" + "d";
System.out.println(s == "abcd");
运行结果为:
- false
- true
- true
第一条语句打印的结果为false,第二条语句打印的结果为true,这说明javac编译可以对字符串常量直接相加的表达式进行优化,不必要等到运行期去进行加法运算处理,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果。题目中的代码被编译器在编译时优化后,相当于直接定义了一个”abcd”的字符串,所以,题目中的代码应该只创建了一个String对象。
写如下两行代码,String s = "a" + "b" + "c" + "d";
System.out.println(s == "abcd");最终打印的结果应该为true
在文章最后贴上这些类型题目的一个代码^_^:
public class Test{
public static void main(String[] args){
String s1 = "a";
String s2 = s1 +"b";
String s3 = "a" + "b";
System.out.println("s2 == \"ab\" " + (s2 == "ab"));
System.out.println("s3 == \"ab\" " + (s3 == "ab"));
String s4 = "ab";
String s5 = new String("ab");
String s6 = new String("ab");
System.out.println("s4 == s5 " + (s4 == s5));
System.out.println("s4.equals(s5) " + (s4.equals(s5)));
System.out.println("s4 == s3 " + (s4 == s3));
System.out.println("s2 == s5 " + (s2 == s5));
System.out.println("s5 == s6 " + (s5 == s6));
}
}
输出结果是:
本文详细解析了 Java 中 String 对象的创建过程及内存分配,并通过多个示例对比了使用 == 和 equals 方法的区别,有助于理解字符串常量池的工作原理。


214

被折叠的 条评论
为什么被折叠?



