面试复习(Java)

线程池

  • 线程池的原因
    • 不断的创建和删除线程,会带来较大的系统资源负载
    • 线程缺乏统一的管理,可能会出现无限制的创建线程
    • 线程之间抢占资源
  • 线程池的属性
    • 核心线程数:保持存在的线程数量,这些线程会一直存在,不会被删除
    • 任务缓冲队列:当所有的核心线程都在运行时,新的任务会被加入到缓冲队列中
    • 非核心线程数:当任务缓冲队列满后,将会创建新的线程来执行队列中的任务,且额外创建的线程数不会超过非核心线程数
    • 空闲线程的存活时间:当非核心线程空闲时,且持续了一段时间后,此线程将会被删除
    • 拒绝策略:当非核心线程和任务缓存队列满后,对待新的任务的策略
  • Java 默认的线程池类型
名称核心线程数线程池大小非核心线程存活时间等待队列大小
CachedThreadPool0$\infty$60s0
SingleThreadExecutor110$\infty$
FixedThreadPool$n$$n$0$\infty$
ScheduledThreadPoolExecutor
  • Java 默认的线程池有什么问题,为什么会引起 OOM 异常(OutOfMemoryError)
    • CachedThreadPool 允许创建无线的线程,从而引起 OOM 异常
    • SingleThreadExecutor 和 FixedThreadPool 请求队列为无限长,可能会堆积大量的消息,从而引发 OOM 异常

Java 内存

  • 内存模型
    • 主内存:线程之间共享的变量储存在主内存中
    • 本地内存:每个线程独立拥有的内存
    • 本地内存保存的是主内存的共享变量的副本
  • 垃圾回收
    • 根搜索算法(可达性分析算法)从 GC ROOT 节点沿着引用链搜索,无法到达的节点即为不可到达的对象
  • 垃圾回收器
    • G1 收集器
      • 独特的分代垃圾回收器,分代GC:分代收集器,同时兼顾年轻代和老年代
      • 使用分区算法,不要求eden,年轻代或老年代的空间都连续
      • 并行性:回收期间,可由多个线程同时工作,有效利用多核cpu资源
      • 空间整理:回收过程中,会进行适当对象移动,减少空间碎片
      • 可预见性:G1可选取部分区域进行回收,可以缩小回收范围,减少全局停顿
    • G1 收集器的过程
      • 初始标记(它标记了从GC Root开始直接可达的对象)Stop-The-World
      • 并发标记(从GC Roots开始对堆中对象进行可达性分析,找出存活对象)
      • 最终标记(标记那些在并发标记阶段发生变化的对象,将被回收)Stop-The-World
      • 筛选回收(首先对各个Regin的回收价值和成本进行排序,根据用户所期待的GC停顿时间指定回收计划,回收一部分Region)Stop-The-World

Map

  • hashMap 的结构
    • 采用链地址法,当发生哈希冲突时使用链表解决
    • 当链表过长时,在 JDK 1.8 下采用红黑树代替链表,当数据量较少时,转回链表
    • 当存储的数据量超过一个阈值后,hashMap 的哈希表长度将会扩容到原来的两倍,然后将所有的数据重新分配到新的内存中
  • hashMap 的这样扩容的理由
    • 通过恰好两倍扩容,可以让原来在第 $i$ 个链表的值被恰好分配到第 $i$ 和第 $2i$ 个链表的位置
    • 每一个值,只需要判断其哈希值在某个二进制位上的值即可直接完成分配
  • treeMap 的结构
    • treeMap 是一棵红黑树

设计模式

  • 设计原则
    • 开闭原则:对扩展开放,对修改关闭
    • 里氏替换原则:子类必须拥有所有的父类功能,子类可以开发自己的新功能
    • 依赖倒置原则:高层实现不能依赖于低层实现,而是依赖于低层的抽象类
    • 单一职责原则:一个类应当只负责一个职责
    • 接口隔离原则:接口应该更小更具体,而不是去实现很庞大的接口来适应所有的需求
    • 迪米特法则:避免与无关实体进行通信
    • 合成复用原则:尽量先使用组合或者聚合等关联,其次考虑继承
  • 设计模式
    • 创建型模式
      • 单例模式:限制一个类只能有一个实例
      • 原型模式:以一个此类型的实例为模板,通过拷贝内存中的二进制值来直接创建一个对象
      • 工厂模式:将创建对象的过程由另一个类进行封装
      • 建造者模式:将一个复杂的对象分为多个简单的对象的组合,并实现将多个小对象进行拼装的过程
    • 结构型模式
      • 代理模式:使得两个对象之间不能直接引用访问,只能通过第三方,可以保护目标对象,扩展目标对象的功能
      • 适配器模式:将一个类的接口转换为另外一个类的接口,通常是为了适配两个接口的对接问题
      • 桥接模式:将抽象与实现分离,使得他们可以独立变化,用组合关系来代替继承关系
      • 装饰器模式:为类增加新的功能的同时,避免了继承
      • 外观模式:隐藏系统的复杂性
      • 享元模式:重复使用已经创建的同类对象
      • 组合模式
    • 行为型模式
      • 模板方法模式:仅实现一个操作中的骨干步骤,具体步骤由其子类实现
      • 策略模式
      • 命令模式
      • 责任链模式
      • 状态模式
      • 观察者模式
      • 中介者模式
      • 迭代器模式
      • 访问者模式
      • 备忘录模式
      • 解释器模式

String

  • String 类型
    • String 类型是不可变的,对 String 类型进行操作的时,会重新生成新的 String 对象
  • StringBuilder 和 StringBuffer
    • StringBuilder 和 StringBuffer 都是可变的
    • StringBuilder 没有线程同步,存在线程安全问题,但是其效率略高于 StringBuffer
    • StringBuffer 能够保证线程安全,但效率较低

接口和抽象类

  • 接口和抽象类的区别
    • 抽象类可以写非抽象方法,而接口类只能有抽象方法
    • 一个类只能继承自一个抽象类,而一个类能实现多个接口
    • 继承是一个“是不是”的关系,而 接口 实现则是“有没有”的关系

构造函数

  • 子类实例化总是默认调用父类的无参构造方法
    • 为了让父类初始化属性和方法

equals

  • equals== 的区别
    • == 对于基本类型时,比较的是两个对象的值是否相同,而对于对象时,则比较的是这两个引用是否指向了同一个对象
    • equals 则可以由类进行重写,使得其满足正常的比较关系。若不进行重写,则与 == 等价
  • equalshashCode 为什么需要同时进行重写
    • hashCodeObject 类下的默认行为是将此值的地址取出作为 hashCode,但这与 hashCode 本意不同,hashCode 的值应当满足对于 $\forall x$ 若 x.equals(y) = true,则 x.hashCode() = y.hashCode()。所以当重写 equals 时,通常意味着这个值的相等概念与 == 不同,所以必然需要重写 hashCode 避免在 hashMap 中出现意料之外的情况

面试复习(Java)
https://blog.mauve.icu/2021/02/25/interview/java/
作者
Shiroha
发布于
2021年2月25日
许可协议