常用的设计模式
有单例模式,工厂模式,代理模式,构造者模式,责任链模式,适配器模式,观察者模式等.
单例模式
线程安全实现的常见三种方法:
- 静态初始化(饿汉).不管是否使用都会创建
- 双重检查(懒汉).单例变量必须要用 volatile 修饰.(注意内存可见性引起的并发问题)
- 单例注册表.spring 中 bean 的单例模式就是用该方法实现.
// 双重检查锁的单例模式
public class Singleton {
private volatile static Singleton uniqueSingleton;
private Singleton() {
}
public Singleton getInstance() {
if (null == uniqueSingleton) {
synchronized (Singleton.class) {
if (null == uniqueSingleton) {
uniqueSingleton = new Singleton();
}
}
}
return uniqueSingleton;
}
}
工厂模式
这个是创建不同类型实例常用的方式。
例如 Spring 中的各种 Bean 是由不同的工厂类进行创建的
代理模式
- 主要用在不适合或不能直接引用另一个对象的场景。
- 可以用代理模式对被代理的队象进行访问行为的控制。
- Java 的代理模式分为静态代理和动态代理
- 静态代理是指在编译时就创建好的代理类。例如我们在源代码中编写的类
- 动态代理指在 JVM 运行过程中动态创建的代理类
例如,在 Mybatis 中 getMapper 时会通过 MapperProxyFactory 及配置文件动态生成的 Mapper 代理对象,代理对象会拦截 Mapper 接口的方法调用,创建对应方法的 MapperMethod 类并执行 execute 方法,然后返回结果.
(使服务看上去就像是在使用本地的方法)
责任链模式
有点像工厂的流水线,链上的某一个节点完成对对象的某一种处理
Netty 框架在处理消息时,使用的 Pipeline 就是一种责任链模式
适配器模式
类似于转接头,把两种不适配的对象进行适配,也可以起到对两个不同的对象进行解耦的作用。
日志处理框架 SLF4J,可以跟 Log4j、logback 这种具体的日志实现框架进行解藕,通过不同适配器将 SLF4J 与 Log4j 等不同框架进行适配,完成日志功能的使用。
观察者模式
也可称为发布订阅模式,适用于一个对象的某个行为需要触发一系列事件的场景。
GRPC 中 stream 流式请求的处理就是通过观察者模式来实现的
构造者模式
适用于一个对象有很多复杂的属性,需要根据不同情况来创建不同具体的对象
创建 Protocol Buffer 对象时,需要用到 Builder 方式
Java 语言特性
动态代理与反射
- 是 java 语言的特色,需要掌握动态代理与反射的使用场景
- ORM 框架中会大量使用代理类
- PRC 调用时使用反射机制调用实现类的方法
Java 常见数据类型
面试的常见问题:
- 每种数据类型占用多大空间
(1.byte boolean 2.char short 4.int float 8.double) - 数据类型的自动转换与强制转换
- 基础数据类型与 Wrapper 数据类型的自动装箱与拆箱等
Java 对对象的引用
分为强引用、软引用、弱引用、虚引用 4 种。
这些引用在 GC 时的处理策略不同?
- 强引用不会被 GC 回收
- 软引用在内存空间不足时会被 GC 回收
- 弱引用在每次 GC 时都会被 GC 回收
- 虚引用必须和引用队列联合使用,主要用于跟踪一个对象被垃圾回收的过程
Java 的异常处理机制
就是 try/catch/finally 机制
需要知道在异常时在 try、catch 中的处理流程
需要了解 Error 和 Exception 的区别
HashMap 与 ConcurrentHashMap
Map 的实现这个题目,能够考查到数据结构、Java 基础实现以及对并发问题的处理思路的掌握程度。
HashMap
-
HashMap 的实现
简单说,Java 的 HashMap 就是数组 + 链表实现的
数组中的每一项都是一个链表
通过计算存入对象的 HashCode,来计算对象在数组中要存入的位置,用链表来解决散列冲突,链表中的节点存储的是键值对。 -
填充因子的作用
-
Map 扩容的 rehash 机制
-
需要知道它的容量是二的幂次方
是为了可以通过按位与操作来计算余数,比求模要快
-
HashMap 是非线程安全的
在多线程 put 的情况下,有可能容量在超过填充因子时进行 rehash。因为 HashMap 为了避免尾部遍历,在链表的插入时使用的是头插法,多线程的场景下,可能会产生死循环。
ConcurrentHashMap
会从非线程安全的 HashMap 就会自然的跳转到线程安全的 ConcurrentHashMap
- 采用分段锁的思想来降低并发场景下的锁定发生频率
- 在 jdk1.7 和 jdk1.8 中的实现差异非常大
- 1.7 中采用 segment 分段加锁,降低并发锁定程度
- 1.8 中采用 CAS 自旋锁(一种乐观锁实现模式)提高性能.但在并发度较高时,性能一般.
- 1.8ConcurrentHashMap 引入红黑树,用来解决 hash 冲突时的链表顺序查找问题。
- 红黑树的启用条件与链表的长度和 map 的总容量有关。默认是链表大于 8,且容量大于 64 时转为红黑树方式。建立阅读源码来进行学习
Java 版本
jdk1.8 和 jdk1.11 是长期支持版本
-
jdk1.8
- Lambda 表达式
- StreamAPI
- 方法引用
- 接口默认方法
- (对方法区进行了调整)Metaspace 替换 PermGen
(Metaspace 与 PermGen 的最大区别在于:Metaspace 并不在虚拟机中,而是使用本地内存
替换的目地:1.提升对源数据的处理、提升 GC 效率 2.方便后续 Hotpot 与 gearotic 合并)
-
jdk1.9-1.10
- 模块系统
- 默认 G1 回收器
- 接口私有方法
- 局部变量判断
- Graal 编译器
-
jdk1.11
- ZGC(最激动人心的,新的 gc 回收器)
- 为大内存堆设计,能够实现 10ms 以下的 gc 停顿
- 字符串 API 增强(提供了字符复制等功能)
- 内建 HTTP Client
- ZGC(最激动人心的,新的 gc 回收器)
面试考察点
-
基本概念和基本原理
要求:正确清晰
- 网络协议 4/7 层模型的概念
- TCP 协议流量控制的实现原理
- 等
-
实现方法和使用方式
- HashMap 在 JDK1.8 中的实现方式
- 单例模式有哪几种实现方式,什么场景该使用静态方法实现,什么场景该使用双检锁实现
- 等
-
经常用到的知识点
- 常用的 Linux 命令有哪些,用来解决什么样的问题
- 等
-
实际应用中容易犯错的点
- ==与 equals 区别是什么
- 对象强引用使用不当会导致内存泄露,考察不同引用方式和作用的理解
- 等
-
与面试方向相关的知识点
- 中间件:存储,网络相关的考察
- 等
加分项
-
知识点与典型的业务场景关联.
如,谈到设计模型时,可以讲 XX 框架在解决 XX 问题时使用了那种设计模式.
-
以反例来描述实际场景中误用的危害.
如,大量使用反射会影响性能. -
与知识点相关的优化点.
如,讲到 tcp 建连和断连时,如遇到洪水攻击或大量 TIME_WAIT 时,可以调整系统参数预防.
-
与知识点相关的最新技术趋势.
如,讲到 ConcurrentHashMap,可以介绍 1.8 的改进细节.
或,讲到 HTTP 时,能说出 HTTP2 和 QUIC 的特点和实现.
-
在了解的前提下,尽量增加回答内容深度.
如,讲到 tcp 的滑动窗口时,能讲到流量与拥塞控制,进一步能指出解决拥塞的不同算法.
真题汇总-1
-
进程和线程的区别和联系
从资源占用,切换效率,通信方式等方面解答
-
简单介绍一下进程的切换过程
线程上下文的切换代价,要回答,切换会保存寄存器,栈等线程相关的现场,需要由用户态切换到内核态,可以用 vmstat 命令查看线程上下文的切换状况
-
你经常使用哪些 Linux 命令,主要用来解决哪些问题?
参考之前操作系统汇总中提到的命令
-
为什么 TCP 建连需要 3 次握手而断连需要 4 次?
参考之前内容 -
为什么 TCP 关闭链接时需要 TIME_WAIT 状态,为什么要等 2MSL?
参考之前内容
-
一次完整的 HTTP 请求过程是怎样的?
DNS 解析,TCP 建连,HTTP 请求,HTTP 响应等.
实际回答时可以画一个简单的交互图 -
HTTP2 和 HTTP 的区别有哪些?
-
在你的项目中你使用过哪些设计模式?主要用来解决哪些问题?
-
Object 中的 equals 和 hashcode 的作用分别是什么?
-
final,finally,finalize 的区别与使用场景
-
简单表述一下 Java 的异常机制
-
线上上使用的那个版本 jdk,为什么使用这个版本(有什么特色)?
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于