引入
==
- 基本數據類型(也稱原始數據類型) :byte,short,char,int,long,float,double,boolean。他們之間的比較,應用雙等號(==),比較的是他們的值。
- 復合數據類型(類):當他們用(==)進行比較的時候,比較的是他們在內存中的存放地址(確切的說,是堆內存地址)。
- 注:對于第二種類型,除非是同一個new出來的對象,他們的比較后的結果為true,否則比較后結果為false。因為每new一次,都會重新開辟堆內存空間。
equals
- JAVA當中所有的類都是繼承于Object這個超類的,在Object類中定義了一個equals的方法,這個方法的初始行為是比較對象的內存地址,但在一些類庫當中這個方法被復寫了,如String、Integer、Date。在這些類當中equals有其自身的實現,而不再是比較類在堆內存中的存放地址了。
所以說,對于復合數據類型之間進行equals比較,在沒有覆寫equals方法的情況下,他們之間的比較還是內存中的存放位置的地址值,跟雙等號(==)的結果相同;如果被復寫,按照復寫的要求來。
小結
- “==”比較的是值【變量(棧)內存中存放的對象的(堆)內存地址】
- equal用于比較兩個對象的值是否相同【不是比地址】
- 【特別注意】Object類中的equals方法和“==”是一樣的,沒有區別,而String類,Integer類等等一些類,是重寫了equals方法,才使得equals和“==不同”,所以,當自己創建類時,自動繼承了Object的equals方法,要想實現不同的等于比較,必須重寫equals方法。
- "=="比"equal"運行速度快,因為"=="只是比較引用.
Integer 和 int
代碼塊
public class Test {
public static void main(String args[]){
test1();
test2();
}
public static void test2(){
System.out.println("既有基本數據類型,又有對應的類(這里用Integer和int舉例 1231):");
Integer a1 = new Integer(1231);
Integer a2 = new Integer(1231);
System.out.println("Integer new Integer");
System.out.println("== —> "+(a1 == a2));//new 出來兩個對象,==比較的是地址
System.out.println("equals —> "+(a1.equals(a2)));//equals比較的是值
Integer b1 = 1231;
Integer b2 = 1231;//這里會調用Integer.valueof();
System.out.println("Integer 直接賦值:");
System.out.println("== —> "+(b1 == b2));//b1、b2自動裝箱產生的對象,其值都是1231,那么這里很特殊的是1231正好不在-128<=i7<=127這個范圍內的,那么會重新new出一個對象
System.out.println("equals —> "+(b1.equals(b2)));
int c1 = 1231;
int c2 = 1231;
System.out.println("基本數據類型int 直接賦值:");
System.out.println("== —> "+(c1 == c2));//“==”對于基本數據類型,判斷兩個變量的值是否相等
System.out.println("沒有equals方法");
int d1 = new Integer(1231);
int d2 = new Integer(1231);
System.out.println("基本數據類型int new Integer:");
System.out.println("== —> "+(d1 == d2));//c1、c2拆箱產生的對象,調用的是Integer.intValue的方法
System.out.println("沒有equals方法");
int e1 = 1231;
int e2 = new Integer(1231);
System.out.println("一個int new Integer,一個直接賦值");
System.out.println("== —> "+(e1 == e2));
System.out.println("沒有equals方法");
Integer f1 = 1231;
Integer f2 = new Integer(1231);
System.out.println("一個Integer new Integer,一個Integer直接賦值");
System.out.println("== —> "+(f1 == f2));//f1是從IntegerCache取的數據,而f2是new出來的一個對象,自然不一樣
System.out.println("equals —> "+(f1.equals(f2)));
}
public static void test1(){
System.out.println("既有基本數據類型,又有對應的類(這里用Integer和int舉例 123):");
Integer a1 = new Integer(123);
Integer a2 = new Integer(123);
System.out.println("Integer new Integer");
System.out.println("== —> "+(a1 == a2));//new 出來兩個對象,==比較的是地址
System.out.println("equals —> "+(a1.equals(a2)));//equals比較的是值
Integer b1 = 123;
Integer b2 = 123;//這里會調用Integer.valueof();
System.out.println("Integer 直接賦值:");
System.out.println("== —> "+(b1 == b2));//b1、b2自動裝箱產生的對象,其值都是123,那么這里很特殊的是123正好在-128<=i7<=127這個范圍內的,那么會去IntegerCache中取,既然都是去IntegerCache中去取,那么自然該對象應該是一個對象,那么再堆中的地址應該是一樣的,所以在判讀兩個對象是不是== 的時候,會輸出true
System.out.println("equals —> "+(b1.equals(b2)));
int c1 = 123;
int c2 = 123;
System.out.println("基本數據類型int 直接賦值:");
System.out.println("== —> "+(c1 == c2));//“==”對于基本數據類型,判斷兩個變量的值是否相等
System.out.println("沒有equals方法");
int d1 = new Integer(123);
int d2 = new Integer(123);
System.out.println("基本數據類型int new Integer:");
System.out.println("== —> "+(d1 == d2));//c1、c2拆箱產生的對象,調用的是Integer.intValue的方法
System.out.println("沒有equals方法");
int e1 = 123;
int e2 = new Integer(123);
System.out.println("一個int new Integer,一個直接賦值");
System.out.println("== —> "+(e1 == e2));
System.out.println("沒有equals方法");
Integer f1 = 123;
Integer f2 = new Integer(123);
System.out.println("一個Integer new Integer,一個Integer直接賦值");
System.out.println("== —> "+(f1 == f2));//f1是從IntegerCache取的數據,而f2是new出來的一個對象,自然不一樣
System.out.println("equals —> "+(f1.equals(f2)));
}
}
運行結果
既有基本數據類型,又有對應的類(這里用Integer和int舉例 123):
Integer new Integer
== —> false
equals —> true
Integer 直接賦值:
== —> true
equals —> true
基本數據類型int 直接賦值:
== —> true
沒有equals方法
基本數據類型int new Integer:
== —> true
沒有equals方法
一個int new Integer,一個直接賦值
== —> true
沒有equals方法
一個Integer new Integer,一個Integer直接賦值
== —> false
equals —> true
既有基本數據類型,又有對應的類(這里用Integer和int舉例 1231):
Integer new Integer
== —> false
equals —> true
Integer 直接賦值:
== —> false
equals —> true
基本數據類型int 直接賦值:
== —> true
沒有equals方法
基本數據類型int new Integer:
== —> true
沒有equals方法
一個int new Integer,一個直接賦值
== —> true
沒有equals方法
一個Integer new Integer,一個Integer直接賦值
== —> false
equals —> true
源碼分析
Integer裝箱源碼(主要是Integer.valueOf方法)
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
IntegerCache類
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
數值在-128<=x<=127這個范圍內的,那么會去IntegerCache中取。超過這個范圍回去new一個對象。
Integer拆箱源碼(主要是Integer.intValue方法)
/**
* Returns the value of this {@code Integer} as an
* {@code int}.
*/
public int intValue() {
return value;
}
從源碼可以看出,當包裝器類型和基本數據類型進行“==”比較時,包裝器類型會自動拆箱為基本數據類型。
基本類型 | 占用空間(Byte) | 表示范圍 | 包裝器類型 |
---|---|---|---|
boolean | 1/8 | true、false | Boolean |
char | 2 | -128~127 | Character |
byte | 1 | -128~127 | Byte |
short | 2 | -2?15~2?15-1 | Short |
int | 4 | -2?31~2?31-1 | Integer |
long | 8 | -2?63~2?63-1 | Long |
float | 4 | -3.403E38~3.403E38 | Float |
double | 8 | -1.798E308~1.798E308 | Double |
以下感覺寫的比較好的博客:
Java自動裝箱與拆箱及其陷阱
Java 的Integer、int與new Integer到底怎么回事?
String
Java的虛擬機在內存中開辟出一塊單獨的區域,用來存儲字符串對象,這塊內存區域被稱為字符串緩沖池。當使用 String a = "abc" 這樣的語句進行定義一個引用的時候,首先會在字符串緩沖池中查找是否已經相同的對象,如果存在,那么就直接將這個對象的引用返回給a,如果不存在,則需要新建一個值為"abc"的對象,再將新的引用返回a。
String a = new String("abc");這樣的語句明確告訴JVM想要產生一個新的String對象,并且值為"abc",于是就在堆內存中的某一個小角落開辟了一個新的String對象。
- == ?在比較引用的情況下,會去比較兩個引用的內存地址是否相等。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1 == str2);
System.out.println(str1.equals(str2));
String str2 = new String("abc");
System.out.println(str1 == str2);
System.out.println(str1.equals(str2));
以上代碼將會輸出
true
true
false
true
**第一個true:**因為在str2賦值之前,str1的賦值操作就已經在內存中創建了一個值為"abc"的對象了,然后str2將會與str1指向相同的地址。
**第二個true:**因為`String`已經重寫了`equals`方法:為了方便大家閱讀我貼出來,并且在注釋用進行分析:
```
public boolean equals(Object anObject) {
//如果比較的對象與自身內存地址相等的話
//就說明他兩指向的是同一個對象
//所以此時equals的返回值跟==的結果是一樣的。
if (this == anObject) {
return true;
}
//當比較的對象與自身的內存地址不相等,并且
//比較的對象是String類型的時候
//將會執行這個分支
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;
//在這里循環遍歷兩個String中的char
while (n-- != 0) {
//只要有一個不相等,那么就會返回false
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
```
自定義類
public class Test {
public static void main(String args[]){
test();
}
public static void test(){
A a = new A(1);
A b = new A(1);
System.out.println(a==b);
System.out.println(a.equals(b));
}
static class A {
private int a;
public A(int a){
this.a =a ;
}
}
}
這時候輸出的結果兩個都是false,因為A類是Objcet的子類,在沒有重寫equals方法的時候,調用equals方法其實是調用Object的equals的方法,而Object類中的equals方法和“==”是一樣的,比較的還是內存中的存放地址
Object的equals的源碼
public boolean equals(Object obj) {
return (this == obj);
}
重寫Object的equals方法(類似于String的equals方法)
public class Test {
public static void main(String args[]){
test();
}
public static void test(){
A a = new A(1);
A b = new A(1);
System.out.println(a==b);
System.out.println(a.equals(b));
}
static class A {
private int a;
public A(int a){
this.a =a ;
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof A)) {
return false;
}
A other = (A) obj;
return a==other.a;
}
}
}
這里返回值第一個是false,第二個是true