当前位置: 首页 > news >正文

面试踩过的坑

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

解释如下:

整数常量池的作用场景

  1. 自动装箱(Autoboxing)

    • 当将基本类型 int 转换为包装类 Integer 时(比如赋值给 Integer 变量,或存入集合类),Java 会尝试复用常量池中已缓存的 Integer 对象,而不是每次都创建新对象。

    • 例如:

      Integer a = 100; // 自动装箱,使用常量池中的对象 Integer b = 100; System.out.println(a == b); // 输出 true(同一对象)

  2. 手动创建 Integer 对象

    • 如果直接通过 new Integer() 创建对象,会强制生成新实例,绕过常量池:

      Integer c = new Integer(100); Integer d = new Integer(100); System.out.println(c == d); // 输出 false(不同对象)
      
  3. 超出缓存范围的情况

  4. 当数值超出 -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没有变?

标下每个变量在内存中怎么存的

关键解释

  1. 对象存储位置

    • new Stdent("zzw", 15) 和 new Integer(22) 是对象实例,存储在堆中。

    • 它们的地址分别是 0x100 和 0x200(假设的内存地址)。

  2. 引用变量存储位置

    • zzw 和 i 是引用变量,存储在栈中。

    • 它们的值是堆中对象的地址(如 zzw = 0x100)。

  3. 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在堆中的内容,他存的对象不回清理。

相关文章:

  • 深度学习--自然语言处理统计语言与神经语言模型
  • 2015-2023 各省 GDP 数据,用QuickBI 进行数据可视化——堆叠图!
  • 数据库-少库多表与多库少表理论
  • 数据库对比
  • 插入html文件,让数据可视化彰显高端大气-Excel易用宝
  • flume整合Kafka和spark-streaming核心编程
  • eclipse怎么导入junit4
  • vscode和git 踩坑
  • deepseek-r1-671B满血版,全栈式智能创作平台 - 多模态大模型赋能未来创作
  • JavaScript { message } = toRefs(obj) 中的 { message }的语法格式
  • 伟世通与火山引擎深度合作 前沿AI智能座舱解决方案亮相上海车展
  • 如何在Linux用libevent写一个聊天服务器
  • 提示js方法未定义,但是确实<textarea>标签未闭合。
  • AI网络渗透kali应用(gptshell)
  • 华为VRP系统简介配置TELNET远程登录!
  • AI 工程师崛起:科技浪潮下的新兴力量
  • Flink 数据清洗与字段标准化最佳实践
  • 【高中数学/古典概率】从1~2000中随机抽一个数,问取到的数既不被8整除,又不被12整除的概率是多少?
  • 计算机网络核心知识点全解析(面试通关版)
  • 全面解析 MCP(Model Context Protocol):AI 大模型的“万能连接器”
  • 如何做大中国拳击产业的蛋糕?这项赛事给出办赛新思考
  • 王毅会见乌兹别克斯坦外长赛义多夫
  • 健康社区“免疫行动”促进计划启动,发布成人预防“保典”
  • 科克托是说真话的骗子,而毕加索是一言不发、让大家去猜的人
  • 潘功胜:一季度中国经济开局良好,金融市场展现出较强韧性
  • 加拿大财长:加拿大需要抗击美国关税