String a = new String(“abc”); 创建了几个对象?String a = “abc”; 呢?
答案:String a = new String(“abc”); 创建了1个或2个对象;String a = “abc”; 创建了0个或1个都对象
String a = new String(“abc”); 创建过程
- 首先在堆中创建一个实例对象
new String
, 并让a引用指向该对象。(创建第1个对象) - JVM拿字面量
"abc"
去字符串常量池试图获取其对应String对象的引用。 - 若存在,则让堆中创建好的实例对象
new String
引用字符串常量池中"abc"
。(只创建1个对象的情况) - 若不存在,则在堆中创建了一个
"abc"
的String对象,并将其引用保存到字符串常量池中,然后让实例对象new String
引用字符串常量池中"abc"
(创建2个对象的情况)
String a = “abc”; 创建过程
- 首先JVM会在字符串常量池中查找是否存在内容为"abc"字符串对应String对象的引用。
- 若不存在,则在堆中创建了一个
"abc"
的String对象,并将其引用保存到字符串常量池中。(创建1个对象的情况) - 若存在,则直接让a引用字符串常量池中
"abc"
。(创建0个对象的情况)
扩展:String a = “abc” + “d”; 创建了几个对象?
因为在编译期间,应用了编译器优化中一种被称为常量折叠(Constant Folding)的技术,会将编译期常量的加减乘除的运算过程在编译过程中折叠。编译器通过语法分析,会将常量表达式计算求值,并用求出的值来替换表达式,而不必等到运行期间再进行运算处理,从而在运行期间节省处理器资源。
而上边提到的编译期常量的特点就是它的值在编译期就可以确定,并且需要完整满足下面的要求,才可能是一个编译期常量:
- 被声明为
final
- 基本类型或者字符串类型
- 声明时就已经初始化
- 使用常量表达式进行初始化
所以上面的语句在运行期就等同于String a = “abcd”; 故答案是创建了0个或1个都对象。
比较字符串
package com.fastech;
public class StringTest {
public static void main(String[] args) {
String a = "abc";
String b = "abc";
String c = "ab" + "c";
String d = new String("abc");
String e = new String("abc");
System.out.println(a == b);
System.out.println(a == c);
System.out.println(a == d);
System.out.println(d == e);
System.out.println(a.equals(d));
System.out.println(d.equals(e));
}
}
结果:
true
true
false
false
true
true
== 运算符
- 如果 == 比较的是基本数据类型,那么比较的是两个基本数据类型的值是否相等;
- 如果 == 是比较的两个对象,那么比较的是两个对象的引用,也就是两个对象是否为同一个对象,并不是比较的对象的内容;
通过之前的几个问题可知变量a、b、c的引用地址相同。故a == b和a == c的结果为true。而new String(“abc”) 无论字符串常量池中是否存在“abc”,都会在堆中生成一个新的对象。d和e的引用指向各自堆的地址,堆引用才指向常量池。所以a、d、e三者不相等。
equals方法
- equals方法主要用于两个对象之间,检测一个对象是否等于另一个对象;
- equals方法在Object的代码其实也是 == 的关系运算符。
public boolean equals(Object obj) {
return (this == obj);
}
String类重写了Object类的equals方法
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
可以看到String类的equals方法先比较两个对象的引用地址,此时运用了 == 的关系运算符。如果两个对象的引用地址不同,会比较字符串中的每个字符是否相同,全部相同则返回true,否则返回false。故a、b、c、d、e这几个变量通过equals方法比较都相等