最近要换工作,面试了几家,在此记录。其实面试也是对既往工作的一种回顾,及时总结挺好的,希望自己能不断提升,也希望这份面经能帮助到大家。
- 关于简历方面的:
1.简单介绍一下自己 突出自己的优势
面试官您好,我叫XXX,江西XX人。很高兴有机会能和您聊一聊。我今年27岁,参加工作5年半,有3年半是做互联网行业,我对web开发全套基本比较熟悉,我有良好的沟通与协作能力能够独立负责所交予的工作模块。希望能够和像面试官你们一样优秀的工程师一起共事,谢谢。
2.简单说一下你的工作经历 突出项目情况,比如架构,规模,并发情况等
我第一份工作是在昆明一家小公司待了一年多,算上实习2年,做C/S开发的,用户规模300人左右,我们项目组3-4个人,后面也参与了省委党校的项目,我负责考试。后面半年我升为项目组技术组长(当时3个人),主要负责工作计划制定与实行,代码管理,技术上当时比较老吧,用tomcat部署,nginx做负载均衡,nginx配置的是权重策略。后面因为我打算回家乡这边发展,又考虑到做it的,应该去外面看看,就去了杭州,离九江也比较近。在杭州是做智慧建造,开发了劳务工管理平台,有web端,app端,当时项目开发人员4人,我是核心开发员之一,独立负责海康平台的对接和硬件接入,还有劳务工管理,比如实名制管理,进出场管理,工资核算,问题反馈等,项目是saas化部署,一开始还没有采用微服务,用户规模就是几百人,后面我主导了flyway的应用和git-flow代码版本管理落地,当时环境也比较简单,就是开发环境,线上环境。两年后因为个人原因我离开了,来了咱们南昌这家公司,这边是做党建信息化的,微服务spring boot+cloud,以及各种常用中间件,私有化部署的,用户规模千人。
3.说说在这些项目中解决过什么问题 突出自己的贡献
在昆明那家公司也解决过一些算印象比较深的问题吧,不过说实话不太记得。在杭州那家公司,遇到过人脸下发成功率不高,一方面下发是调用海康平台接口,有qps上限,另一方面当时也是来了就处理,后面改成固定线程数的fixedthreadpool,经过一番调试最后人脸下发成功率提高了一倍多
在这里做过考试优化,通过消息队列异步削峰和改进序列化,1000人并发提交保证无误,学习记录由于数据量比较大做了分区优化。
- 关于spring boot:
1.openfeign原理
大概说一下,它是基于面向接口的动态代理方式生成实现类,然后根据接口类的注解解析出底层method handler,最后基于重试器发送http请求。比如我们写一个业务的feign,最后通过autowired注入的对象是ReflectiveFeign.newInstance(Target<T> target)生成的对象。初始化大概就是使用@FeignClient定义客户端后,通过@EnableFeignClient启用客户端,再通过registar注册,然后生成客户端代理对象,最后通过reflectivefeign创建客户端实例。
2.@Autowired原理
Spring容器启动时,AutowiredAnnotationBeanPostProcessor被注册到容器;如果带@Autowired注解,则将依赖注入信息封装到InjectionMetadata中;在创建bean时,会调用各种BeanPostProcessor对bean初始化bean。
3.@Autowired和@Resource注解的区别和联系
联系:
@Autowired和@Resource注解都是作为bean对象注入的时候使用的
区别:
@Autowired注解是Spring提供的,而@Resource注解是J2EE本身提供的
@Autowird注解默认通过byType方式注入,而@Resource注解默认通过byName方式注入
@Autowired注解注入的对象需要在IOC容器中存在,否则需要加上属性required=false
4.说说SpringBean的创建原理了解
在spring框架中,创建操作交给spring容器实现。即 IOC(控制反转);
实例化对象的依赖对象(成员变量)也会由spring容器查找创建初始化,即DI(依赖注入)。
1)spring bean具体是啥?
Bean可以理解为一些元数据信息。Bean有生命周期:Bean的定义——实例化(空的对象)-Bean的初始化(属性填充和具体的初始化过程)——Bean的使用——Spring容器关闭的时候销毁Bean。
2)spring容器具体是啥?
在Spring中BeanFactory是IOC容器的实际代表者。
3)bean定义注册表是啥?
Spring 提供两种不同类型的容器:BeanFactory 和 ApplicationContext。BeanFactory为IOC容器具体实现指定了基本的规范,一组以Registry为后缀的接口定义了spring bean的注册功能,这些Registry接口的实现类即为spring bean definition的老巢,所有bean的配置信息都在这些类中,也就是可以理解为bean注册表。
5.怎么理解控制反转
我的理解就是说:从依赖下层到依赖上层的反转,之前是依赖具体的实现,现在是依赖接口
6.创建bean的方式
用得多得就是@Component,@Service,@Controler,@Repository注解,还有使用@Bean注解 使用@Import注解 实现ImportSelector接口,流程大概是通过上下文对象先定位,通过BeanDefinitionReader和AnnotationConfigUtils等加载,然后通过DefaultListableBeanFactory注册。
7.认证怎么做的 是jwt 还是auth2 用户信息存在哪里
Auth2认证,进行了auth2认证相关配置,比如鉴权客户端,我们有web,app,指挥中心端,小马等 用户登录有效时间,错误登录次数存在redis。我们使用jwt认证协议,它是一种轻量级的数据,存在内存中。
8.JWT和OAuth2的区别
JWT是一种认证协议
JWT提供了一种用于发布接入令牌(Access Token),并对发布的签名接入令牌进行验证的方法。 令牌(Token)本身包含了一系列声明,应用程序可以根据这些声明限制用户对资源的访问。
OAuth2是一种授权框架
另一方面,OAuth2是一种授权框架,提供了一套详细的授权机制(指导)。用户或应用可以通过公开的或私有的设置,授权第三方应用访问特定资源。
9.spring cloud的那些组件有什么用处
openfeign 用来跨服务调用
enruke 用来发现服务
auth2 用来认证
config 用来做配置中心
10.enruka怎么发现服务
源码没有深入了解,知道偏大概的过程
Eureka的Service Registry和Service Discovery能力,通过Eureka Server、Service Provider、Service Consumer三个角色完成。Eureka Server提供服务注册和发现,Service Provider,服务提供方,将自身服务注册到Eureka,Service Consumer,服务消费方。
服务注册时,Service Provider启动时会将服务信息(InstanceInfo)发送给eureka server,eureka server接收到之后会写入registry中,服务注册默认过期时间是90秒,InstanceInfo写入到本地registry之后,然后同步给其他peer节点。服务信息就包括application-yml中定义的spring.application.name,端口那些。eureka server可以集群部署,多个节点之间会异步进行数据同步,保证数据最终一致性,
eureka server端通过appName和instanceInfoId来唯一区分一个服务实例,记录在一个map中。Appname和serviceid在配置文件中配置。
12.zuul起到什么作用
Zuul提供了服务网关的功能,相当于是前置门户入口。请求首先通过网关,进行路径的路由,定位到具体的服务节点上。
Zuul本身也是一个微服务。也会在Eureka注册中中心进行服务的注册和发现,通过在zuul配置文件配置routes,请求通过Zuul来进行路由。
我们这边/api类接口都必须校验身份,通过配置OauthFilter进行过滤
UserOperationFilter用于验证用户合法登陆后,记录用户的信息,以便在指挥平台上展示。
13.zuul和gateway的区别
gateway对比zuul多依赖了spring-webflux,在spring的支持下,功能更强大,内部实现了限流、负载均衡等,扩展性也更强,但同时也限制了仅适合于Spring Cloud套件,而zuul则可以扩展至其他微服务框架中,其内部没有实现限流、负载均衡等。
14.ribbon怎么做负载均衡
ribbon是一个为客户端提供负载均衡功能的服务,它内部提供了一个叫做ILoadBalance的接口代表负载均衡器的操作。负载均衡器是从EurekaClient(EurekaClient的实现类为DiscoveryClient)获取服务信息,根据IRule去路由,并且根据IPing判断服务的可用性。
在默认情况下,每10秒钟,ribbon向EurekaClient发送一次”ping”。
LoadBalancerClient(RibbonLoadBalancerClient是实现类)在初始化的时候(execute方法,会通过ILoadBalance(BaseLoadBalancer是实现类)向Eureka注册中心获取服务注册列表,并且每10s一次向EurekaClient发送“ping”,来判断服务的可用性,如果服务的可用性发生了改变或者服务数量和之前的不一致,则从注册中心更新或者重新拉取。LoadBalancerClient有了这些服务注册列表,就可以根据具体的IRule来进行负载均衡。
15.hystrix怎么应用的
目前没有具体应用。
16.用户上下文怎么拿到的,我们所有controller继承基础controller,直接通过getcontext获取用户上下文,用户登录成功后,得到用户信息,前端请求接口时会带token,authFilter解析用户信息对吧,然后通过@ModelAttribute注解绑定到上下文中。
17.Spring的启动过程
启动过程是一个流程化的东西
大体分为2个步骤
1:获取、解析、注册配置信息,将配置的文件信息转换Map<name,beanDefinition>
2:根据上述的Map<name,beanDefinition>去实例化bean,并完成依赖注入
具体有
构造annotationconfigapplicationcontext
创建单例工厂
创建声明的beandefinitionreader
创建类路径beandefinitionreader
刷新
包括容器启动前准备工作
记录启动时间
初始化资源
准备beanFactory
初始化配置
添加bean后置处理器
注册默认的工厂依赖bean
注册单例bean
扫描配置类
注册后置处理器
添加国际化资源信息
初始化时间发布器
注册监听器
bean生命周期处理
容器启动后,发布事件finishrefresh
- 应用问题:
1.服务端入口ip变了,要修改客户端 怎么办
我认为可以做一层代理吧 不管入口ip是多少,出口始终不变。
2.你们这些中间间做了集群吗
我们数据库、es、mongo、redis都部署了集群,我们项目是私有化部署在docker上,我们是比如说,mysql、es、mongo、redis对应的1号节点部署在一台服务器,2号几点部署在一台服务器。主要是因为一个中间件的一个节点就用一台服务器有点浪费。
mysql节点开启了主从同步,再同步到es,从而也保证es节点数据同步。
3.集群和分布式有什么区别和联系
我个人觉得这两者没什么关系,分布式是一种架构方式,集群描述的是一些应用的组成状态吧。
4.redis用过哪些数据结构 分别是什么应用场景
在Redis中有一个「核心的对象」叫做redisObject,用来表示所有的key和value,在redisObject中「type表示属于哪种数据类型,encoding表示该数据的存储方式」。
String是Redis最基本的数据类型,
String类型的数据结构存储方式有三种int、raw、embstr
int整理类型
假如存储字符串是长度大于32个字节,会使用SDS方式进行存储,并且encoding设置为raw;否则将encoding改为embstr来保存字符串。
hash类型一般以字典的方式应用,底层通过hashtable实现
List类型使用ziplist和linkedlist进行实现。应用场景有阻塞队列,我们这边有用于消息队列,取出用户登录信息。
ZSet集合通过跳跃表实现,跃表是一种有序的数据结构,它通过每一个节点维持多个指向其它节点的指针,从而达到快速访问的目的。我们这边使用场景就是党建热词。
用的比较多的有String,list,hash,zset
String就是适用于简单的信息保存 我们用得地方很多,拿我考试这边来说,可以人为评分最后要算总分数,我就记录上一次的得分,以便下次相加。
list适用于同一种数据
Hash类型适用于其实就是字典
zset有序集合适用于需要排序的场景,这边展示热词用到了 因为要按热度排序
平时遇到过什么问题?
很多呀,比如不明确的的类型导致问题,k/v,value可以是数字也可以是字符串,个人感觉有利有弊吧-就比如我这边的一个业务,有个数量存在redis,有一天发现那个数据从redis查不出来,后来发现用户多输了一个空格,代码里面转int就失败了,因为存的没转,取出来异常被吃掉了,导致没显示得报出来 后面只好看日志排查到
还有线上Key暴增问题,因为没有设置合理的过期时间
redis崩掉 当时登录有遇到过 当时letters的设置有点问题导致内存飙升
还有数据更新了如何更新缓存,比如字典
还有有个业务是频繁业务,流量也不小,比如党员经常要去查,我们缓存了家庭成员信息,为了不让这些信息集体失效,设置了随机过期时间
redis淘汰策略问题
FIFO 淘汰最早数据、LRU 剔除最近最少使用、和 LFU 剔除最近使用频率最低的数据几种策略。
5.kafka原理有一些了解吗
kafka这边东西很多,我简单说下了解的一些消息发送和消费过程
发送消息时 首先获取topic的所有Patition
如果不指定Patition,也没有指定Key的话,使用自增长的数字取余数的方式实现指定的Partition。这样Kafka将平均的向Partition中生产数据。
如果想要控制发送的partition,则有两种方式,一种是指定partition,另一种就是根据Key自己写算法。继承Partitioner接口,实现其partition方法。
当生产者将数据发布到topic时,消费者通过pull的方式,定期从服务器拉取数据,在pull数据的时候,服务器会告诉consumer可消费的消息offset。发送消息也是通过线程去处理的。
我们主要用于日志收集和消息系统
6.debezium监控数据库的过程了解吗
有些了解,当时自己还实践了下,差一点搞好。debezium connector通过监控数据库的binlong并通过kafka connetor写入kafka,后台可以监控这个topic做一些业务。这就是我了解的大概过程。
我们这边将数据库和特定数据表分开监控,因为各自的业务需要,
具体我们这边的应用有指挥平台上的登录跳点展示,还有redis后台更新等
还有一个是党建后台首页的任务卡片,首先初始化任务,用户录入数据后金控到变化再更新对应的任务。为什么要做这个呢,因为用户录了数据要对其做出响应,比如实时统计,或者业务变化。
7.用的什么数据库
数据库用的Mysql
8.devops了解多少
我们这边有专门的运维人员
环境配置也是运维管理
Api管理用的什么的 Yapi
研发管理用的什么 禅道
9.用了哪些中间件,分别是干嘛用的
es用作搜索 主要是指挥平台上用到
Xxljob定时调度 指挥平台上用到
flyway数据库版本管理
Mongo数据存储 统计查询
Redis 用于缓存
Cassandra 数据变化记录在Cassandra,还有一些接口调用信息在cassandra 主要是指挥平台上用到 因为每天的登录信息,数据变化,接口都是很多的,故考虑用Cassandra
debezium监控的数据变化 指挥平台上用到
graylog收集日志
10.分布式锁原理和应用
当有多个使用者对一个资源进行使用的时候,为了保证避免对资源的使用冲突,要进行的一种串行控制操作。
分布式锁,则在java的synchronized,Lock等基础上鉴于分布式系统而实现的在系统间进行互斥访问共享资源的一种方式。
目前我们用到的有基于数据库-操作简单,容易理解,可以自定义时间戳,重入次数等,基于redis的-非阻塞,性能比较好
基于数据库的,用过for update行锁,比如下订单减库存的时候,也可以用乐观锁判断就可以满足基本的锁需求。
基于redis的,我们是使用使用lua脚本实现。每个客户端有自己的唯一标识,一个客户端加的锁只能被这个客户端解锁。
11.你对乐观锁和悲观锁的理解
我个人觉得就是说对其的一个信任度和可接受度,乐观锁效率较高,损耗较小,但锁可能不一定成功,悲观锁强调锁成功率,损失一些性能换取。
12.线程池的理解
线程池是为了避免线程频繁地创建和销毁,我们项目中用到的线程池有,都是在要处理较为繁重的任务的情况下使用的,比如指挥平台上动态感知数据,还有消息时间的处理,用的比较多的是Scheduled 线程池,定期执行任务,还有固定线程数的fixedthreadpool。
服务器应用程序中经常出现的情况是:单个任务处理的时间很短而请求的数目却是巨大的。
构建服务器应用程序的一个过于简单的模型应该是:每当一个请求到达就创建一个新线程,然后在新线程中为请求服务。实际上,对于原型开发这种方法工作得很好,但如果试图部署以这种方式运行的服务器应用程序,那么这种方法的严重不足就很明显。
每个请求对应一个线程(thread-per-request)方法的不足之一是:为每个请求创建一个新线程的开销很大;为每个请求创建新线程的服务器在创建和销毁线程上花费的时间和消耗的系统资源要比花在处理实际的用户请求的时间和资源更多。除了创建和销毁线程的开销之外,活动的线程也消耗系统资源(线程的生命周期!)。在一个JVM 里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或“切换过度”。为了防止资源不足,服务器应用程序需要一些办法来限制任何给定时刻处理的请求数目。
线程池为线程生命周期开销问题和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。其好处是,因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。而且,通过适当地调整线程池中的线程数目,也就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。
13.索引规范
1.主键的名称以“pk_”开头,唯一键以“uk_”或“uq_”开头,普通索引以“idx_”
开头,一律使用小写格式,以表名/字段的名称或缩写作为后缀。
2.InnoDB 和 MyISAM 存储引擎表,索引类型必须为 BTREE;MEMORY 表可以根据需要选择
HASH 或者 BTREE 类型索引。
3.单个索引中每个索引记录的长度不能超过 64KB。 4.建议单个表上的索引个数不要超过 7 个。
5.在建立索引时,多考虑建立联合索引,并把区分度最高的字段放在最前面。如列 userid
的区分度可由 select count(distinct userid)计算出来。
6.在多表 join 的 SQL 里,保证被驱动表的连接列上有索引,这样 join 执行效率最高。
7.建表或加索引时,保证表里互相不存在冗余索引。对于 MySQL 来说,如果表里已经存
在 key(a,b),则 key(a)为冗余索引,需要删除
14.redis哨兵模式
主要是为了高可用,没有怎么了解
15.缓存穿透是指使用不存在的数据频繁请求接口,导致查询缓存不命中,查询db也不命中。
Redis缓存击穿,就是某个热点数据失效时,大量针对这个数据的请求会穿透到数据源。
缓存雪崩。产生的原因是缓存挂掉,这时所有的请求都会穿透到 DB。
16.线上问题排查思路说一说
定位问题
查日志 很重要
分析问题
hotfix
或发版
17.要读取一个十万行的excel插入数据到excel,怎么做
考查批处理 可以通过mysql导文件的方式做批处理
18.AOP的实现方式
面向切面
有静态AOP,动态AOP,动态字节码生成,自定义类加载器等
静态AOP就是说在编译的时候,切面以字节码的行是编译到目标文件中
动态AOP通过JDK动态代理实现,在运行期目标类加载后为接口动态生成代理类
动态字节码生成的话比如CGLIB,在运行期目标类加载后动态生成目标类的子类
还有自定义类加载器
你自己用过吗,用过,就用过用注解织入的@Aspect
@Aspect 表示它是一个切面
@Before 表示他将在方法执行之前执行
@EnableAspectJAutoProxy 启用 AOP 功能
beforea after是通知类型,通知方法在目标方法后什么时候执行 execution within是切点指示器,指定使用这个切点的地方
切点(PointCut): 可以插入增强处理的连接点。
切面(Aspect): 切面是通知和切点的结合。
实际使用:通过切面校验参数EnableParamValidation
19.jvm启动参数有哪些 什么时候需要调整
java启动参数分为三类:
其一是标准参数
其二是非标准参数
其三是非Stable参数
设置参数要重启服务,我们发了通知,说凌晨维护,就在这段时间内重启,第二天观察
标准参数比如-client,设置jvm使用client模式,特点是启动速度比较快。
-server,设置jvm使server模式,启动速度比较慢,运行时性能很高,适用于生产环境
-Dproperty,设置系统属性名/值对,可用System.getProperty("property")得到该值
-jar 指定以jar包的形式执行一个应用程序。必须让jar包的manifest文件中声明初始加载的Main-class
-verbose类的
-verbose:class 输出jvm载入类的相关信息,当jvm报告说找不到类或者类冲突时可用这个进行诊断
-verbose:gc 输出每次GC的相关情况。
非标准参数有
-Xmsn 指定jvm堆的初始大小,默认为物理内存的1/64
-Xmxn 指定jvm堆的最大值,默认为物理内存的1/4
-UseParallelGC 启用并行GC 一般是这个设置
-UseSerialGC 启用串行GC
HeapDumpPath=./java_pid<pid>.hprof 指定导出堆信息时的路径或文件名
HeapDumpOnOutOfMemoryError 两者结合使用
一般系统停顿的时候可能是GC的问题也可能是程序的问题,多用jmap和jstack查看,或者killall -3 java,然后查看java控制台日志,能看出很多问题。(相关工具的使用方法将在后面的blog中介绍)
jstack命令主要用于调试java程序运行过程中的线程堆栈信息,可以用于检测死锁,进程耗用cpu过高报警问题的排查
实际用是用过 但不是排查问题 就是试着用看看
20.用过什么工具做压测 或者性能检查工具
JProfile 可以查看运行时内存线程等情况,还可以压测sql
Jmeter 压测
21.如何保证mysql和缓存一致性
我觉得这个问题其实有点伪命题,要看应用场景,如果是单机版的数据库和redis,可以采取延迟双删测策略,如果是集群的另说
另外缓存就是为了数据在一定时间内不变化,要求强一致性,那用缓存干嘛呢。cap理论也说到3选2,一致性也只能是最终一致性,或者是逐渐趋向于强一致性。
redis的目的主要是扛流量,高并发,防止流量查询mysql,如果为了一致性把redis数据删了,也不行呀。
22.你知道还有哪些方式可以额外配置类加载路径吗
可以参照spring boot starter的方式,在meta-info spring.factories下面配置类路径,我们第三方这边一般就是要到的第三方工具,比如微信、阿里、百度、腾讯等工具,由于这些第三方基本都要提供appkey等相关配置,配置写在配置文件中,为了方便统一管理,把这些三方的东西整合到单独的工程中,通过starter的方式制作jar供需要的地方使用,引用这些jar包的地方要定义这些配置,现在第三方配置定义在thirdparty.yml中,哪个服务需要用到,可以通过freemarker声明这些配置
23.你的sql调优经验:
有啊。比如说之前我负责的业务要查本机和下级大概11个统计数,当时写的sql是left join 统计数1中间表2,left join 统计数2中间表,当时写下来,数据量几千差不多1s,本来那个业务点不太用,后来客户那边领导要来视察,结果1个月录到了查不到百万条,查询就7-8秒了,后面发现这些统计数其实都是来源一张表,只是条件不同,通过If判断写在一个sql中,优化后查询在1秒左右,可以接受。
24.分区实践说一说
我这边学习记录,业务上一般不会查询某年内的数据,于是按年分区
1.分区表的分区字段(partition-key)必须包含在表的所有主键或者唯一索引中。 2.单个分区表中的分区(包括子分区)个数不能超过 1024。 3.上线前研发或 DBA 必须指定分区表的创建、清理策略。4.访问分区表的 SQL 必须包含分区键且建议不要跨分区查询。
25.redis持久化怎么做的
Redis 有 RDB 和 AOF 两种持久化方式,RDB 是把内存中的数据集以快照形式写入磁盘,二进制压缩存储;AOF 是以文本日志的形式记录 。
RDB 把整个 Redis 的数据保存在单一文件中,比较适合用来做备份。
AOF 对日志文件的写入操作使用追加模式,有灵活的同步策略。
26.es倒排索引相关:
全文检索要求一个词语或一段话在整个文档中被搜索,传统的数据库每个字段存储单个值,这对全文检索并不够。文本字段中的每个单词需要被搜索,对数据库意味着需要单个字段有索引多值(这里指单词)的能力,平常我们对字符串类型的字段一般不会建索引,一方面文本多变,没什么规律,另一方面对文本搜索的要求可能也不太大。Elasticsearch 使用一种称为 倒排索引 的结构,它适用于快速的全文搜索。
一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,有一个包含它的文档列表,文档记录了每个词出现的总次数(次数跟权重有关系)
es将每个文档的 content 域拆分成单独的 词(我们称它为 词条 或 tokens ),创建一个包含所有不重复词条的排序列表,然后列出每个词条出现在哪个文档。
27.集合相关:list arraylist linkedlist set 使用场景,不同点-重要
List是一个接口
ArrayList,LinkedList,Vector等等继承了它:
ArrayList的基层是数组,实现了通过index直接访问数据,元素可重复
LinkedList是一个通过双向链表来实现的,元素可重复,查找和修改和arraylist基本相同
set接口实现类特点:无法通过index索引值,而且集合中是无序的,元素不重复;
hashset我感觉和hashmap基本相同,只是将put方法变成了add方法吧;
28.对spring boot的理解:
先说说spring吧。
Spring 通常指的是 Spring Framework ,spring的对象管理帮我们解决了随着项目越来越大sevlet那些对象的管理问题,
使用控制反转,依赖注入,切面编程的特性对这些类生命周期的管理,但随之又出现一个问题,xml配置太多,
这偏离了 Spring Framework 最初设计的初衷,所以 Spring Boot 被设计出来。
Spring Boot 将 Spring Framework 的功能进行了扩展,将繁琐的配置功能进行了内部整合,通过一些自动化的配置
和发现机制自动感知功能组件,降低了使用成本,保证了和Spring Framework 的一致性
Spring Framework 和 Spring Boot 的根本是一致的。Spring Boot 是 Spring Framework 的引导程序以简化其配置和使用。而Spring Framework 是 Spring Boot 的基础,Spring Boot 无法脱离 Spring Framework 。用户通过上层 Spring Boot 的引导来使用 Spring Framework 。
29.spring控制反转IOC:
是程序中的之间的关系,不用代码控制,而完全是由容器来控制
在运行阶段,容器会根据配置信息直接把他们的关系注入到组件中。
而依赖注入强调关系的注入是由容器在运行时完成,而控制反转强调关系是由容器控制。
30.对spring中filter的理解
Filter 是 JavaEE 中 Servlet 规范的一个组件,位于包javax.servlet 中,它可以在 HTTP 请求到达 Servlet 之前,被一个或多个Filter处理。Filter的这个特性在生产环境中有很广泛的应用,如:修改请求和响应、防止xss攻击、包装二进制流使其可以多次读
init(FilterConfig fConfig)
init()方法用来初始化过滤器,可以在init()方法中获取Filter中的初始化参数。
doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
doFilter()方法完成过滤操作。当请求发过来的时候,过滤器将执行doFilter方法。
destroy()
Filter对象创建后会驻留在内存,当web应用移除或服务器停止时调用destroy()方法进行销毁。在Web容器卸载 Filter 对象之前被调用。destroy()方法在Filter的生命周期中仅执行一次。通过destroy()方法,可以释放过滤器占用的资源。
- 基础问题:
1.do vo dto使用规范
数据对象:xxxDO
数据传输对象:xxxDTO
展示对象:xxxVO,xxx 一般为网页名称
POJO 是 DO/DTO/BO/VO 的统称
2.集合中哪些是线程安全的
Vector 关键性的操作,方法前面都加了synchronized关键字,
Hashtable 使用了synchronized关键字,所以相较于Hashmap是线程安全的。
ConcurrentHashMap:使用锁分段技术确保线性安全
Stack:栈,也是线程安全的,继承于Vector
3.Mysql回表是怎么回事
就要说到基于主键索引和普通索引的查询的区别
如果语句是select * from T where ID=500,即主键查询方式,则只需要搜索ID这棵B+树;
如果语句是select * from T where k=5,即普通索引查询方式,则需要先搜索k索引树,得到ID的值为500,再到ID索引树搜索一次。这个过程称为回表。
4.说说ca cp吧,常用中间件是分别侧重(偏向)ca cp的哪方面
是说一个分布式系统最多只能同时满足一致性、可用性和分区容错性这三项中的两项。
kafka 消息中间件,有partiton 个人感觉偏向于一致性 和 分区容错性
redis zookeeper 个人感觉偏向于一致性、可用性
mysql 个人感觉偏向于一致性、可用性
5.如何保证kafka消费的幂等性 不重复消费
可以保证每条消息的id都不一致,使用分布式锁来消费 行不行噢
6.说说mysql的聚簇索引和非聚簇索引
主键索引也被称为聚簇索引
非主键索引也被称为二级索引
7.说说你们git-flow是怎么使用的
各本地分支-提交pr-new-develop
8.表a 5条记录,表b10条记录,表b的前3条和a第一条能关联上,那么a left join b,会查出几条记录
7条 1*3+4
9.集合的sort底层用的是什么排序
首先先判断需要排序的数据量是否大于60。
小于60:使用插入排序,插入排序是稳定的
大于60的数据量会根据数据类型选择排序方式:
基本类型:使用快速排序。因为基本类型。1、2都是指向同一个常量池不需要考虑稳定性。
Object类型使用归并排序。因为归并排序具有稳定性。
10.一条sql的执行过程
客户端发起连接后,由连接器进行权限验证,再到分析器,分析器查询缓存,如果命中直接返回
不命中再到优化器,再到执行器。
分析器,内置解析树,对其语法检查,先from,再on,再join,再where......;检查权限等
11.service循环调用
我们有这样用,因为spring会解决循环依赖问题
Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池( singletonObjects ),二级缓存为早期曝光对象 earlySingletonObjects ,三级缓存为早期曝光对象工厂( singletonFactories )
12.@ServletComponentScan了解吗
@ServletComponentScan注解的含义在SpringBootApplication(启动类)上使用@ServletComponentScan注解后,Servlet、Filter(过滤器)、Listener(监听器)可以直接通过@WebServlet、@WebFilter、@WebListener注解自动注册,无需其他代码!实际用得比较少。
13.线程暂停有哪些方法,有什么区别
sleep
yield
wait
sleep(long) 和 yield()是Thread类的静态方法
wait()是java.lang.Object的方法,须在Synchronized语句块内使用这三个方法
sleep ,释放cpu资源,不释放锁资源,如果线程进入sleep的话,释放cpu资源,如果外层包有Synchronize,那么此锁并没有释放掉。
wait,释放cpu资源,也释放锁资源,
yield:让出CPU调度,调用yield方法一般是一个建议 可以让别的相同优先级的线程使用CPU了
14.AOP和IOC的联系和区别
两者没啥关系
15.delete 和 tuncate的区别
一个是条件删除,DELETE是可以带WHERE的,所以支持条件删除;而TRUNCATE只能删除整个表。
然后是事务回滚 DELETE是数据操作语言,操作时会进行存储,可以进行回滚。TRUNCATE是数据定义语言,操作时不会进行存储,不能进行回滚。
在数据量比较小的情况下,DELETE和TRUNCATE的清理速度差别不是很大,数据量大的时候,由于第二项中说的,TRUNCATE不需要支持回滚,所以使用的系统和事务日志资源少。DELETE 语句每次删除一行,并在事务日志中为所删除的每行记录一项,固然会慢,但是相对来说也较安全。
最后一个是高水位重置 DELETE操作之后虽然表的数据删除了,但是并没有降低表的高水位,随着DML操作数据库容量也只会上升,不会下降。所以如果使用DELETE,就算将表中的数据减少了很多,在查询时还是很和DELETE操作前速度一样。而TRUNCATE操作会重置高水位线,数据库容量也会被重置,之后再进行DML操作速度也会有提升。
16.运行时异常和一般异常的区别
运行时异常:由java虚拟机抛出的异常. 用户不必处理。而一般异常是用户可以抛出的异常,如果抛出调用方必须进行处理。
另外,
运行时异常,在定义方法时不需要声明会抛出runtime exception,在调用方法时也不需要捕获这个runtime exception; runtime exception是从java.lang.RuntimeException或java.lang.Error类衍生出来的。
一般异常,定义方法时必须声明所有可能会抛出的checked exception,调用方法时,必须捕获它的checked exception,不然就得把它的exception传递下去,checked exception是从java.lang.Exception类衍生出来的。
17.char和varchar的区别
varchar 是变长存储,char是定长存储,比 char 更省空间。char的话一般用于固定长度的表单提交数据存储 ;例如:身份证号,手机号,电话,密码等
char最大长度限制为255 其数据范围可以是0~255或1~255
varchar数据类型的长度到65535
18.integer.parseInt内部实现
根据字符的ASCII的解析符号位,并按字符转为数字,然后使用逆向负进位方式生成数字,最后修正符号得出我们想要的结果。算法符合了字符串解析的顺序
19.DNS查询过程了解多少
实际DNS查询的过程,是这样的:
举个例子,比如用户在浏览器中输入这个域名:123.abc.qq.com.cn
1、浏览器会先看自身有没有对这个域名的缓存,如果有,就直接返回,如果没有,就去问操作系统,操作系统也会去看自己的缓存,如果有,就直接返回,如果没有,再去hosts文件看,也没有,才会去问LDNS。
2、LDNS会去先看看自己有没有123.abc.qq.com.cn的A记录,要有就直接返回,要没有,就去看有没有abc.qq.com.cn的NS记录,如果有,就去问它要答案,如果没有,就去看有无qq.com.cn的NS的记录,如果有,就去问它,没有就去看有无com.cn的DNS,还没有就去看有无cn的DNS,如果连cn的NS记录都没有,才去问根。
21.String类了解多少
String类的一个最大特性是不可修改性,而导致其不可修改的原因是在String内部定义了一个常量数组,因此每次对字符串的操作实际上都会另外分配分配一个新的常量数组空间(这片空间位于jvm的静态方法区)
实例化字符串
public String(String original) { this.value = original.value; this.hash = original.hash; }
System.arraycopy 是在底层操作中经常用到的一个数组复制方法
在重写equals()时需要重写hashCode()方法,以保证相同对象的hash值也是一样的,否则会出现意想不到的问题的。因为如果我们对一个对象重写了euqals,意思是只要对象的成员变量值都相等那么euqals就等于true,但不重写hashcode,那么我们再new一个新的对象, 当原对象.equals(新对象)等于true时,两者的hashcode却是不一样的,由此将产生了理解的不一致,导致混淆
22.StringBuiler和StringBuffer的区别
StringBuffer、StringBuilder和String一样,也用来代表字符串。String类是不可变类,任何对String的改变都 会引发新的String对象的生成;StringBuilder则是可变类,任何对它所指代的字符串的改变都不会产生新的对象,而StringBuffer(使用了synchronized)是线程安全版的StringBuilder。
23.Map了解多少
HashMap 就是数组加链表实现的,数组中的每一项是一个链表。通过计算存入对象的 HashCode,来计算对象在数组中要存入的位置,用链表来解决散列冲突,链表中的节点存储的是键值对。HashMap 的容量都是 2 的幂次方,可以通过按位与操作来计算余数.但是 HashMap 是非线程安全的。因为 HashMap 为了避免尾部遍历(为了提高效率),在链表插入元素时使用头插法,在多线程 put 的情况下,有可能在容量超过填充因子时进行 rehash,产生死循环。
ConcurrentHashMap 采用分段锁的思想来降低并发场景下的锁定发生频率
linkedhashmap元素有序
hashmap元素无序
24.synchronized 锁原理
synchronized 对对象进行加锁,在 JVM 中,对象在内存中分为三块区域:对象头、实例数据和对齐填充。在对象头中保存了锁标志位和指向 monitor 对象的起始地址,
当 Monitor 被某个线程持有后,就会处于锁定状态
Lock基于队列同步器也成为AQS来实现,维护两个队列,一个等待队列,一个同步队列。当获得锁的线程需要等待某个条件时,会进入等待队列,等待队列可以有多个。当条件满足时,线程会从等待队列重新进入同步队列进行获取锁的竞争。Semaphore信号量也是基于 AQS 。
25.JVM 内存模型主要指运行时的数据区
栈也叫方法栈,是线程私有的
本地方法栈与栈类似
程序计数器保存着当前线程所执行的字节码位置
堆是 JVM 管理的内存中最大的一块,堆被所有线程共享,目的是为了存放对象实例,几乎所有的对象实例都在这里分配。OOM异常就是这里抛出的
方法区也是各个线程共享的内存区域,又叫非堆区。用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
java内存模型
JMM 所有的共享变量都存储在主内存中共享。每个线程有自己的工作内存,工作内存中保存的是主内存中变量的副本,线程对变量的读写等操作必须在自己的工作内存中进行,而不能直接读写主内存中的变量。
在多线程进行数据交互时,例如线程 A 给一个共享变量赋值后,由线程 B 来读取这个值,A 修改完变量是修改在自己的工作区内存中,B 是不可见的,只有从 A 的工作区写回主内存,B 再从主内存读取自己的工作区才能进行进一步的操作。由于指令重排序的存在,这个写—读的顺序有可能被打乱。因此 JMM 需要提供原子性、可见性、有序性的保证。
volatile 的另一个作用就是阻止指令重排序,这样就可以保证变量读写的有序性。
26.类加载机制
据了解,分为加载、链接、初始化
只有对类主动使用时,才会进行初始化,初始化的触发条件包括在创建类的实例时、访问类的静态方法或者静态变量时、Class.forName() 反射类时、或者某个子类被初始化时。
27.垃圾回收算法
CMS 算法 JDK1.7 之前可以说是最主流的垃圾回收算法。 使用标记清除算法。分代回收。第一个阶段是初始标记,第二个阶段是并发标记。第三个阶段是重新标记阶段,第四个阶段是并发清理阶段,最后一个阶段是并发重置阶段
G1 算法 ,1.9 版本后成为 JVM 的默认垃圾回收算法。
ZGC DK1.11 版本中提供的高效垃圾回收算法 使用了着色指针技术
28.有过调优经验吗
比较少
29.线程分配的内存多大
2M。进程是系统资源分配的最小单位,线程是程序执行的最小单位;
进程使用独立的数据空间,而线程共享进程的数据空间。
30.三次握手建连
三次握手是为了建立双向的链接,建立链接前需要 Server 端先监听端口,这时 Client 端准备建立链接,先发送一个 SYN 同步包,由于 TCP 是双工传输,Server 端也会同时向 Client 端发送一个 SYN,申请 Server 向 Client 方向建立链接,Client 收到 Server 的 ACK 后,向 Server 端发送 ACK,回复 Server 端的 SYN 请求。Server 端收到 Client 端的 ACK 后,链接完成
四次握手断开连接
假如Client 先主动发起了关闭链接请求,Client 向 Server 发送了一个 FIN 包,Server 端收到 FIN 后,返回 ACK,当 Server 端数据发送完毕后,Server 端会向 Client 端发送 FIN,Client 端收到 Server 端的 FIN 后,回复 ACK(等待然后再关闭),Server 端收到 ACK 后直接就进入 CLOSED 状态。
补充:
1.你觉得是否有必要使用外键约束
2.SSH和SSM区别
3.数据库主键的设计原则了解吗
- 智力题:
1.有很多绳子,每根绳子刚好1小时可以烧完,那如何用这些绳子测出1.5小时
思路:从两头烧
总结:对自己当前所在的项目能讲清楚就是最好的,其实不用多么高大上,但是要能说出来,有条理些。原理和底层那些一般有些了解就行,不需要很清楚。要注意岗位需求。