關於java中的==,equals()
1. 先從一道面試題說起
請問下面的
public class Demo {
public static void main(String args[]){
String a = "a" + "b" + 1;
String b = "ab1";
System.out.println(a == b);
}
}
要了解這個問題,需要回答下面的幾個問題:
- 關於“ ==”是做什麼的?
- equals 呢?
- a和b在記憶體中是什麼樣的?
- 編譯時優化方案。
2. 關於==
在Java語言中,“==”就是對比兩個記憶體單元的內容是否一樣。
如果是原始型別byte,boolean,short,char,int,long,float,double,就是直接比較它們的值。
如果是引用,比較的就是引用的值,“引用的值”可以被認為物件的邏輯地址。如果兩個引用發生“==”操作,就是比較相應的兩個物件的地址值是否一樣。換句話說,如果兩個引用所儲存的物件是同一個物件,則返回true,否則返回false(如果引用指向的是null,其實這也是一個jvm賦予給它的某個指定的值)。
看下面的程式碼:
public class Demo {
public static void main(String args[]){
List<String> a = null;
List<String> b = null;
System.out.println(a == b);
}
}
// 輸出結果
true
3. 關於“equals()”方法
“equals()”方法,首先是在Object類中被定義的,它的定義中就是使用“==”方式來匹配的。
//equals在Object類中的原始碼 public boolean equals(Object obj) { return (this == obj); }
也就是說,如果不去重寫equals()方法,並且對應的類其父類中沒有重寫過equals()方法,那麼預設的equals()操作就是對比物件的地址。
equals()方法之所以存在,是希望子類去重寫這個方法,實現對比值的功能。
3. a和b在記憶體中是什麼樣的?
a和b在記憶體中是指向同一塊記憶體空間的。這就得益於Java的編譯時優化方案。
我們用反編譯軟體jd-gui看看編譯後的程式碼是怎麼樣的?
import java.io.PrintStream; public class Demo { public static void main(String[] args) { String a = "ab1"; String b = "ab1"; System.out.println(a == b); } }
看到這裡結果應該就一目瞭然了。JVM會把常量疊加在編譯時進行優化,因為常量疊加得到的是固定的值,無須執行時再進行計算,所以會這樣優化。
看到這裡彆著急,JVM只會優化它可以幫你優化的部分,它並不是對所有的內容都可以優化。例如,就拿上面疊加字串來說,如果幾個字串疊加出現了變數,即在編譯時還不確定具體的值是多少,那麼JVM是不會去做這樣的編譯時合併的。
如果上面的這段話你理解了,我們再來看一個例子:
public class Demo {
public static void main(String args[]){
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);
}
private static String getA(){
return "a";
}
}
//輸出結果:
false
true
false
根據我們上面的解釋,判斷b==compare和e==compare輸出結果為false,這個比較容易理解,因為a和getA()並不是一個常量,編譯時並不會對此進行優化,我們用jd-gui可靠編譯後的程式碼:
import java.io.PrintStream;
public class Demo
{
public static void main(String[] args)
{
String a = "a";
String c = "a";
String b = a + "b";
String d = "ab";
String e = getA() + "b";
String compare = "ab";
System.out.println(b == compare);
System.out.println(d == compare);
System.out.println(e == compare);
}
private static String getA()
{
return "a";
}
}
從編譯後的程式碼,我們可以驗證我們的結論,b和e並沒有被JVM優化。
比較奇怪的是變數d,被JVM優化了。區別在於對疊加的變數c有一個final修飾符。從定義上強制約束了c是不允許被改變的,由於final不可變,所以編譯器自然認為結果是不可變的。
4. 記憶體中的字串(詳細解釋)
字串物件內部是用字元陣列儲存的,那麼看下面的例子:
String m = "hello,world";
String n = "hello,world";
String u = new String(m);
String v = new String("hello,world");
這些語句會發生什麼事情?大概是這樣的:
- 會分配一個11長度的char陣列,並在常量池分配一個由這個char陣列組成的字串,然後由m去引用這個字串。
- 用n去引用常量池裡邊的字串,所以和m引用的是同一個物件
- 生成一個新的字串,單內部的字元陣列引用著m內部的字元陣列。
- 同樣會生成一個新的字串,但內部的字元陣列引用常量池裡邊的字串內部的字元陣列,意思是和u是同樣的字元陣列。
我們使用圖來表示的話,情況就大概是這樣的:
結論就是,m和n是同一個物件,但m,u,v都是不同的物件,但都使用了同樣的字元陣列,並且用equal判斷的話也會返回true。
我們可以使用反射修改字元陣列來驗證一下效果:
public class Demo {
public static void main(String args[]) throws NoSuchFieldException, IllegalAccessException {
String m = "hello,world";
String n = "hello,world";
String u = new String(m);
String v = new String("hello,world");
Field f = m.getClass().getDeclaredField("value");
f.setAccessible(true);
char[] cs = (char[]) f.get(m);
cs[0] = 'H';
String p = "Hello,world";
System.out.println(m.equals(p));
System.out.println(n.equals(p));
System.out.println(u.equals(p));
System.out.println(v.equals(p));
}
}
//輸出結果:
true
true
true
true
從上面的例子可以看到,經常說的字串是不可變的,其實和其他final類沒有什麼區別,還是引用不可變的意思。雖然String類不開放value,但同樣是可以通過反射進行修改。
5. 關於String中的intern方法
public class Demo {
public static void main(String args[]){
String a = "a";
String b = a + "b";
String c = "ab";
String d = new String(b);
System.out.println(b == c);
System.out.println(c == d);
System.out.println(c == d.intern());
System.out.println(b.intern() == d.intern());
}
}
//輸出結果
false
false
true
true
String引用所指向的物件,它們儲存在常量池中,同一個值的字串保證全域性唯一。
如何保證全域性唯一呢? 當呼叫intern()方法時,JVM會在這個常量池中通過equals()方法查詢是否存在等值的String,如果存在,則直接返回常量池中這個String物件的地址;若沒有找到,則會建立等值的字串,然後再返回這個新建立空間的地址。只要是同樣的字串,當呼叫intern()方法時,都會得到常量池中對應String的引用,所以兩個字串通過intern()操作後用等號是可以匹配的。
相關推薦
Java中==和equals(),equalsIgnoreCase()
關於==和equals,我們需要知道java中的資料型別,可分為兩類: 1.基本資料型別,也稱原始資料型別。byte,short,char,int,long,float,double,boolean 他們之間的比較,應用雙等號(==),比較的是他們的值。 2.複合資料
Java中==和equals的區別,equals和hashCode的區別
在java中: ==是運算子,用於比較兩個變數是否相等。 equals,是Objec類的方法,用於比較兩個物件是否相等,預設Object類的equals方法是比較兩個物件的地址,跟==的結果一樣。Ob
【JAVA學習】java中==、equals()、hashCode()都和物件的比較有關,在java中這三者各有什麼用處呢,即java中為什麼需要設計這三種物件的比較方法呢?
關於hashCode() 為什麼會設計hashCode()方法? hashCode()方法返回的就是一個數值,我們稱之為hashCode吧。從方法的名稱上就可以看出,其目的是生成一個hash碼。hash碼的主要用途就是在對物件進行雜湊的時候作為key輸入,據此很容易推斷出,我們需要每個物件的ha
Java中 == 與 equals方法,以及常見的 == 比較
1、”==”: 是算數運算子,比較的是兩個引用指向的是否是同一個記憶體地址,也就是指向的是否是同一物件。 2、equals方法: 是屬於Object的方法,是開發者自己根據具體的業務邏輯來定義該方法,用於檢查兩個物件的相等性,注意是讓開發者自己去重寫的方法
JAVA中重寫equals()方法的同時要重寫hashcode()方法
內存地址 his mov bool args 變量 維護 log obj object對象中的 public boolean equals(Object obj),對於任何非空引用值 x 和 y,當且僅當 x 和 y 引用同一個對象時,此方法才返回 true;註意:當此方法
Java中的equals()和hashCode()
sea 接口 後來 ide itl 一個數 毫無 exceptio title 概述 在我們使用類集框架(比方使用hashMap、hashSet)的時候,常常會涉及到重寫equals()和hashCode()這兩個方法。 這兩個方法的聯系是:
java中==與equals
字符串 基本類型 true 變量 由於 als 不同 引用 str2 == ==可用於比較基本類型與引用類型,對於基本類型變量比較的是其存儲的值是否相等,對於引用類型則比較的是其是否指向同一個對象。 如: int a = 10; int b = 20; d
java中,什麽是構造函數?什麽是構造函數重載?什麽是復制構造函數?
默認 調用 構造函數 多個 必須 自己 ava nbsp 每一個 當新對象被創建的時候,會調用構造函數。每一個類都有構造函數。在程序員沒有給類提供構造函數的情況下,java編譯器會為這個類創建一個默認的構造函數。 java中構造函數的重載和方法重載很相似。
Java中“==”和“equals()”的區別
spa logs bsp 指向 monday class code equals out “==”比較的是變量所指向的對象,當S1在內存中定義以後,再定義s2的時候s2所指向的值是定義s1時候所創建的,而不是又在內存創建了一個“Monda
java 中 “==” 和 equals 的區別
通過 引用 而在 program 值範圍 兩個 比較 copy mon 在初學Java時,可能會經常碰到下面的代碼: 1 String str1 = new String("hello"); 2 String str2 = new String("hello");
使用java中,面向對象封裝+繼承的方法算題
去掉空格 方法 amp get urn 余數 oid pan 新的 1.第一種:給定一行字符,逆序輸出此字符串(空格.數字不輸出),如“ab 23,(4 cd”輸出“dc(,ba”。(要求:使用面向對象封裝+繼承) class Bu { private Strin
Java中==和equals和區別詳解+案例
兩個 布爾型 整數 返回 boolean 和equal clas 定義 true 一開始遇見==和equals我也是分不清,後來看了很多博客,收益匪淺, 擔心以後給忘了,所以寫下這個,以後復習可以用。 (有哪裏寫得不對的,希望可以留言幫忙改進,大家一起共同進步) 一、Jav
java中的equals與==
bsp 源碼 內部 brush ++ class object類 功能 length equals()與==都是java中用於進行比較的,返回boolean值,不同的是equals()是Object類中定義的一個方法,==是一個比較運算符。下面是equals()在O
Java中,類及其組成所使用的常見修飾符
成員 name fin 常用 tro string prot 默認 abstract Java中,類及其組成所使用的常見修飾符 (1)修飾符的分類: 權限修飾符:private、默認、protected、public 狀態修飾符:static、fin
Java中,內部類的概述和內部類的訪問特點和內部類的分類(內部類的位置)
back 外部 mage 對象 post info bsp 一個 strong 內部類的概述: 把類定義在另一個類的內部,該類就被稱為內部類。 舉例:把類B定義在類A中,類B就被稱為內部類。 內部類的訪問特點: A:內部類可以直接訪問外部類的成員,包括
Java中,局部內部類
外部類 col 局部變量 外部 成員 變量名 class post 常量值 局部內部類 A:局部內部類可以直接訪問外部類的成員。 B:局部內部類在局部位置可以創建內部類對象,通過內部類對象調用內部類方法,來使用局部內部類功能。 C:局部內部類訪問局部變量
Java中,匿名內部類
java () ack http 代碼 一個 back 抽象 子類 匿名內部類 就是局部內部類的簡化寫法。 前提:存在一個類或者接口。 這裏的類可以是具體類也可以是抽象類。 格式: new 類名或者接口名() { 重寫方法; }
Java中,權限修飾符的權限測試
註意 div pre package img string 需要 fat prot ============================================================================= 1、 1 /* 2
java中的equals方法
equal zjoi csb wow kvc gfw 因此 sas hid 一、equals方法介紹 1.1.通過下面的例子掌握equals的用法 1 package cn.galc.test; 2 3 public class TestEquals { 4
在java中,輸入兩個數,輸出較大的數
ring bsp println OS [] tint sys span 適用於 第一種 1 import java.util.*; 2 public class A{ 3 public static void main(String[] args){ 4