從老生常談的包裝類型“==” 和 “equals” 說起
最近在刷leetcode題時(shí),又學(xué)到了很多之前沒有注意到的點(diǎn),隨便寫寫,想到哪,寫到哪吧。
- 請看如下代碼:
public static void main(String[] args) {
//這里改成Integer integerA = new Integer(12)會(huì)對最終結(jié)果有影響,后面會(huì)說明
Integer integerA = 12;
Integer integerB = 12;
System.out.println("integerA="+integerA+",integerB="+integerB);
if(integerA == integerB){
System.out.println("integerA == integerB");
}else {
System.out.println("integerA != integerB");
}
if(integerA.equals(integerB)){
System.out.println("integerA equals integerB");
}else {
System.out.println("integerA not equals integerB");
}
}
- 上述代碼輸出為:
integerA=12,integerB=12
integerA == integerB
integerA equals integerB
- 修改變量A、B的值為integerA=128,integerB=128之后,輸出為:
integerA=128,integerB=128
integerA != integerB
integerA equals integerB
- 此時(shí)integerA != integerB
- equals方法:這里Integer 包裝類重寫了equals方法,比較的是內(nèi)置基礎(chǔ)類型的值,同一個(gè)值是同一地址??梢赃@樣理解,在編譯時(shí),IntegerA 和 IntegerB是引用類型的對象,他們兩個(gè)有自己的地址,其中Integer對象中的value變量的地址是同一個(gè)。
//Integer.java
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
/**
* Returns the value of this {@code Integer} as an
* {@code int}.
*/
public int intValue() {
return value;
}
- “==”比較的是引用或者說地址是否相等(8種基本類型可以理解為直接比較的值)正常情況下,IntegerA 和 IntegerB作為一個(gè)引用 類型的對象調(diào)用“==”比較,都應(yīng)該不同,而這里變量等于12時(shí),,IntegerA 和 IntegerB是同一個(gè)引用,正是因?yàn)閖ava常量池。
- 關(guān)于jvm內(nèi)存可以參考:JVM內(nèi)存模型。注意jdk1.7中的常量池移到了堆內(nèi)存中,同時(shí)在jdk1.8中移除整個(gè)永久代,對應(yīng)的是元空間(Metaspace)區(qū)域的引進(jìn)。Oracle jdk1.7說明,RFE 6962931
何為常量池?
- 通俗的講,即為常量共享的一種方案。可以參考這篇文章,講的很詳細(xì)。Java常量池理解與總結(jié)
- 5種包裝類(Byte,Short,Integer,Character,Long) ,內(nèi)存中只緩存-128 到 127范圍內(nèi)的數(shù)據(jù),其中Byte類型全部進(jìn)行了緩存。這也是為什么當(dāng)時(shí)我們初始化的值比128小時(shí),引用都是用的同一個(gè)的原因。如果我們初始化時(shí)使用new Integer(12),則不會(huì)相同。直接上代碼:
Integer.java
/**
* 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,
(劃重點(diǎn),內(nèi)存中只緩存-128 to 127范圍內(nèi)的數(shù)據(jù))
* 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);
}
Byte.java
/**
* Returns a {@code Byte} instance representing the specified
* {@code byte} value.
* If a new {@code Byte} instance is not required, this method
* should generally be used in preference to the constructor
* {@link #Byte(byte)}, as this method is likely to yield
* significantly better space and time performance since
* all byte values are cached.
*(全部 byte 值已經(jīng)緩存)
*
* @param b a byte value.
* @return a {@code Byte} instance representing {@code b}.
* @since 1.5
*/
public static Byte valueOf(byte b) {
final int offset = 128;
//這里加偏移量是因?yàn)閎yte對應(yīng)的整數(shù)值和緩存索引值并非相等
return ByteCache.cache[(int)b + offset];
}
/**
* Byte.java中的內(nèi)部類
*/
private static class ByteCache {
private ByteCache(){}
static final Byte cache[] = new Byte[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Byte((byte)(i - 128));
}
}
知道了這些有什么用
- 享元模式Flyweight Design Pattern in Java
- 享元模式(Flyweight Pattern)主要用于減少創(chuàng)建對象的數(shù)量,以減少內(nèi)存占用和提高性能。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,減少對象數(shù)量從而改善應(yīng)用所需的對象結(jié)構(gòu)的方式。實(shí)現(xiàn)方式一般是通過HashMap完成。java常量池的設(shè)計(jì)初中也是為了減少內(nèi)存占用,同時(shí)保證訪問安全。繼承Number的包裝類常量池存儲(chǔ)使用數(shù)組,String使用繼承自HashTable的SStringTable:
op StringTable::basic_add(int index_arg, Handle string, jchar* name,
int len, unsigned int hashValue_arg, TRAPS) {
// ...
HashtableEntry<oop, mtSymbol>* entry = new_entry(hashValue, string()); // 新建
add_entry(index, entry); // 添加
return string();
}
- 說到底,設(shè)計(jì)模式是為了解決實(shí)際問題,而不是炫技,從問題出發(fā),找出解決問題的方案,如果該方案是通用的,也就成了設(shè)計(jì)模式了。
- 在查資料的過程中,好多人的文章已經(jīng)過時(shí)了,有的說的不準(zhǔn)確甚至錯(cuò)誤的。甄別的過程,也是蠻花力氣的。要養(yǎng)成獨(dú)立思考的習(xí)慣,有矛盾的,以官方資料為準(zhǔn)。