对于这个问题,我们得了解基本类型和引用类型在内存中的结构,具体如下:
引用对象句柄访问:
引用对象直接指针访问:
可以看出,无论哪种方式访问基本类型和引用类型的实例数据,基本类型的内存上是保存值,而引用类型的内存上是保存指针(地址)。
== 可以理解为比较图中栈内存的数据。所以基本类型就是比较值,引用类型就是比较地址。
equals是个方法,只有引用类型才能调用,基础类型如int等是无法调用的。该方法在Object中就存在,实际就是调用 == 来比较,代码如下:
若equals不重写的话,效果等同于==,只不过equals只能被引用类型调用,==可以被所有类型数据调用。
另外,==判断是有特殊情况的,基础类型的包装类和String就有些情况就不是对比地址了,虽然它们是引用类型。具体比较如下图:
这里可以看到 包装类比如 Integer不用new声明,==判断就是值判断,因为JVM不会创建Integer对象,会直接在常量池创建变量;若用new 声明,则==判断是引用判断,因为JVM会创建Integer对象,声明的变量存储的是Integer实例在堆中的地址。
int i = 1和Integer j = 1的 i和j都是在常量池中保存值1,所以两个相等。而Integer h = new Integer(1)会在堆中新建Integer(1)对象,h在常量池中保存Integer(1)对象的地址,所以j==h进行常量池数据比较为false。
String不用new声明,首先会在常量池中找字符串值,若有则该变量保存的是这个字符串值的地址,若没有找到字符串值,则在常量池中声明新字符串,此时该变量保存的是常量池中新字符串的地址。若用new声明,则直接新建字符串对象,变量为字符串在堆中的地址。
String x="a"会在常量池中新建字符串"a",然后x保存"a"的地址。String y="a"会因为常量池中存在"a"(x声明的"a"),所以y也直接保存"a"的地址,所以x==y是true。而String z = "ab"和String n = new String("ab")分别会在常量池和堆中新建字符串"ab"并保存它们的地址,这里由于地址一个指向常量池,一个指向堆,显然z和n不等。另外实测 z=="a"+"b"和z!=x+"b",直接固定字符串相加则是常量池查找新建字符串,而有变量参与相加则是堆新建字符串。