Java比較問題詳細(xì)分析
Java中的比較問題是一個很基礎(chǔ)又很容易混淆的問題。今天就幾個容易出錯的點作一個比較詳細(xì)的歸納與整理,希望對大家的學(xué)習(xí)與面試有幫助。
一、==與equals()的區(qū)別
首先,我們需要知道==與equals()的區(qū)別,==號比較的一直是地址值,對于基本數(shù)據(jù)類型來說,==比較實際上就是變量數(shù)值是否相等,而對于引用數(shù)據(jù)類型,比較的則是地址值。這里特別需要注意的是String類型,很容易想當(dāng)然的使用==,很容易出錯。equals()方法是Object類里的方法,我們知道Java中一切類都會默認(rèn)繼承Object類,所以類對象都會有equals()方法。Object類中equals()方法如下圖所示:

由源碼可以看出,Object類里的equals()方法底層也是用的==,所以它比較的其實也是地址值。所以如果想用equals()方法去作其他比較,我們需要重寫equals()方法。
二、基本數(shù)據(jù)類型及其包裝類
我們都知道,byte、short、int、long、boolean、char、double、float這八個是基本數(shù)據(jù)類型,它們聲明的變量存放在棧內(nèi)存中。而它們對應(yīng)的包裝類型(Byte、Short、Integer、Long、Boolean、Character、Double)定義的變量則存在于堆內(nèi)存中。對于基本數(shù)據(jù)類型,它們的比較相對而言較為簡單,即判斷是否相等用==,比較大小用<、>、<=、>=即可。而對于包裝類型,卻有些不同。
首先對于判斷是否相等,看如下代碼的執(zhí)行結(jié)果:
package dailytest;
import org.junit.Test;
/**
* Java中的比較總結(jié)
* @author yrr
*/
public class JavaCompareTest {
/**
* Integer類型判斷是否相等
*/
@Test
public void test01() {
int n3 = 48;
System.out.println("--------使用new對象時,當(dāng)值在[-127,128]之間時---------");
Integer n7 = new Integer(48);
Integer n8 = new Integer(48);
System.out.println(n7 == n8); //false
System.out.println(n7 == n3); //true
System.out.println("--------直接賦值方式,當(dāng)值在[-128,127]之間時---------");
Integer n1 = 48;
Integer n2 = 48;
System.out.println(n3 == n1); //true
System.out.println(n1 == n2); //true
System.out.println(n1.equals(n2)); //true
System.out.println(n1.equals(n3)); //true
System.out.println(n1.intValue() == n2.intValue()); //true
System.out.println("--------直接賦值方式,當(dāng)值不在[-127,128]之間時---------");
Integer n4 = 128;
Integer n5 = 128;
int n6 = 128;
System.out.println(n4 == n5); //false
System.out.println(n4 == n6); //true
System.out.println(n4.equals(n5)); //true
System.out.println(n4.equals(n6)); //true
System.out.println(n4.intValue() == n5.intValue()); //true
//使用Integer.intValue()方法時需要注意驗證是否為null,防止出現(xiàn)NullPointException
}
/**
* Long類型判斷是否相等
*/
@Test
public void test02() {
//這里需要注意,使用long定義時,不需要加L或者l,而使用Long時必須加,否則會報錯
//建設(shè)都加上,以示區(qū)別
long n3 = 48L;
System.out.println("--------使用new對象時,當(dāng)值在[-127,128]之間時---------");
Long n7 = new Long(48);
Long n8 = new Long(48);
System.out.println(n7 == n8); //false
System.out.println(n7 == n3); //true
System.out.println("--------直接賦值方式,當(dāng)值在[-127,128]之間時---------");
Long n1 = 48L;
Long n2 = 48L;
System.out.println(n3 == n1); //true
System.out.println(n1 == n2); //true
System.out.println(n1.equals(n2)); //true
System.out.println(n1.equals(n3)); //true
System.out.println(n1.intValue() == n2.intValue()); //true
System.out.println("--------直接賦值方式,當(dāng)值不在[-127,128]之間時---------");
Long n4 = 128L;
Long n5 = 128L;
long n6 = 128;
System.out.println(n4 == n5); //false
System.out.println(n4 == n6); //true
System.out.println(n4.equals(n5)); //true
System.out.println(n4.equals(n6)); //true
System.out.println(n4.intValue() == n5.intValue()); //true
//使用Long.intValue()方法時需要注意驗證是否為null,防止出現(xiàn)NullPointException
}
}
針對上面的執(zhí)行結(jié)果,作如下說明:
首先,對于new方法來聲明一個Integer或者Long對象,因為new對象都是在堆里開辟一塊空間,所以即便兩者的數(shù)值相同,但對于==來說,比較的是地址值,所以會返回false。 對于基本數(shù)據(jù)類型的包裝類,都重寫了equals()方法,會比較數(shù)值大小,所以用equals()方法是可以根據(jù)數(shù)值大小進(jìn)行判斷的。 對于Integer變量與int變量比較的問題,會發(fā)現(xiàn)也是基于數(shù)值大小得出來的比較值,這是因為在比較時,Integer類型做了自動拆箱,轉(zhuǎn)成了int類型。 前三點的解釋,對所有包裝類型都是適用的 對于直接賦值方式,值為48的兩個Integer變量,用==號判斷是true,而當(dāng)值為128后,卻為false。這是因為在底層,對于Integer n1 = 48;這種直接賦值的方式,其實調(diào)用了Integer.value()方法。我們可以簡單看一下Integer.value()方法的源碼,如下圖所示:


我們可以看到,這里有個if判斷,當(dāng)輸入的i在[-128,127]的范圍內(nèi)時,直接從IntegerCache數(shù)組中返回了。所以,對于在這個范圍內(nèi)的數(shù)值,返回的都是這個數(shù)組對應(yīng)的地址值,因此用==號判斷會返回true。而不在這個范圍內(nèi)的,是new出的對象,因此會返回false。這個結(jié)論對于Byte、Short、Integer、Long類型都成立(感興趣的可以去看下它們對應(yīng)的value()方法的源碼),因為Byte類型的范圍就是[-128,127],所以對于Byte類型來說,使用==與equals()沒有區(qū)別。
而對于大小比較,使用>、<、<=、>=是沒有問題的,它們會進(jìn)行自動拆箱。但是我們通常建議使用以下兩種方式來進(jìn)行大小比較:
調(diào)用xxxValue()方法轉(zhuǎn)成基本數(shù)據(jù)類型進(jìn)行比較 使用compareTo()方法進(jìn)行比較,在包裝類中,都重寫了compareTo()方法。查看compareTo()源碼,可以看出,其實它底層使用的也是通過自動拆箱轉(zhuǎn)成了對應(yīng)的基本數(shù)據(jù)類型再進(jìn)行比較的。

二、Java對象的比較
有了上面的介紹之后,對象的比較就比較容易了。原理都是一樣的。
1. String類型的比較
需要注意的是,String類型不能直接使用>、<=、>=、<,會報編譯異常。
package dailytest;
import org.junit.Test;
/**
* Java中的比較總結(jié)
* @author yrr
*/
public class JavaCompareTest {
@Test
public void test03() {
String s1 = new String("123");
String s2 = new String("123");
System.out.println(s1 == s2); //false
System.out.println(s1.equals(s2));
String s3 = "234";
String s4 = "234";
System.out.println(s3 == s4); //true
System.out.println(s3.equals(s4)); //true
//System.out.println(s1 <= s3); //The operator < is undefined for the argument type(s) java.lang.String, java.lang.String
System.out.println(s1.compareTo(s3) < 0); //true
}
}
2. 類對象的比較
類對象比較結(jié)論也是一樣的,但是相對于基本數(shù)據(jù)類型和String類型,較為復(fù)雜一點。
根據(jù)某一規(guī)則,判斷兩個對象是否相等,需要在被判斷類中重寫equals()方法,示例代碼如下:
package dailytest;
import org.junit.Test;
/**
* Java中的比較總結(jié)
* @author yrr
*/
public class JavaCompareTest {
@Test
public void test04() {
Person p1 = new Person("yrr",18);
Person p2 = new Person("yrr",18);
System.out.println(p1 == p2); //false
System.out.println(p2.equals(p1)); //true
}
}
class Person{
private String name;
private Integer age;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
@Override
public boolean equals(Object obj) {
Person person = (Person) obj;
return name.equals(person.getName()) && age.equals(person.getAge());
}
}
而如果要比較兩個對象的大?。ㄟ@也是常會問到的面試題),有兩種方式:
被比較類實現(xiàn)Comparable接口,并重寫compareTo()方法 自己定義實現(xiàn)了一個Comparator接口的類或者利用內(nèi)部類,重寫compare()方法 兩者的區(qū)別:前者定義在被比較類上,而后者定義在被比較類外。通過這種區(qū)別,兩者的優(yōu)缺點也很明顯,前者簡單,但需要對被比較類進(jìn)行修改,而后者則不需要修改原代碼,更加靈活。
第一種方式,示例代碼如下:
package dailytest;
import org.junit.Test;
/**
* Java中的比較總結(jié)
* @author yrr
*/
public class JavaCompareTest {
@Test
public void test5() {
Person p1 = new Person("yrr",18);
Person p2 = new Person("wx",19);
System.out.println(p1.compareTo(p2) < 0);
}
}
class Person implements Comparable<Person>{
private String name;
private Integer age;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public Integer getAge() {
return age;
}
@Override
public int compareTo(Person o) {
return this.getAge() - o.getAge();
}
}
第二種方式,示例代碼如下:
package comparator;
import java.util.Arrays;
import java.util.Comparator;
public class MyComparator {
public static void main(String[] args) {
User[] users = new User[] { new User("u1001", 25),
new User("u1002", 20), new User("u1003", 21) };
Arrays.sort(users, new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o1.getAge() - o2.getAge();
}
});
for (int i = 0; i < users.length; i++) {
User user = users[i];
System.out.println(user.getId() + " " + user.getAge());
}
}
}
class User {
private String id;
private int age;
public User(String id, int age) {
this.id = id;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
以上就是本次給大家講的Java中比較問題的相關(guān)內(nèi)容,大家還有其他問題可以在下方留言區(qū)討論,感謝你的支持。
相關(guān)文章
Java中通過繼承Thread類創(chuàng)建線程的步驟
本文介紹了如何通過繼承Thread類創(chuàng)建線程,包括Thread類的定義、創(chuàng)建線程的步驟、優(yōu)缺點、使用場景和注意事項,通過示例代碼展示了多線程下載文件的實現(xiàn),感興趣的朋友跟隨小編一起看看吧2025-02-02
Java在并發(fā)環(huán)境中SimpleDateFormat多種解決方案
這篇文章主要介紹了Java在并發(fā)環(huán)境中SimpleDateFormat多種解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-07-07
Idea2023創(chuàng)建springboot不能選擇java8的解決方法(最新推薦)
在idea2023版本創(chuàng)建springboot的過程中,選擇java版本時發(fā)現(xiàn)沒有java8版本,只有java17和java20,遇到這樣的問題如何解決呢,下面小編給大家分享Idea2023創(chuàng)建springboot不能選擇java8的解決方法,感興趣的朋友一起看看吧2024-01-01

