关于“==”和“equals()”

本文深入解析Java中字符串比较及equals()方法的工作原理,包括==的作用、字符串实例化优化、equals()方法实现细节以及与==的区别。通过代码示例,清晰展示编译时优化对字符串比较的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题引入

请看下面的代码清单1

<span style="white-space:pre">	</span>public void test1(){
		String a = "a" + "b" + 1;
		String b = "ab1";
		System.out.println(a == b);
	}

上述这段代码来源自谢宇编著的书籍《Java特种兵》上册。


代码清单1中的输出是true


这是个考察Java基本功的问题,类似的问题还有很多,如清单2:

<span style="white-space:pre">	</span>//代码清单2
	public static String getA() { return "a"; }
	
	public void test2(){
		String a = "a";
		final String c = "a";
		
		String b = a + "b";
		String d = c + "b";
		String e = getA() + "b";
		
		String compare = "ab";
		System.out.println(b == compare);
		System.out.println(d == compare);
		System.out.println(e == compare);
	}

Output:

false
true
false

要理解这个问题,首先需要搞清楚这些:

1. 关于“==”是做什么的?

2. a和b在内存中是什么样的?

3. 编译时的优化方案


关于“==”

“==”是用于匹配内存单元上的内容,在Java语言中,当使用“==”匹配时,其实就是对比两个内存单元的内容是否一样。如果是原始类型byte、boolean、short、char、int、long、float、double,就是直接比较它们的值。如果是引用(Reference),比较的就是引用的值,“引用的值”可以被认为是对象的逻辑地址。如果两个引用发生“==”操作,就是比较相应的两个对象的地址值是否一样。

换一句话说,如果两个引用所保存的对象是同一个对象,则返回true,否则返回false。如果a、b两个引用都指向null,则也是返回true。


编译时的优化

在代码清单1中,a引用是通过“+”赋值的,b引用是通过直接赋值的,那么为什么a和b两个引用会指向同一个内存单元?这就是JVM的“编译时优化”。

当编译器在编译代码 String a = "a" + "b" + 1 时,会将其编译成 String a = "ab1" ,因为都是“常量”,编译器认为这3个常量叠加会得到固定的值,无需运行时再进行计算,所以就会这样优化。类似的还有 int i = 2 * 3 + 1,并不是在实际运行时再计算i的值,而是在编译时直接变成了i=7。


而在代码清单2中,b与compare比较时,由于compare是个常量,而b不是常量,原因是b = a + "b",a并不是一个常量,a中的值可以修改,因此编译器不会进行优化。

变量c有一个final修饰符,从定义上约束了c是不允许改变的,因此编译器会进行优化。

变量e的值来自一个方法,虽然方法内返回一个常量的引用,但是编译器并不会去看方法内部做了什么,所以编译器不会进行优化。


关于“equals()”

首先来看看Object中的equals()方法的实现:

public boolean equals(Object obj)
{
     //调用equal的对象的地址和参数对象的地址是否相等
     return (this == obj);
}

从源码中可以看出,Object类中equals()方法是使用“==”来匹配的,也就是说,如果不去重写equals()方法,那么默认的equals()操作就是对比对象的地址。

equals()方法的存在就是希望子类去重写这个方法的 ,而在String类中,就重写了equals()方法:

<span style="white-space:pre">	</span>public boolean equals(Object anObject) {
		// 如果是同一个对象
		if (this == anObject) {
			return true;
		}
		// 如果传递进来的参数是String类的实例
		if (anObject instanceof String) {
			String anotherString = (String) anObject;
			int n = count;// 字符串长度
			if (n == anotherString.count) // 如果长度相等就进行比较
			{
				char v1[] = value;// 取每一个位置的字符
				char v2[] = anotherString.value;
				int i = offset;
				int j = anotherString.offset;
				while (n-- != 0) // 对于每一位置逐一比较
				{
					if (v1[i++] != v2[j++])
						return false;
				}
				return true;
			}
		}
		return false;
	}

在JDK 1.6中,String.equals()的代码逻辑大致是这样的:

1. 判定传入的对象和当前对象是否为同一个对象,如果是就直接返回true;

2. 判定传入的对象类型是否为String,若不是则返回false;

3. 判定传入的String与当前String的长度是否一致,如不一致就返回false;

4. 循环对比两个字符串的char[]数组,逐个比较字符是否一致,如果不一致就直接返回false;

5. 循环结束都没有找到不匹配的,最后返回true。


参考资料:谢宇--《Java特种兵》


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值