面试复习(Java)
线程池
- 线程池的原因
- 不断的创建和删除线程,会带来较大的系统资源负载
- 线程缺乏统一的管理,可能会出现无限制的创建线程
- 线程之间抢占资源
- 线程池的属性
- 核心线程数:保持存在的线程数量,这些线程会一直存在,不会被删除
- 任务缓冲队列:当所有的核心线程都在运行时,新的任务会被加入到缓冲队列中
- 非核心线程数:当任务缓冲队列满后,将会创建新的线程来执行队列中的任务,且额外创建的线程数不会超过非核心线程数
- 空闲线程的存活时间:当非核心线程空闲时,且持续了一段时间后,此线程将会被删除
- 拒绝策略:当非核心线程和任务缓存队列满后,对待新的任务的策略
- Java 默认的线程池类型
名称 | 核心线程数 | 线程池大小 | 非核心线程存活时间 | 等待队列大小 |
---|---|---|---|---|
CachedThreadPool | 0 | $\infty$ | 60s | 0 |
SingleThreadExecutor | 1 | 1 | 0 | $\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
- G1 收集器
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
则可以由类进行重写,使得其满足正常的比较关系。若不进行重写,则与==
等价
equals
和hashCode
为什么需要同时进行重写hashCode
在Object
类下的默认行为是将此值的地址取出作为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/