首先说明,笔者从毕业来摸爬滚打3年,一直在小厂工作,没有经历过太多高并发的项目,所以经验有限,一直都是自主学习的状态,最近用了3个月佛系面试了几家不大不小的厂,特地总结一下经验,好让后辈们少走弯路。
面试简历我就不多说了,总之一些精通,熟悉的词汇,大家注意下,别被面试官问到死,量力而行。。
首先都是从自我介绍开始,自我介绍的时候最好把自己的项目大概内容,所用技术说出来,最后如果有自己的博客或者github开源项目那更优。
然后面试官会从你的项目中开始问你流程,所用技术,如何实现,所以大家要对自己做过的项目,负责的模块要非常熟悉。
接着面试官开始问框架源码,中间会夹杂一些基础底层,最后会问一些加分项,比如大数据相关。至于算法,那只好祝大家平安了。。
以上是我自己的经验,我面试的厂子一般都是这个流程,当然肯定有不一样,所以大家根据面试官随机应变。
废话不多说,重点来了:
框架
问:说一下springmvc的流程吧
1.用户提交请求到前段控制器,也就是DispatcherServlet
2.前端控制器寻找一个或者多个handlermapping,返回指定的controller
3.前端控制器调用指定的controller
4.controller调用业务层方法,返回到modelandview
5.前端控制器再寻找一个或者多个视图解析器View Resolver,返回对应的视图view
6.前端控制器在再根据view,进行试图渲染,返回给客户端
这块大家要自己理解,不要死记硬背,要熟悉各自流程,用自己语言概括
问:spring的核心是什么
1.IOC,控制反转,DI依赖注入
2.AOP,面向切面编程
问:控制什么,反转什么?
1.控制反转是一种思想,控制对象的创建,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
2.反转是指依赖对象的过程被反转,也就是依赖的对象是被动的,控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入
这个问题真的很刁钻,所以笔者的理解也只能达到这个程度
问:spring的动态代理有哪几种
1.jdk自带的代理,只能代理接口
2.CGLib,通过继承实现的,它也是动态的创建一个类,但是代理的是这个类的子类
问:spring的作用域
1.singleton作用域(scope 默认值)
2.prototype
3.request
4.session
5.全局session也就是global session
问:怎么修改bean作用域
1.xml的话,直接在bean后面加scope属性
2.springboot的话,在bean上加上注解@scope
问:spring中bean的生命周期知道么?
1.实例化一个Bean,也就是我们通常说的new
2.然后注入到IOC中
3.根据配置,进行相应属性的设置
4.然后执行init的方法
5.执行业务处理
6.执行destroy方法销毁
这块有一个很详细的流程,笔者也记得大概,差不多是这些流程,具体的大家可以自行百度
问:BeanFactory和Factorybean区别
1.BeanFactory是管理Bean的工厂,是ioc的核心接口
2.Factorybean是一个接口,实现了FactoryBean的bean,本质还是bean
问:springcloud用过么?
具体工作中没有用过,在家搭过一个玩玩。
1.用Eureka做负载均衡
2.Feign调用相关接口
3.Hystrix熔断器做降级处理
4.LCN框架做分布式事物处理
因为这块笔者确实没有太多使用经验,所以面试官也没有太多深问
问:springcloud和dubbo有什么区别
1.cloud是spring的;dubbo是阿里的
2.cloud的协议底层还是http;dubbo默认是自己的dubbo协议,当然也有其他的,比如hessian,webservice等等
3.cloud是微服务,dubbo本质是rpc
4.注册中心不同,cloud有Eureka,当然也可以用其他的,比如zk,Consul;dubbo用的zk,redis
5.cloud本质调用的http协议,所以调用的是controller层方法;dubbo调用的service中的方法
问:分布式和微服务有什么不同?
1.分布式表示有很多服务器,比如A,B,C,其中一个挂了,那么整个分布式就瘫痪了。
2.微服务不一样,微服务是对业务粒度更细微的拆分,ABC,挂掉一个,不会导致整个瘫痪,整体还是可以运行的
这个问题真的全凭自己理解,我的程度只能如此。。
问:你说你用了分布式事物?了解哪些?知道seata么?
1.分布式这块有很多解决方案,我先说我了解的,比如XA方案,TCC方案,还有一些框架,我了解的好像就只有LCN,至于你所说的seata,它是阿里提供的,其他我就不太清楚了
2.XA方案就是指两段提交协议,有一个全局的事物管理器协调多个事物,比如有AB两个数据库,事物管理器会先问各个数据库情况,如果两个数据库都回复ok,则一起提交事物,任何一个库回答不ok,就回滚。具体实现比如springJTA
3.TCC方案比较复杂,大多数依赖自己写代码来回滚和补偿,一般和钱有关的强相关事物要用TCC方案
4.LCN是指网上的一个开源项目,LCN模式是通过代理Connection的方式实现对本地事务的操作,然后在由TxManager统一协调控制事务。当本地事务提交回滚或者关闭连接时将会执行假操作,该代理的连接将由LCN连接池管理。
分布式事务确实是一个难点。笔者也只是使用过JTA,LCN,了解个大概,没有具体深入研究。
Redis
问:redis有些数据类型,你用过哪些?具体干了什么?
1.string,set,zset,list,hash
2.除了set和zset很少用,其他基本都用过
3.string可以存储数据,可以实现分布式锁
4.hash,我一般都用来存储对象,实现过购物车之类的
5.list实现过消息推送,但也很少用
真的别说只用string,那真的太low了,好歹说下hash,效率比string高多了,当然,既然说string,一定要说分布式锁,加加加分项。
问:分布式锁?那你怎么实现的?
1.加锁的时候用setnx命令,同时要加上过期时间,过期时间根据自己业务复杂度来定义
2.解锁的时候,最好在finally代码块解锁,也就是delete操作,我在项目中用了lua脚本去删除,为了原子性
问:lua脚本?你如何优化的?
1.首先lua脚本本来效率就很高
2.如果真的要提高效率的话,可以多用用local属性
问:其他的呢?
1.不太清楚,这方面我没系统学习过。
问:如果我在锁的时候,时间过期了怎么办?
1.可以开一个线程,反复加锁,也就是自旋锁,加锁的时间可以设置为一开始时间的三分之一
2.网上也有针对redis分布式锁的jar,叫redisson,可以直接用这个第三方,底层实现就是我说的那样
redis的分布式锁真的是重中之重,一定要学会。至于为啥时间是三分之一,我也不清楚,但是redisson底层确实是如此
问:了解过的redis的持久化么?
1.RDB和AOF,也就是快照和日志
问:那你解释下各自区别
1.RDB是redis默认方案,会形成一个dump.rdb文件。redis重启的时候,会通过加载dump文件恢复数据
RDB分为两种持久化方式1.同步save2.异步bgsave;
他保存的同时,使用了LZF算法压缩RDB文件,这个算法默认开启,消耗一定CPU;
RDB文件紧凑,适合备份,加载速度快于AOF,但是无法妙级持久化,而且老版的redis不兼容RDB
2.AOF采用的是重写机制
他先命令追加到AOF缓冲区,缓冲区内容写到AOF文件里,再将AOF文件保存磁盘
Redis重启的时候,会更具AOF日志文件内容将里面的写的指令从前往后执行一次,完成数据恢复
这几乎必问
问:redis多线程单线程?
单线程
问:那他为什么那么快?
1.纯内存操作
2.单线程操作,避免了频繁的cpu上下文切换
3.采用了非阻塞的IO多路复用机制
可以停止装逼了,再继续说,估计面试官就要让你手写redis了。。
问:了解他的过期策略么?
1.他有定期删除策略,惰性删除策略,内存淘汰机制
2.定期策略是指,redis默认每隔100毫秒随机抽取一些过期的key,检查是否过期,过期就删除
3.惰性删除是指获取某个key时,redis会检查一下这个key是否过期,过期就删除,不返回任何东西
4.还有内存淘汰机制,里面有很多算法,这块我不是特别了解
问:你们公司怎么解决缓存雪崩,缓存穿透的?
1.雪崩可以在过期时间加上一个随机值,或者用高可用的架构,如果发生雪崩,只能重启redis,快速恢复缓存数据
2.穿透,直接用bloomfilter布隆过滤器或者可以将空对象设置到缓存,设置一个较短的过期时间
缓存雪崩,穿透,击穿,预热,降级,大家最好先看看理解理解
有的公司会问你用了什么架构的redis,无非主从,哨兵,集群,大家可以自己预先了解下,特别是一些大公司,都会问些集群,笔者以前都用主从,所以面试官也没多问。
kafka
问:你们项目中用了kafka具体干了什么?
消息同步
问:还有呢?
其他没了,毕竟小公司,kafka的削封,异步,解偶,只用到了异步,两个客户端同步数据
这个问题大家可以根据自己项目实际回答
问:那你为什么不用activemq?如果只是同步数据。active就够了把,为啥还用kafka
1.所在的公司比较封闭,技术的选择不是我决定的,而且,我们引进一个新的技术框架,要走很多流程,所以没得选
我记得我是这样回答的,但是事后我感觉他在问我activemq和kafka的区别,我总结了下:
1.activemq的单机吞吐量不如kafka
2.kafka内部分区,有多个副本,active内部不分区
3.active有较低的概率会导致数据丢失,kafka经过参数优化,可以0丢失
4.kafka的功能更简单,而且社区更庞大,技术更成熟。在实时计算,日志采集被大规模使用过
还有其他的,大家可以自己看看
问:kafka如何保证数据不丢失?
可以分为3个角度去思考
1.生产者数据不丢失
1.kafka有自己的ack机制,每次生产者给broker发送消息的时候,都会有一个确认反馈机制,也就是ack机制,有的写法是acks,这个机制确保消息能被正常接收到,他有3个状态0,1,-1或者0,1,all
2.0:意味着生产者不等待broker同步完成确认,继续发送下一批
3.1生产者要等待leader成功收到数据得到确认,才发送下一批
4.-1或者all,生产者要得到所有的follower确认,才发送下一批数据
2.消费者数据不丢失
kafka内部有offset机制,相当于id,可以通过offset commit来保证数据不丢失,kafka自己记录了每次消费
offset的值,再次继续消费时,会接上策的offset进行消费
3.broker数据不丢失
每隔broker中都会分主题,每隔主题会分区,区里会有我们设置的副本也就是replication的个数,当生产者写
数据的时候,会根据分发策略写到leader中,follower在根据leader同步备份,可以设置一个leader多个
follower,这样一个leader挂掉,可以选举一个follower作为leader,保证消息数据不丢失
4.为了防止数据丢失,保险起见可以用定时任务定期去处理,比如quartz,分布式可以用xxl-job
问:那你知道如何保证kafka的幂等性么?
生产者幂等:
* 只需要增加以下配置即可:enable.idempotence=true
消费者幂等结合业务来思考,几个思路:
* 比如你拿个数据要写库,你先根据主键查一下,如果这数据都有了,你就别插入了,update 一下好吧。
* 比如你是写 Redis,那没问题了,反正每次都是 set,天然幂等性。
* 比如你不是上面两个场景,那做的稍微复杂一点,你需要让生产者发送每条数据的时候,里面加一个全局唯一的 id,类似订单 id 之类的东西,然后你这里消费到了之后,先根据这个 id 去比如 Redis 里查一下,之前消费过吗?如果没有消费过,你就处理,然后这个 id 写 Redis。如果消费过了,那你就别处理了,保证别重复处理相同的消息即可。
* 比如基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了,重复数据插入只会报错,不会导致数据库中出现脏数据。
这块kafka的流程,大家要自己好好看看,在总结
zk
问:zookeeper了解么?
1.了解的不多。kafka的时候用过
问:zookeeper有哪些类型的节点
1.持久节点
2.持久顺序节点
3.临时节点
4.临时顺序节点
问:zk你除了跑kafka还干了什么
1.试过zk的分布式锁,其他没了
zk这块笔者也只是了解,用的真不多。。所以面试官也没多问
jvm
问:你说你项目jvm调优了,都怎么用的
1.我项目经常调用jni,所以和c打交道比较多,C有指针的说法,java没有,所有经常出现堆栈益处的问题
2.主要改了一些堆栈大小,比如-xms:改堆的最小值,-xss:栈的最大值,-xmn:新生代大小
注意,装逼来了,笔者在jvm这块研究的满深入的,所以故意往jvm深层次引,当你说出新生代,老年代的时候,面试官就知道你不是泛泛之辈了。
问:那你说下jvm的内存模型
栈,堆,方法区,程序计数器,本地方法栈
问:堆里面的结构了解么
堆分为新生代包括伊甸园eden,s0,s1;还有老年代,永久代
问:jdk1.8和1.7JVM有什么不同
取消了永久代,用元数据区代替
问:说几个垃圾回收器吧
以oracle的hotspot,jdk1.7为例子
新生代:serial GC;parNew GC; Parallel scavenge GC;
老年代:CMS GC;Serial old GC;Parallel old GC;
还有1.7新加入的G1 GC新老同吃
1.7以后还加入zGC,Shenandoah GC
这块最好别引火上身,如果要你说出每个GC的特点。。真的要下一番功夫
问:说一下你jvm怎么调优的
我一般根据图形化界面jvisualvm来查看堆栈情况
根据自己的项目挑选合适的GC
然后根据堆栈情况,来调节
比如更改GC命令用-xx:+UseSerialGC启动serialGC
-xx:+UseConcMarkSweepGC启动parNewGC
-Xms:初始堆大小
-Xmx:最大堆大小
-XX:NewRatio调节年轻代和老年代壁纸
-XX:MaxPermSize调节最大方法区内存
关于jvm这块真的不介意大家说精通,熟悉,我也只是写了解,如果面试官问每个GC的具体算法,那真的GG了
问:类加载过程知道么
1.加载:把 .class字节码文件加载到堆中
2.验证:格式验证,元数据验证,字节码验证,符号验证
3.准备:就是分配一个数据结构来存储类的信息
4.解析:虚拟机将常量的符号引用转换成直接引用的过程,把引用指向分配的内存地址
5.初始化:执行类中定义的java代码
6.使用
7.卸载
问:了解双亲委派模型么
依次往上是自定义类加载器,应用程序类加载器Application classloader,扩展类加载器Extension classloader,启动类加载器bootstrap classloader
了解jvm模型,大家最好也要了解jmm模型,这个jmm,面试官没问我
java基础
问:你经常用的集合有哪些?
arraylist和linkedArraylist比较多
问:知道arraylist的底层实现么?
arraylis底层数组实现,查询效率高,有序可重复
问:那他的扩容机制呢?
初始10,扩容是以前的1.5倍
那我要用插入效率高的集合用什么?
LinkedaArraylist
问:为什么?
因为它是链表,只需要移动指针
问:hashmap呢?
1.hashmap底层数组加链表
2.jdk1.8后对他进行优化。链表长度达到8以后,转红黑树
3.初始16,扩容是以前的2倍
问:它线程安全么?
不安全,如果线程安全可以用hashtable和concurrenthashmap
问:为什么concurrenthashmap线程安全,他底层实现是什么?
jdk1.7他底层用了sgment分段锁,继承了ReetRantLock可重入锁。
jdk1.8采用Node数组+链表+红黑树的数据结构来实现,并发控制使用Synchronized和CAS来操作,整个看起来就像是优化过且线程安全的HashMap,效率高,而且安全
我记得很清楚,面试官就是这么一步一步套我的。关于CAS大家可以去了解一下,不难,就是针对VEN三个值进行if判断。
问:我要一边遍历集合,一边删除如何实现
1.倒序删除
2.迭代器删除
这个问题,我很懵逼。。
问:list如何去重
直接放在hashset里面
问:string可以被继承么?
不可以,他是final修饰的类
这应该是这几个月面试以来。最简单的问题。。
问:你项目中用线程池了么?用的什么?
用的jdk自带的Exectors
问:经常用那个种类的线程池?
1.CachedthreadPool可缓存线程池比较多
2.也有用FixedThreadPool定长线程池的
问:线程池大小多少?你为什么这么定义长度?
1.30左右
2.关于线程池长度选择可以根据业务来定义
3.如果你的项目是IO密集型,你可以定义CPU数量*2的长度
4.如果你的项目是CPU密集型。可以定义CPU数量的长度
5.或者可以(线程等待时间与线程CPU时间之比+1)*CPU数量
关于线程池长度,说法不一,网上说法也不一样,但大概可以分为这几种。大家可以根据自己项目实际情况来定
问:线程回调知道么?
Future?
问:嗯,那线程通信呢?
wait,notify,join,volatile也算吧
你都用过哪些锁?
synchronize关键字,lock,分布式锁
synchronize实现原理呢?
synchronize其实底层是就JVM的一个监视器Monitor。同步代码块在编译前,会在同步的方法前加一个monitor.enter指令,退出方法或者异常处加入monitor.exitor指令
1、同步代码块采用monitorenter、monitorexit指令显式的实现。
2、同步方法则使用ACC_SYNCHRONIZED标记符隐式的实现。
synchronize和lock有什么区别?
1.synchronize关键字,lock是接口
2.lock可以手动释放锁,通常在finally代码块释放,但是synchronize不能手动释放
3.lock性能更优,还可以trylock非阻塞拿锁
用重入锁ReetRantLock如何线程间通信呢?
可以实现newcondition接口,它里面有await和signal方法
关于各种锁大家一定要熟悉,数据库乐观锁悲观锁,会经常问,还会问sleep和wait的区别,有的直接问threadpoolExecutor底层,所以大家对于他的结构参数,核心数量,队列,最大数量,一定要非常了解。
那你说下volatile关键字吧
1.volatile是为了保证主内存和工作内存的可见性,就是我对它进行修改时,会被其他线程发现?
他怎么会被发现?他有原子性么?
1.我记得工作内存和主内存有一种线程嗅探机制,就是工作内存一旦修改值,然后提交到主内存,当主内存的值一旦修改,线程嗅探机制就会通知其他各个工作内存告诉他们我的这个值已经修改。
2.它不具有原子性,想要原子性。可以用jdk自带的atomic类
既然你知道主内存,那本地内存有什么专属的变量?
ThreadLocal
为什么它是本地内存专属?底层原理知道么?
它其实就是一个map,k是当前线程的id,v是值,你用threadLocal的时候,它会判断k和当前线程的Id一不一样
死锁的条件
1.互斥条件:资源仅有一个线程占有
2.不剥夺条件:资源使用前,不能被其他进程夺走
3.请求和保持:进程已经保持资源,又提出资源请求,但是该资源被其他资源占有
4.循环等待:循环等待链,每个进程已获得的资源被下一个进程请求
数据库
你用sql语句如何去重?
select distinct 字段 from xx
你知道mysql的慢查询么?
知道,命令:show status like ‘slow_queries'
你平时都怎么优化sql的?
1.先从表设计开始,能用数字类型就不要用字符串类型,能用不可变char就不要用varchar,比如身份证,字段长度尽可能短,还有引擎的选择,myisam还是innoDB
2.还有一些sql语句优化,那些很简单,我就不多说了
3.主要优化就是建立索引
sql语句的优化大家多多少少应该都记得一点吧,面试可以说的详细些;
那你说的数据库引擎都有什么区别呢?比如MyISAM和InnoDB。
1.最大的区别就是。MyISAM不支持事物,DB支持
2.MyISAM是表级锁,DB是行级锁
3.MyISAM强调性能,查询原子性,速度快,插入性能优秀,比DB快很多很多倍。
4.MyISAM不支持外键
那你还知道什么引擎?
InnoDB,MyISAM,ISAM,memary
虽然现在mysql都默认InnoDB,但是大家对于这个引擎还是要多研究研究的,其实引擎有很多,我也只记住这4个,MyISAM是ISAM的升级版,memary是做缓存的,其他我也不太懂。
索引底层知道么?
1.InnoDB默认B+树,也就是多路平衡查找树,每次查找都从根节点出发,查到叶子节点可以获得所查的键值,然后根据查询判断是否需要回表查询数据。
2.还有Hash索引,底层hash表实现,查找时调用hash函数取得相应键值对之后回表查询所获得的实际数据
3.hash查询快,无法范围查询,也不支持索引排序,不支持模糊查询
你平时都怎么建立索引的?
一般在常用字段建立索引,比如where语句后面,我说下我sql优化的过程
1.一般在项目的测试阶段优化sql,可以查看mysql的慢查询记录,mysql会默认把查询时间超过10s的sql语句记录在日志,这个功能要自己手动打开。
2.然后你根据这些慢语句用explain命令查看它执行计划,看看索引是否失效,或者sql语句是否高效,有没有遵循最左原则。
3.根据执行计划针对性的修改
4.如果数据量实在庞大,那就考虑分表分库了
模糊查询,like左边放%会失效么?
会
模糊查询,like右边呢?
不会
还有哪些会导致索引失效?
<,>,!=,is null, is not null,相关计算操作,还有没遵循最左原则的,or中有一个没索引,就不会用索引
你都怎么分表分库的?
1.mycat比较多
2.还有shardingJDBC
分表分库这块真不难,按照时间分表,按照主键奇偶分表,%分表。都很常见,大家都可以自己看看。mysql集群之类的用mycat就好,这个数据库中间件真的非常快捷方便好
hive用过吗?
没,他是hadoop的一个数据仓库,其他不太了解
对于这些知识盲区,大家可以了解多少说多少
其他
web应用如何解决高并发问题?
1.数据库高可用,包括主从,集群,读写分离
2.HTML静态化
3.搭建图片服务器
4.使用缓存技术
5.负载均衡
集群情况如何解决session一致性问题
1.最常见的把session数据集中存储,比如放在redis里,然后不同的web从同一个地方获取session
2.web 服务器增加会话同步,比如mq
3.让同样的session的请求每次都发送到同一个服务器处理
我记得这块经常会问集群,各种集群。ngix,mycat,mq,redis。笔者接触的不多,所以就不献丑了。。
还有微服务,RPC,Docker,大家都可以根据自己情况回答,毕竟笔者经验有限,好多没学到~
算法
这块真的祝你平安了。。我感觉算法这块不是硕士都很难接触到很深,而且对于数学功底,数据结构要有很强的要求。。大神例外。
以上都是笔者亲身面试经历,大家如果面试被问到其他的,可以在下面留言,我会总结下然后不断更新的~