Bootstrap

小米研发服务器工程师面经

小米研发服务器工程师面经

  • 作者简介:一名后端开发人员,每天分享后端开发以及人工智能相关技术,行业前沿信息,面试宝典。
  • 座右铭:未来是不可确定的,慢慢来是最快的。
  • 个人主页极客李华-CSDN博客
  • 合作方式:私聊+
  • 这个专栏内容:BAT等大厂常见后端java开发面试题详细讲解,更新数目100道常见大厂java后端开发面试题。
  • 我的CSDN社区:https://bbs.csdn.net/forums/99eb3042821a4432868bb5bfc4d513a8
  • 微信公众号,抖音,b站等平台统一叫做:极客李华,加入微信公众号领取各种编程资料,加入抖音,b站学习面试技巧

一面

1. ==equals 的区别

  • ==:
    • 比较的是引用是否相同(对于对象),或者是否相等(对于基本数据类型)。
    • 对于引用类型,==判断两个引用是否指向同一个内存地址。
  • equals:
    • Object 类中的方法,默认行为和 == 一致,但可以被重写。
    • 通常用于比较对象的内容是否相同。
    • 例如:String 类重写了 equals 方法,比较的是字符串的内容,而不是内存地址。

2. 重写 equals 的过程中需要注意的事情

  • 必须满足以下特性
    • 自反性:x.equals(x) 为 true。
    • 对称性:x.equals(y) 为 true,则 y.equals(x) 也为 true。
    • 传递性:x.equals(y)y.equals(z) 为 true,则 x.equals(z) 也为 true。
    • 一致性:多次调用 x.equals(y) 结果始终一致,前提是对象未被修改。
    • hashCode 一致:如果两个对象 equals 返回 true,则其 hashCode 也必须相等。
  • 重写hashCode:重写 equals 时应同步重写 hashCode 方法,以确保 HashMap 等集合的正确性。

3. 代码示例分析

String a = new String("111");
String b = new String("111");
System.out.println(a == b); // 输出 false
  • 原因new String("111") 会创建新的对象,ab 是两个不同的对象,== 比较的是引用地址。
String a = "111";
String b = "111";
System.out.println(a == b); // 输出 true
  • 原因:字符串常量池中的相同字符串只会存储一份,ab 指向同一个常量池地址。

4. HashMap 的基本结构

  • 基本结构

    • HashMap 是基于数组 + 链表 + 红黑树实现的。
    • 通过哈希函数将键值对映射到数组的某个索引位置,数组存储的是链表(JDK1.8 之前)或红黑树(JDK1.8 之后)。
    • 当链表长度超过阈值(默认 8)时,会转化为红黑树,优化查询效率。
  • 存储原理

    • 调用 hashCode 方法计算键的哈希值,再通过 (n - 1) & hash 计算出数组索引。
    • 解决哈希冲突的方法是拉链法。

5. HashMap 是否可以存放空值?频繁 put null 会怎样?

  • 可以放空值

    • null 只会被映射到数组的第一个位置(索引 0)。
    • null 无限制,可以存储任意多个。
  • 频繁 put null

    • 如果频繁对 nullput 值,只会覆盖之前的值,不会产生冲突或其他问题。

6. ArrayListLinkedList 的区别

  • 主要区别

    • ArrayList 是基于动态数组实现,随机访问性能更好,但插入和删除元素(尤其中间位置)性能较差。
    • LinkedList 是基于双向链表实现,插入和删除性能较好,但随机访问性能较差
  • 线程安全性

    • 都不是线程安全的。如果需要线程安全,可以使用 Collections.synchronizedListCopyOnWriteArrayList

7. 多线程下访问一个 List 应该使用哪一个?

  • 推荐使用 CopyOnWriteArrayList
    • 线程安全,适合读多写少的场景。
    • 采用写时复制的策略,每次修改都会创建一个新的副本。

8. concurrent 包的了解及实际应用

  • 常见组件

    • ConcurrentHashMap:线程安全的哈希表,分段锁机制提高并发性能。
    • CopyOnWriteArrayList:适合读多写少的场景。
    • BlockingQueue:线程安全的队列,用于生产者-消费者模型。
    • ExecutorService:线程池框架。
  • 实际应用

    • 线程池(ThreadPoolExecutor)处理高并发任务。
    • 使用 ConcurrentHashMap 作为缓存。
    • 使用 BlockingQueue 实现任务队列。

9. 线程池的默认拒绝策略

  • 默认拒绝策略是 AbortPolicy
    • 当线程池满了并且队列也满了时,会抛出 RejectedExecutionException

10. FixedThreadPool 的默认队列长度及潜在问题

  • 默认使用 LinkedBlockingQueue,队列长度为无界
  • 问题
    • 如果任务量过大,队列可能无限增长,导致内存溢出(OOM)。

11. 如何完成线程的并行等待?

  • 使用 CountDownLatchCyclicBarrier
    • CountDownLatch:用于等待一组线程完成任务。
    • CyclicBarrier:用于一组线程相互等待,直到全部到达屏障点。

12. 字节流和字符流的区别

  • 字节流:以 字节 为单位(InputStreamOutputStream)。
  • 字符流:以 字符 为单位(ReaderWriter)。
  • 区别
    • 字节流适合处理二进制数据(如图片、视频)。
    • 字符流适合处理文本数据,支持编码转换。

13. 如何实现断点续传?

  • 实现步骤

    1. 文件切分:根据分块大小切分文件,记录每个分块的开始和结束位置。
    2. 记录断点信息:使用文件或数据库记录已传输的分块位置。
    3. 多线程下载:通过多线程并发下载分块。
    4. 合并文件:下载完成后,将分块拼接为完整文件。
  • 关键技术RandomAccessFile 读取和写入指定文件位置。


14. Spring 的相关问题

  • Spring 的基本特性

    • 提供了 IOC(控制反转)和 AOP(面向切面编程)机制。
    • 管理单例 Bean,支持事务管理和多种集成。
  • Spring 如何解决循环依赖

    • 通过 三级缓存(单例池、提前暴露的对象和正在创建中的 Bean)实现。
    • 在实例化和初始化过程中暴露一个早期代理对象。
  • Spring 的事务管理

    • 使用 @Transactional 注解声明事务,基于 AOP 实现事务控制。
    • 默认传播机制是 REQUIRED
  • @Transactional 注解失效原因

    1. 方法未被代理(如同类方法调用)。
    2. 事务配置错误。
    3. 事务注解未生效。
  • 事务传播机制

    • 例如:可以使用 REQUIRES_NEW 创建一个新事务,独立于外部事务。

15. 数据库隔离级别

  • 隔离级别(由低到高):
    1. READ UNCOMMITTED:可能出现脏读。
    2. READ COMMITTED:避免脏读,但可能出现不可重复读。
    3. REPEATABLE READ:避免脏读和不可重复读,但可能出现幻读。
    4. SERIALIZABLE:完全避免并发问题,但性能较差。
;