目录
一、“==”运算符
“==”运算符不论左右两边是引用类型还是基本类型,永远比较的都是左右两者的值是否相同,这个没有问题。
问题出在基本类型变量的值就是你赋值的值比如1、100、1000等等,而引用类型变量的值是一个内存地址。
1.八大基本类型
对于这8种基本数据类型的变量,变量直接存储的是值,这个值可以是整形、浮点型等任意八种基本类型。
2.引用类型
-
自定义引用类型: 引用类型的变量存储的是该实例在内存中的地址,打印的结果是一串十六进制数比如@15db9742。例如:
-
Java提供的引用类型: 但是我们使用Java为我们提供的引用类型(基本类型对应包装类、字符串、集合类等等)创建一个变量,打印结果并不是一个十六进制数呀??????这是因为打印的本质是调用toSting()方法,通过翻看JDK源码我们发现,这些类全部都重写了toString()方法。
特别的
先看两个例题。
Integer num1 = 400;
int num2 = 400;
System.out.println(num1 == num2);
Integer num1 = 100;
int num2 = 100;
Long num3 = 200l;
System.out.println(num3 == (num1 + num2));
结论:当一个基本数据类型与包装类进行==、+、—、*、/、运算时,会将包装类进行拆箱,再与基本数据类型进行运算。
所以答案是两个true。
二、Object中的equals方法
源码
因为java.lang.Object类中有定义equals方法,所以java中的所有类都会继承这个方法。
Object类中的equals方法:
public boolean equals(Object obj) {
//1.this是equals方法调用者;obj是参数
//2.此时比较的是这两个对象在堆内存中的地址是否相同
return (this == obj);
}
由此可以得出这样的结论:当一个类不重写equals方法时,因为Java中所有类都会默认继承Object类,此时使用equals方法和使用“==”运算符是等价的,因为equals方法就是用“= =”运算符实现的。
例一
class Animal {
String name;
Animal(String name) {
this.name = name;
}
}
public class Demo1 {
public static void main(String[] args) {
Animal a1 = new Animal("狗子");
Animal a2 = new Animal("狗子");
Animal a3 = a1;
System.out.println(a2 == a1);
System.out.println(a2.equals(a1));
System.out.println(a3.equals(a1));
}
}
结果:
false
false
true
这显然不是我们想要的结果。实际上,Java中提供的引用类型(八大基本类型对应包装类和字符串类型)都已经重写过equals方法了。
三、八种基本类型对应封装类中的equals方法
Integer类源码
属性
private final int value;
构造方法
public Integer(int value) {
this.value = value;
}
intValue()方法
public int intValue() {
return value;
}
主角equals()方法
public boolean equals(Object obj) {//这里有一个向上类型转换
//判断obj是否是Integer类型的对象
//是,执行if里面的表达式
//否,直接返回false
if (obj instanceof Integer) {
//1.先将obj强制类型转换为Integer类,
//再调用intValue()方法得到它的值
//2.此时“==”运算两边都是基本类型
//字面量相等就相等
return value == ((Integer)obj).intValue();
}
return false;
}
例一
Integer num1 = 100;
int num2 = 100;
//这里有一个自动装箱的过程,不明白请学习我的另一篇文章
System.out.println(num1.equals(num2));
结果:
true
例二
Integer num1 = 100;
long num2 = 100;
System.out.println(num1.equals(num2));
结果:
false
分析:
num2对应的封装类是Long类,在if (obj instanceof Integer)判断的时候得到false,不能进入if里。
其他类源码
其他类源码与Integer类同理
例如,Character类
public boolean equals(Object obj) {
if (obj instanceof Character) {
return value == ((Character)obj).charValue();
}
return false;
}
例如,Boolean类
public boolean equals(Object obj) {
if (obj instanceof Boolean) {
return value == ((Boolean)obj).booleanValue();
}
return false;
}
特别的是,Float类
public boolean equals(Object obj) {
return (obj instanceof Float)
&& (floatToIntBits(((Float)obj).value) == floatToIntBits(value));
}
如果一直深究的话,会发现调用了一个native方法,这里不做深究,知道其原理就行,Double 类同理。
四、String类
属性
//可知其实String的内部是用的char型数组来存储字符串
private final char value[];
equals方法
public boolean equals(Object anObject) {
//this是equals方法调用者;anObject是参数
//this和anObject都是引用类型,此时比较的是地址是否相同
if (this == anObject) {
return true;
}
//判断obj是否是String类型的对象
if (anObject instanceof String) {
//进行强制类型转换
String anotherString = (String)anObject;
//因为value是数组,可以用length得到元素个数
int n = value.length;
//两者长度一样
if (n == anotherString.value.length) {
//此处的功能一目了然就是把两个对象中的value属性的元素一个个进行比较
//只要有一个不相同,就返回false
//全部相同则返回true
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;
}
链接:
(1)自动装箱和拆箱源码分析JDK1.8(一)