1、?“==”和equals 的區別
- “==”是運算符,如果是基本數據類型,則比較存儲的值;如果是引用數據類型,則比較所指向對象的地址值。
- equals是Object的方法,比較的是所指向的對象的地址值,一般情況下,重寫之后比較的是對象的值。
ps:問的是:int a = 3;if(a==3){},比較的是值還是內存地址。這個忘了,想到另一個概念整數常量池,是自己想多了,和整數常量池的問題記混了,看到a==3,就想著3不會在內存中新建。傻了,這里再說下在方法中int a = 3。a和3(基本類型)都是保存在棧中的,引用類型對象才會在棧中存堆的內存地址。所以==比較基本類型時只能比較值,沒有什么內存地址給你比較。
下面代碼
Integer a = 3;//自動裝箱:等價于 Integer.valueOf(3) System.out.println(a==3);Integer b = new Integer(4); System.out.println(b==4);
結果是true ,true,雖然Integer是個整數對象,不是基本類型,按理說要比較內存地址,這里應該比較內存了,但是Integer和int一起運算時會拆包成基本類型,所以比的還是值。
Integer p = new Integer(5); Integer q = new Integer(5); System.out.println(p == q); // 輸出 false(顯式創建新對象)
這里比較的都是引用類型,比較就是內存地址了。?
2、整數常量池
Integer i = 123; Integer i2 = 123; System.out.println(i==i2);Integer i3 = 129; Integer i4 = 129; System.out.println(i3==i4);
輸出結果:
true
false
解釋如下:
整數常量池的作用場景
-
自動裝箱(Autoboxing):
-
當將基本類型?
int
?轉換為包裝類?Integer
?時(比如賦值給?Integer
?變量,或存入集合類),Java 會嘗試復用常量池中已緩存的?Integer
?對象,而不是每次都創建新對象。 -
例如:
Integer a = 100; // 自動裝箱,使用常量池中的對象 Integer b = 100; System.out.println(a == b); // 輸出 true(同一對象)
-
-
手動創建?
Integer
?對象:-
如果直接通過?
new Integer()
?創建對象,會強制生成新實例,繞過常量池:Integer c = new Integer(100); Integer d = new Integer(100); System.out.println(c == d); // 輸出 false(不同對象)
-
-
超出緩存范圍的情況:
-
當數值超出?
-128
~127
?范圍時,即使通過自動裝箱,也會生成新對象:Integer e = 200;
Integer f = 200;
System.out.println(e == f); // 輸出 false(超出緩存范圍)
?整數常量池的實現原理
Java 在?Integer
?類中通過靜態內部類?IntegerCache
?實現緩存
private static class IntegerCache {
? ? static final int low = -128;
? ? static final int high; // 默認 127,可通過 JVM 參數調整
? ? static final Integer[] cache;
? ??
? ? static {
? ? ? ? // 初始化緩存數組
? ? ? ? high = 127;
? ? ? ? cache = new Integer[(high - low) + 1];
? ? ? ? for (int i = 0; i < cache.length; i++) {
? ? ? ? ? ? cache[i] = new Integer(i - 128);
? ? ? ? }
? ? }
}
通過?Integer.valueOf(int)
?方法獲取?Integer
?對象時,會優先從緩存中取:
public static Integer valueOf(int i) {
? ? if (i >= IntegerCache.low && i <= IntegerCache.high)
? ? ? ? return IntegerCache.cache[i + 128];
? ? return new Integer(i);
}
3 整數塞入map后怎么存放值的
Stdent zzw = new Stdent("zzw", 15); Map<String ,Object> map = new HashMap<>(); map.put("stu", zzw); Integer i =new Integer(22); map.put("size",i); System.out.println(map); i =new Integer(33); zzw.age =20; System.out.println(map);
static class Stdent{public String name ;public int age ;public Stdent(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Stdent{" +"name='" + name + '\'' +", age=" + age +'}';} }
結果:
{size=22, stu=Stdent{name='zzw', age=15}}
{size=22, stu=Stdent{name='zzw', age=20}}
這里為什么age變了,size沒有變?
標下每個變量在內存中怎么存的
關鍵解釋
-
對象存儲位置
-
new Stdent("zzw", 15)
?和?new Integer(22)
?是對象實例,存儲在堆中。 -
它們的地址分別是?
0x100
?和?0x200
(假設的內存地址)。
-
-
引用變量存儲位置
-
zzw
?和?i
?是引用變量,存儲在棧中。 -
它們的值是堆中對象的地址(如?
zzw = 0x100
)。
-
-
put
?操作存儲的內容-
map.put("stu", zzw)
?和?map.put("size", i)
?存入的是?堆中對象的地址(即?0x100
?和?0x200
),而不是變量?zzw
?或?i
?的棧地址。 -
Map
?的鍵值對中存儲的是實際對象的引用。
-
然后后面改變值的時候,zzw.age =20 ,只是改變stu對象里的值,map的引用并不變,就是stu存的還是student對象在堆中的內存地址,所以后面打印的會變。那為什么map中的size的輸出不變呢。因為Intger對象是final修飾的,這個對象不可以修改,i =new Integer(33);后面半句會在堆中建一個新的內存地址,然后前半句把i在棧中存的內存地址換成這個新的,但是map里存的還是之前對象的地址。所以不會輸出不會變。
4、map的操作都是深拷貝還是淺拷貝?
上個例子其實可以看出來,zzw.age=20,然后map里的stu里的age也跟著變了,所以map的put方法是個淺拷貝,問了下deepseek告訴我map的操作幾乎都是淺拷貝,那我想putall也是了?
Stdent zzw = new Stdent("zzw", 15); Map<String ,Object> map = new HashMap<>(); map.put("stu", zzw); map.put("size",22); Map<String ,Object> map2 = new HashMap<>(); map2.putAll(map); map.clear(); System.out.println(map); System.out.println(map2);
結果是
{}
{size=22, stu=Stdent{name='zzw', age=15}}?
?deepseek給的解釋
3.?map2.putAll(map)
ok,所以clear只是清除map在堆中的信息,切斷了這個map和stu和整數對象的引用。
但是map2還是沒有變,不是說淺拷貝,map2和map就指向同一個內存地址.
Stdent zzw = new Stdent("zzw", 15); Map<String ,Object> map = new HashMap<>(); map.put("stu", zzw); map.put("size",22); Map<String ,Object> map2 = map; map.clear(); System.out.println(map); System.out.println(map2);結果:
{}
{}
再看一個
Stdent zzw = new Stdent("zzw", 15); Map<String ,Object> map = new HashMap<>(); map.put("stu", zzw); map.put("size",22); Map<String ,Object> map2 = new HashMap<>(); map2.putAll(map); Stdent zzw2 = new Stdent("zzw2", 30); map.put("stu", zzw2); map.put("size", 32); System.out.println(map); System.out.println(map2);
?結果:
{size=32, stu=Stdent{name='zzw2', age=30}}
{size=22, stu=Stdent{name='zzw', age=15}}
所以結論就是
Map<String ,Object> map2 = new HashMap<>();
map2.putAll(map);
這里有個2個關鍵點,1:這里我new了一個map,在堆中新建了內存地址,和之前map不是同一個了,但是2:map里面的對象沒有重新new一個新的,還是復用,只是大家(map和map2)都指向這些對象的內存地址。
然后clear只是清空這個map在堆中的內容,他存的對象不回清理。