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; } @Override public 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在堆中的内容,他存的对象不回清理。