导图社区 java面试
java面试中经常被问到的问题和常见知识点
编辑于2020-06-22 12:51:18java面试
Java基础
基本类型
集合
map
list
列表转制
列表去重
java中的list实现你有那些,各自的实现原理是什么?使用场景是什么
ArrayLIst 数组实现,查询快,增删慢,轻量级(线程不安全)
数组扩容策略
添加时将判断内部数据是否需要扩容 1. 获取最小要满足的大小 2.
LinkedLIst 双向循环列表,增删快,查询慢(线程不安全)
实现原理
1. 定义了Node节点,每个节点前后节点,构建为双向列表的节点
2. LinkedList 有三个全局变量,分别是 size first last 用于记录列表的长度和起始点级结束点
3. add方法 默认是向后添加,如果需要先前可以用addFirst
4. get方法,通过节点后移定位,大于size的时候会进行从头开始查询,应为LInkedList是双向循环列表,但因为前置做了 index的判断,也会出现数组下标越界的错误
作用
实现栈
实现队列
实现双向队列
Vector 数组实现,重量级(线程安全,使用少)
set
线程
线程之间的通讯
如何让线程按顺序运行
join
让main线程wait,子线程顺序start之后 join
使用syncrhoized、wait/notifyAll
使用大小为1的线程池,依次submit
创建线程的方式
继承Thread
实现Runnable
实现Callable接口
线程8种状态
NEW
刚创建
RUNNABLE
可运行 NEW->t.start() ,或者拿到锁
RUNNING
运行中 RUNNABLE拿到时间片
BLOCKed
阻塞状态
RUNNING->t.sleep() 等待io
等待池队列
running->t.wait()
锁池队列
可以去抢锁, 等待池->o.notifyAll()唤醒所有 ,o.notify()随机唤醒一个
DEAD
run()结束
多线程
JUC包
countDownLatch和circleBarrier
两者都可以用来协调线程,前者只能用一次,后者可以重新计数
volatile关键字
保证内存可见性,变量时总会返回最新写入的值
atomicInteger
原子操作
CAS无锁非阻塞算法
乐观锁
原子操作比较并替换,可能存在ABA问题,使用AtomicStampedReference解决,本质上类似版本号
CountDownLatch、circleBarrier
ReenTrantLock
并发容器,线程池基础
ConcurrentHashMap
线程安全,基于cas算法
kv都不能为null
1.7之前使用分段锁+数组+链表实现
1.8之后使用table数组+链表或红黑树, 单槽个数大于8用红黑树
rehash
负载因子0.75
扩容两倍,将链表i位置的元素映射到i+newsize/2和i位置
与hashmap类似
ArrayBlockingQueue
阻塞队列,队列满或者为空时添加或者获取元素会阻塞等待
LinkedBlockingQueue
ConcurrentLinkedQueue
Concurrent,线程安全,可多线程操作
AQS原理
AQS就是基于CLH队列,用volatile修饰共享变量state,线程通过CAS去改变状态符,成功则获取锁成功,失败则进入等待队列,等待被唤醒;释放时修改状态符并唤醒下一个等待的节点
Executor和线程池
如何优雅地关闭线程池
使用shutdown,关闭不再活动的线程,一定要确保任务里不会有永久阻塞等待的逻辑,否则线程池就关闭不了。
使用shutDownNow,需要在任务中捕捉异常中断
需要awaitTermination阻塞等待线程池关闭
threadPoolExecutor线程池的6个参数
corePoolSize 核心线程数、线程池最多有corePoolSize个线程工作
maximumPoolSize 最大线程数、线程池最多有maximumPoolSize个线程可运行;队列满了的时候,这个参数才有意义
keepAliveTime 空闲时间,超过keepAliveTime不工作将被停止
unit 空闲时间的单位,毫秒,秒
workQueue 工作队列,存放需要被线程池执行的线程队列
threadFactory 创建线程的工厂
handler 超过线程范围而使执行被阻塞时所使用的处理程序
线程池工作原理
1. 线程数量未达到 corePoolSize,则新建一个线程(核心线程)执行任务 2. 线程数量达到了 corePools,则将任务移入队列等待 3. 队列已满,新建线程(非核心线程)执行任务 4. 队列已满,总线程数又达到了 maximumPoolSize,就会由RejectedExecutionHandler抛出异常
线程池中的核心线程是如何被重复利用的
循环getTask,Worker就是Thread
锁(lock+synchronized)
lock 和 synchronized 之间的区别
lock 有那些实现(JDK)
reentrantlock、readwritelock
equals 与 == 的比较
Integer
equals 比较内容,== 智能比较 -128~127
String
equals 比较内容, == 比较地址值常量的是一样的,新建的地址不一样
对象
代理 动态 代理
树
平衡二叉树 如何快速打印
jvm
内存模型
线程独占区
程序计数器
native方法栈
本地方法栈与虚拟机栈十分相似,其为 native 方法服务
虚拟机栈
Java方法执行的内存模型
每个方法执行时都会创建一个栈帧用于存储局部变量,操作栈,动态链接,方法出口等信息,
每个方法被调用的过程,对应一个栈帧在虚拟机栈中入栈到出栈的过程
局部变量表,在编译器已经确定起内存大小
共享区
堆
堆中对象为所有线程共享,目的是存放对象实例。GC主要管理的就是堆,
方法区(永久代)
所有线程共享
常量池
1.7以后 hotspot 将运行时常量池从永久代移除
类加载信息
JMM通信机制
线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化
线程A向B通信,A把本地变量修改后刷新到主存中,B再把主存中的变量刷新到本地
垃圾回收
判断对象是否存活
引用计数法
可达性分析发
gcRoots
1、虚拟机栈中引用的对象 2、方法区类静态属性引用的对象 3、方法区常量池引用的对象 4、native方法栈JNI引用的对象
垃圾回收算法
标记-清除法
复制算法
标记-整理法
jvm采用分代收集算法,对不同的区域采用不同的算法
新生代 复制算法 eden:suvivor:suvivor2 8:1:1
老年代 标记清除/标记整理
垃圾收集器
年轻代收集器 Serial、ParNew、Parallel Scavenge
老年代收集器 Serial Old、Parallel Old、CMS收集器
特殊收集器 G1收集器[新型,不在年轻、老年代范畴内]
内存泄漏举例
数据库连接、网络连接建立后不使用没有close,连接对象不能被回收
Thread中有个ThreadLocalMap,ThreadLocal是弱引用,作为key,ThreadLocal被清理后,对应的value就无法可达,因此会内存泄漏,ThreadLocal get之后要尽快调用remove()
类加载
类加载器
双亲委托机制
bootstrap classLoader
extension classLoader
SystemClassLoader
自定义classLoader
java类加载过程
加载
类加载器加载class文件,
验证
验证class完整合法性
准备
设置类static变量默认值0
解析
连接
初始化
父类的static代码块->子类的static代码块->初始化父类的属性值/普通代码块(自上而下的顺序排列)->父类的构造方法->初始化子类的属性值/普通代码块(自上而下的顺序排列)->子类的构造方法
使用
卸载
问题定位
内存溢出
内存消耗过大如何查找
cpu消耗过大如何查找
数据库
mysql
基本类型
索引
索引类型
索引失效情况
事务隔离级别
读未提交
脏读x 可重复读x 幻读x
读已提交
脏读v 可重复读x 幻读x
可重复读
脏读v 可重复读v 幻读x
串行化
脏读v 可重复读v 幻读v
innoDB引擎
实现了标准的4种隔离级别
提供了行级锁级外键约束
适用于处理大容量数据
子主题
sql优化
优化工具
编码规约
架构
微服务架构
rpc
远程过程调用
通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议
组成
rpcclient
负责import远程接口的代理实现
rpcServer
子主题
rpcProxy
rpcInvoker
RpcProtocol
dubbo
zk挂掉之后,消费者和服务者还能继续通讯吗
可以,本地缓存
角色
provider
consumer
registry
容器
monitor
java spi原理
ServiceLoader加载
classpath 下面META-INF文件
dubbo spi原理
ExtensionLoader.getExtensionLoader(接口类).getAdaptiveExtension()获取自适应扩展
classpath下面META-INF/dubbo/文件夹下
内容为别名=实现类
不用入侵代码即可完成扩展
服务路由
注册中心路由
调整优先级
条件路由
host黑白名单
应用名
脚本路由规则
标签路由
负载均衡
随机
轮询
最少活跃
一致性hash
容错
failover
下一台重试,读操作
failFast
写操作
Failsafe
失败安全,只记录日志
Failback
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
forking
并发要求高
dubbo的详细启动过程
1、spring启动,解析xml或者注解
2、解析到dubbo标签,根据 spi原理,交给dubbo的beanParser处理,dubbo的service标签会生成一个ServiceBean
3、Spring容器加载完成后,会产生一个刷新事件,接收到刷新事件,ServiceBean开始执行export
4、进入ServiceConfig,将URL中的参数保存到map中,调用代理工厂获取代理类
5、通过代理工厂将ref对象,也就是实际提供服务的对象,转化为invoker对象
6、暴露服务protocol.export(),protocol将invoker转化为exporter,netty创建并启动服务nettyServer,打开端口
7、export调用完, 检查register参数,并注册服务到注册中心
dubbo详细调用过程
1、spring解析Reference标签生成referenceBean
2、referenceBean实现了FactoryBean接口,调用get()方法获取其实现类
3、创建invoker对象,多个注册中心或多个provider的URL合并到一个invoker中
4、使用javassist创建代理类,启动完成
5、代理类调用invoke,invoker中包含了服务降级逻辑
6、根据负载均衡策略获取一个invoker,
7、多个过滤器调用
8、序列化,编码生成dubbo消息体
9、netty客户端发送请求,
10、服务端收到消息,解码反序列化,派发到线程池执行
11、invoker执行invoke,javassist代理,找到实际服务类调用业务方法
12、服务端封装response,将消息返回给消费端,消费端接收并解析消息,请求完成
zookeeper 注册中心
dubbo与zookeeper的交互过程
启动时创建/dubbo/com.demoservie/provider、.../consumer、.../router、.../configuration持久化节点
/provider和/consumer下面创建多个临时节点
monitor订阅/com.demoservie、consumer订阅/providers、consumer注册到/consumers目录下
Consumer会一直维持着对Provider的ChildListener,监听Provider的实时数据信息。当Providers节点的子节点发生变化时,实时通知Dubbo,更新URL,同时更新Dubbo容器内的Consumer Invoker对象,
zookeeper Client与Server断开连接后,会定时的不断尝试重新连接,当连接成功后就会触发一个Event,Dubbo注册了CONNECTED状态的监听器,当连接成功后重新注册和订阅
zk宕机
Dubbo内部的zkClient会不停地尝试连接Server。当Zookeeper Server宕机了不影响Dubbo里已注册的组件的RPC调用,因为已经通过URL生成了Invoker对象,这些对象还在Dubbo容器内。当然因为注册中心宕机了,肯定不能感知到新的Provider。 因为在之前订阅获得的Provider信息已经持久化到本地文件,如果zookeeper注册中心不可用,会加载缓存在文件内的Provider信息,还是能保证服务的高可用
子主题
框架
Spring
Spring boot
Spring MVC
Spring Cloud
IOC
AOP
事物
事物传递机制
默认是:使用当前事物 如果没有则新建(0 REQUIRED)
1 SUPPORTS 支持当前事物,如果没有则不使用事物
2 MANDATORY 支持当前事物,如果没有则报错
3 REQUIRED_NEW 新建一个事物,挂起当前事物
4 NOT_SUPPORTED 以无事物的方式进行,如果有事物则挂起当前事物
5 NEVER 以无事物的方式进行,如果有事物则报错
6 NESTED 如果有事物则在当前事物嵌套一个事物,内部事物的回滚不影响当前事物,如果没有事物则与 REQUIRED 一样 新建事物(当前事物会影响子事物)
事物隔离级别
DEFAULT 默认使用数据库的隔离级别 ORACLE(读已提交) mysql(可重复读)
READ_UNCOMITTED 读未提交(脏读)最低的隔离级别,一切皆有可能。
READ_COMMITED 读已提交,ORACLE默认隔离级别,有幻读以及不可重复读风险。
REPEATABLE_READ 可重复读,解决不可重复读的隔离级别,但还是有幻读风险。
SERLALIZABLE 串行化,最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,这样就解决了脏读、不可重复读和幻读的问题了
事物的作用
1. 保证ACID 原子性、一致性、隔离性、持久性
事物的原理
其本质是基于数据库对事物的支持进行的操作,标记事物的地方会进行代理打开事物,完成后进行提交事物,spring的事物帮我们进行是事物的开启和事物的提交以及回滚的操作
spring bean的生命周期
参考链接:https://blog.csdn.net/w_linux/article/details/80086950
1. 实例化bean 通过构造方法,或工厂方法
2. 设置对象属性(setter)(依赖注入)
5. 将Bean实例传递给Bean的前置处理器
6. 调用bean的初始化方法,
7. 将Bean的实例传递给Bean的后置处理器
8. 使用bean
9. 容器关闭之前,调用bean的销毁方法
spring如何解决循环依赖
三个缓存依次调用,
singeltonObjects,初始化后加入
earlySingletonObjects,
objectFactorys,实例化后加入
为什么需要三个而不是两个
objectFactory可以通过后置处理器方便扩展,
spring的注入方式
构造方法注入
setter注入
属性注入
注解注入
xml注入
byType
byName
factoryBean和beanFactory区别
beanFactory
是spring核心接口
ioc容器核心接口
bean的工厂
实现类:DefaultListableBeanFacotry, applicationContext
factoryBean
由spring托管的一个工厂模式bean接口
由工厂类实现接口,泛型返回实际的bean,加&返回工厂bean
实现类:dubbo的ReferenceBean
mybatis
消息队列
原理
无阶队列
算法
排序
参考地址:https://www.cnblogs.com/Glory-D/p/7884525.html
冒泡排序
快速排序
选择排序
插入排序
希尔排序
归并排序
基数排序
堆排序
桶排序
查找
参考地址:https://www.cnblogs.com/lsqin/p/9342929.html
二分
顺序查找
插值查找
斐波那契查找
树表查找
二叉树查找
先、中、后序遍历
使用栈或递归
层次遍历
使用fifo队列
验证二叉树是否BST
中序遍历判断是否升序序列
平衡二叉树之2-3查找
平衡二叉树之红黑树查找
B树和B+树查找
分块查找
哈希查找