“判断两个事物是否相等”,是编程中最多见的操做之一,在Java中,判断是否相等有两种方法,一种是使用“==”判断符,另外一种是使用“equals()”方法,你是否曾因混用两者致使难以想象的bug?本篇文章将带你深刻两者背后的判断原理。java
"=="相等判断符用于比较基本数据类型和引用类型数据。 当比较基本数据类型的时候比较的是数值,当比较引用类型数据时比较的是引用(指针)。程序员
基本数据类型指的是Java中的八大数据类型:byte,short,int,long,float,double,char,boolean编程
这八大基本数据类型有个共同的特色是它们在内存中是有具体值的, 好比说一个 int 类型的数据"2",它在8位数据总线的机器上保存形式为 0000 0010。(8位机器是假设的)数组
当使用 == 比较两个基本数据类型的时候, 就是在比较它们各自在内存中的值。大数据
为了照顾到要刨根问底的同窗,再补充一下两个数值是怎么比较的:cpu 在比较的时候会将两个值做差,而后查看标志寄存器。标志寄存器存放的是运算的结果,里面有一个是否为0的标志位,若是该位为1,证实两者之差为0,两者相等。this
引用数据类型在字面上也是很好理解的, 它就是一个引用, 指向堆内存中一个具体的对象。spa
好比说Student stu = new Student();
这里的 stu 就是一个引用,它指向的是当前 new 出来的 Student 对象. 当咱们想要操做这个 Student 对象时, 只须要操做引用便可, 好比说int age = stu.getAge();
。操作系统
因此用"=="判断两个引用数据类型是否相等的时候,其实是在判断两个引用是否指向同一个对象。设计
看下面的示例:指针
public static void main(String[] args) {
String s1 = "hello"; //s1指向字符串常量池中的"hello"字符串对象
String s2 = "hello"; //s2也指向字符串常量池中的"hello"字符串对象
System.out.println(s1 == s2); //true
String s3 = new String("hello"); //s3指向的是堆内存中的字符串对象
System.out.println(s1 == s3); //false
}
复制代码
从上面的例子能够看到,因为引用"s1"和"s2"指向的都是常量池中的"hello"字符串,因此返回true。(后面我会发布一篇详细讲述Java字符串的文章,涉及字符串初始化和字符串常量池等知识)
而"s3"指向的是新建立字符串对象,由于只要动用了new
关键字, 就会在堆内存建立一个新的对象。
也就是说 s1 和 s3 指向的是不一样的字符串对象,因此返回false。
equals()和 == 有着本质的区别,== 能够看做是对“操做系统比较数据手段”的封装,而equals()则是每一个对象自带的比较方法,它是Java自定义的比较规则。
equals()和 == 的本质区别更通俗的说法是:==的比较规则是定死的,就是比较两个数据的值。
而 equals() 的比较规则是不固定的,能够由用户本身定义。
看下面的例子:
public static void main(String[] args) {
String s1 = "hello";
String s3 = new String("hello");
System.out.println(s1.equals(s3)); //true
}
复制代码
回想前面的案例:用 == 比较的时候, 上面 s1 和 s3 比较出的结果为false。而当用 equals() 比较的时候,得出的结果为 true。
想知道缘由咱们还得看源码,下面是 String 类中的 equals() 方法的源码。
public boolean equals(Object anObject) {
if (this == anObject) { //先比较两个字符串的引用是否相等(是否指向同一个对象), 是直接返回true
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。
若是不是指向同一个对象,则把两个字符串中的字符挨个进行比较。因为 s1 和 s3 字符串都是 "hello",是能够匹配成功的,因此最终返回 true。
经过上面的讲解,相信你已经知道 == 和 equals() 的区别了:一个的比较规则是定死的,一个是能够由编程人员本身定义的。
但是为何会有 equals() 方法, 并且还能够被自由定制呢?
这个问题要落到Java语言的核心 —— 面向对象思想了。
Java 不一样于面向过程的C语言,Java是一款面向对象的高级语言。若是是面向过程编程,直接操做内存上存储的数据的话,用 == 所定义的规则来判断两个数据是否相等已经足够了。
而Java中万物皆对象,咱们常常要面临的问题是这两个对象是否相等,而不是这两串二进制数是否相等,仅有 == 是彻底不够用的。
因为Java程序员们会建立各类知足它们业务需求的对象,系统没法提早知道两个对象在什么条件下算相等,Java干脆把判断对象是否相等的权力交给编程人员。
具体的措施是:全部的类都必须继承 Object 类,而 Object 类中写有equals()方法。编程人员能够经过重写 equals() 方法来实现本身的比较策略,也能够不重写,使用Object类的equals()比较策略。
//Object类中的equals()方法源码
public boolean equals(Object obj) {
return (this == obj);
}
复制代码
从 Object 类的 equals() 源码能够看到,若是编程人员没有显示地重写 equals() 方法,则默认比较两个引用是否指向同一个对象。
补充: 关于基本数据类型包装类的比较
因为 Java 中万物皆对象,就连基本数据类型也有其对应的包装类,那么它们对应的比较策略是什么呢?
public static void main(String[] args) {
int a = 3;
Integer b = new Integer(3);
System.out.println(b.equals(a)); //true, 自动装箱
}
复制代码
从上面的代码能够看到尽管两个引用不一样, 可是输出的结果仍为 true, 证实 Integer 包装类重写了 equals() 方法,追踪其源码:
//Integer类中的equals方法
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
复制代码
从源码看到,基本类型包装类在重写equals()后,比较的仍是基本数据类型的值。
经过探索 == 和 equals() 的区别,咱们摸清楚了两者别后的比较策略,同时也对 Java 中 equals() 方法的设计进行了思考,相信你们在从此的 Java 编程实战中不会再为相等判断而烦恼了。