面经总结(2022-03-05)
总结从三月份到五月份面试遇到的问题,问题按照面试时间顺序,未分类。
—暑期实习已经结束 现开始秋招 有空继续记录
- spring依赖注入原理
Spring依赖注入就是对IOC的实现,将对象注入到IOC容器中,使用时通过getBean的方式获取,学习Spring时也对依赖注入的原理以及注解的使用比较好奇,所以跟着视频简单写了一个IOC容器,主要过程:1.首先自定义几个注解,比如@Component、@AutoWired、@Qualifier、@Value,自定义注解主要是为了通过反射实现对属性的注入。2.编写描述Bean信息的对象,两个参数:beanName和beanClass,之后编写ApplicationContext类,接下来简述一下该类实现原理;
//实现的比较简单,主要有如下四个方法,
//用来存储对象的Map,也就是IOC容器,getBean时通过name返回对象
private Map<String,Object> ioc = new HashMap<>();
//构造器方法 传入的参数是要扫描的包名,构造器调用下面的几个方法去更新ioc容器
public MyAnnotationConfigApplicationContext(String pack)
//该方法的作用是扫描包,首先通过写好的工具类扫描包得到全部的类
//之后遍历这些类找到添加了@Component注解的类,创建BeanDefination对象(name和class,
//name由@Component注解中的value获得,如果value为空name,name为类名的首字母小写)
//将BeanDefination对象 加入到set中,最后返回Set集合
public Set<BeanDefiniton> findBeanDefinitions(String pack);
//遍历Set集合,以name为key,以对象为value存入Map中
//创建对象的过程通过反射实现
//对使用了@Value注解的属性进行属性注入,注入的过程实际上是通过反射调用这个属性的set方法
//并且根据属性的类信息进行类型转换
//最后存入Map
public void creatObject(Set<BeanDefiniton> beanDefinitons);
//creatObject只实现了对Integer、String、Boolean等jdk提供的类型的注入
//@AutoWired 实现对自定义对象的注入
//过程并不复杂,一样首先扫描set集合,找到使用了@AutoWired注解的属性,
//再根据@Qualifier 的value找到beanname,再通过beanname在map中得到对应的Object,
//(这里如果没有使用@Qualifier则根据类名的首字母小写当中beanname)
//反射调用set方法注入Object
public void AutoWiredObject(Set<BeanDefiniton> beanDefinitons);
//从map中根据beanname获取对象
public Object getBean(String beanName){
return ioc.get(beanName);
}
实际的Spring注入要复杂的多,这里只是进行简单的模拟,比如@AutoWired注解默认按照类型进行注入,如果IOC容器中存在两个及以上的相同类型的bean时,根据bean的名称进行注入,如果没有指定名称的bean,则会报错,指定名称用@Qualifier注解
- 场景题
- 读本书,可能会问,Jvm中找一部分研究制作者怎么改进的
用力一星期左右的时间研究了下深入理解JVM中的内存管理部分,有几个可以应对此问题的内容
1.为什么要分堆和栈
①首先堆栈分离在逻辑上就更清晰,堆用来存储数据,栈代表处理逻辑
②堆上的数据是可由栈共享的,节省了大量空间
③栈只能向上增长,因此就会限制住栈存储内容的能力,而堆中的对象是需要动态增长的,堆栈结合使动态正常成为可能。
堆栈分离可节约空间,实现对象的动态增长
2.为什么要分新生代和老年代
主要原因是对象的生存时间不同,对于新生代大多数对象生存时间很短所以GC频繁,对于新生代一般采用标记复制的回收方式(标记整理必要性不高,因为大量对象会在新生代被回收,标记清除则会产生内存碎片),老年代中对象生存时间长,存活率高,所以一般采用标记整理的回收算法。
3.新生代中的survivor区有什么用,为什么要有两个
①假设没有survivor区
survivor区的主要作用就是减少FullGC的次数,假设没有survivor区,在eden区经过一次MinorGC后即进入老年代,老年代很快被填满则进行MajorGC,而MajorGC一般伴随着FullGC
②假设有一个
如果只有一个survivor区,那么为了避免内存碎片化,则只能采用标记整理的回收算法,而在新生代中对象回收频繁,采用标记整理影响效率,所以分配两个survivor区(from 和 to),使用标记复制算法最好。
还有很多关于内存管理、垃圾回收以及垃圾收集器的问题可以回答,这里只总结三个。
- 堆排序
其他排序算法都自己模拟过,但是堆排比较麻烦就没自己模拟,而且一般java中都直接使用优先队列,就没关注堆排序,面试被问到时回答的很笼统,所以总结下,首先堆分为大根堆和小根堆,大根堆也就是堆顶是最大元素,每个元素的左右子节点都比当前元素小,小根堆则相反,这里以大根堆为例。
进行堆排序首先需要构造堆,构造堆就是一个向二叉堆中插入数据的过程,插入数据时会直接插入到堆的最后一个空节点,然后向上调整(如果大于父节点则和父节点交换),插入全部数据后得到大根堆。
堆构造完毕,输出排序序列,输出根节点,将根节点与最后一个节点交换,当前待排序个数为n-1,再对根节点向下调整(向下调整过程是和左右子节点中的最大值进行比较,若当前节点小于该最大值则交换,重复向下调整过程直到当前节点大于左右子节点或左右子节点为空),重复上述过程n次得到排序序列。
##Tips:底层逻辑是二叉树,但是可以用数组实现,因为是完全二叉树,每个节点可通过数组下标得到子节点或父节点位置。
堆排序算法详细流程
- 高内聚,低耦合
高内聚低耦合写过代码的都清楚什么意思,但是面试问到这个问题的时候,我突然不知道从何答起,后面想了想还是应该举例表达下自己的理解较好。
比如:我们在A元素去调用B元素,当B元素有问题或者不存在的时候,A元素就不能正常的工作,那么就说元素A和元素B耦合。
耦合带来的问题就是其中一个被耦合的模块发生修改时,当前模块将不能正常运行。
- jvm堆,栈,方法区
问题3中介绍了为什么要分堆和栈以及分堆和栈的好处,这里主要介绍下堆、栈、方法区主要存储哪些内容。
JVM中主要分为四个部分:堆、栈、方法区、程序计数器。其中堆和方法区由线程共享,栈和程序计数器为线程私有。
堆区域唯一的目的就是存放实例对象,分为新生代和老年代,新生代对象默认年龄达到十五岁就会被晋升为老年代。
##Tips “Hotspot 遍历所有对象时,按照年龄从小到大对其所占用的大小进行累积,当累积的某个年龄大小超过了 survivor 区的一半时,取这个年龄和 MaxTenuringThreshold 中更小的一个值,作为新的晋升年龄阈值”
栈分为本地方法栈和虚拟机栈,虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。栈内存主要的部分是局部变量表,局部变量表主要存储编译期可知的基本数据类型以及对象引用(可能是一个指向对象起始地址的引用指针,也可能是一个对象的句柄)
方法区方法区是一个逻辑部分,在hotspot中方法区的实现方式是永久代,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
- mysql 数据类型,建表
主要的几种类型Int、float、double、DECIMAL、DATE、DATETIME、CHAR、VARCHAR
CREATE TABLE IF NOT EXISTSrunoob_tbl
(
runoob_id
INT UNSIGNED AUTO_INCREMENT,
runoob_title
VARCHAR(100) NOT NULL,
runoob_author
VARCHAR(40) NOT NULL,
submission_date
DATE,
PRIMARY KEY (runoob_id
)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
- Mvcc具体什么时候生成快照
生成快照的时机和隔离级别有关:读取已提交和可重复读。
在读取已提交(RC)隔离级别中,每次查询都会生成一个readView。
在可重复读(RR)隔离级别中,只在事务的第一次查询生成readView。
- 创建一个对象的流程 引用放在哪里
参考内容
分为五个步骤:类加载检查、分配内存、初始化零值、设置对象头、执行方法
1.类加载检查:遇到new指令时去常量池检查是否可以定位到这个类的符号引用以及这个类是否被加载到JVM中。
2.分配内存:为对象分配内存,对象所需大小在类加载完成后即可确定.
分配内存有两种方式,具体采用哪种和垃圾收集器有关
(1)指针碰撞法
(2)空闲列表法
3.初始化零值
4.设置对象头
5.执行init
- 可能会问以后规划,个人优势
规划要根据具体公司地点、工作形势去说,个人优势自己总结就好了,比如性格开朗能迅速融入团队、技术过关在实习期间得到组内认可、心态稳定在实验室项目中可以踏实研究等等。
- redis实际操作
- java和c++区别
编译型 :编译型语言 会通过编译器将源代码一次性翻译成可被该平台执行的机器码。一般情况下,编译语言的执行速度比较快,开发效率比较低。常见的编译性语言有 C、C++、Go、Rust 等等。
解释型 :解释型语言会通过解释器一句一句的将代码解释(interpret)为机器代码后再执行。解释型语言开发效率比较快,执行速度比较慢。常见的解释性语言有 Python、JavaScript、PHP 等等。
Java 程序要经过先编译,后解释两个步骤,由 Java 编写的程序需要先经过编译步骤,生成字节码(.class 文件),这种字节码必须由 Java 解释器来解释执行。
c++有多继承,java只有单继承。
c++有指针,c++没有内存管理。
这种问题随便说几个就好
- 接口和抽象类
共同点:
(1)都不能被实例化
(2)都可以包含抽象方法
(3)都可以有默认实现的方法(java 1.8 之后接口可用default关键字定义默认方法)
区别:
(1)接口一般用于行为约束,实现了一个接口代表该类具有了某种行为,而抽象类主要用于代码复用,表达的是类之间的所属关系
(2)一个类只能继承一个抽象类,但可以实现多个接口
(3)接口中的方法只能是public abstract 只有定义(除了default),接口中的成员变量public static final 不能被修改且必须有初值 ,抽象类中的方法可以任意定义,也可以有默认实现,抽象类的成员变量默认default,可在子类中重新定义也可被重新赋值。
- git合并分支处理冲突,具体命令
实习期间使用的命令:
git pull;git push;git merge;git fetch;git add .;git commit -m “”;git log;git reflog;git reset;
merge 之后会提示冲突 在ide中解决再重新commit即可,也可以右键点击项目-git-conflict xxx(大概是这个)
- spring boot和spring区别
spring两大核心IOC&&AOP,两大功能大大降低了代码量并提高了扩展性,在第一次使用Spring框架时,搭配Spring-mvc、myBatis一起使用,要配置很多xml文件,十分麻烦,springBoot采取约定大于配置的思想,在添加第三方库时只需要加入很少量的配置信息即可。
可以说Spring和SpringBoot的最大区别是SpringBoot的自动装配功能:SpringBoot自动装配 介绍的很详细
简单记录几个注解:
@SpringBootApplication:启动类上的注解 用来标明这个类是当前项目主配置类,执行该类的main方法启动程序。
@EnableAutoConfiguration:该注解主要由@AutoConfigurationPackage和@Import注解组成,@AutoConfigurationPackage将主配置类(@SpringBootApplication标注的类)所在包以及子包里面的所有组件扫描并加载到spring的容器中,@Import({EnableAutoConfigurationImportSelector.class})注解根据spring.factory文件获取需要自动装配的类并以全类名返回。
@ComponentScan:这个注解作用就是扫描当前包及其子包的注解。
- jvm如果发生OOM怎么办,OOM会发生在哪里
OOM全程 Out Of Memory Error,可能发生在堆、栈、方法区,只有程序计数器不会发生溢出
堆内存溢出
方法区溢出 方法区存放已加载的类信息、常量、静态变量,也可能会内存溢出。
虚拟机栈 StackOverflowError 一般由于死循环等造成
- 为什么项目中要使用hashmap,如果存在安全问题怎么办
- sychronized后面怎么优化的,不使用这个怎么并发
jdk6以后 Java官方从JVM层面堆Sychronized做了优化:CAS自旋、锁消除、偏向锁、轻量级锁等。
锁有四种状态:无锁->偏向锁->轻量级锁->重量级锁.
偏向锁其实是可重入的一个过程,在对象头记录存储偏向锁的线程id,以后该线程在进入\退出时只需要验证对象头的MarkWord字段即可。
轻量级锁目的是减少无竞争情况下上下文切换的性能损耗,通过CAS自旋实现。
不使用sychronized如何线程安全
- redis的几种数据结构
- mysql如果创建一个字符串使用哪种,几种数据类型
- varchar和char什么区别,
- war包和jar包区别
- static 可以修饰类吗
- oom怎么处理 虚拟机内存设定多大
- finally
- 二进制的位权
- maven具体命令
- java -jar xxx.jar
- final关键字
- 自己写个string
- 类加载
- linux命令
- https 细看 先tcp再tls握手
- 对称加密和非对称加密
- 讲一下JMM
- mysql有几种锁
- 怎么实现乐观锁和悲观锁
- 乐观锁 mvcc readview 悲观锁 select for update
- 哪些sql语句可以用的悲观锁
- select for update是表锁还是行锁
有明确指定主键或者索引时用行锁,否则表锁
加了索引一定走索引吗,索引实现情况有哪些,如何强制走某个索引
查询条件有函数运算操作,查询条件有隐式转换,模糊查询%在前,or和不等于,都不走索引
强制走索引用force index - tomcat作用
(1)管理servlet应用的生命周期
(2)把客户端请求的url映射到对应的servlet
(3)与Servlet程序合作处理HTTP请求
-
说一下JVM内存模型
-
spring bean的生命周期
-
spring boot自动装配
-
spring boot为什么没有web.xml
-
java如何读取一个10.g的大文件
-
java的api提供
-
过滤器和拦截器区别
-
原理不同,使用范围不同,触发时机不同,拦截的请求范围不同
-
AQS
-
自己实现ioc思路
-
多线程模拟红绿灯
-
没搞透彻
-
sql用户权限
-
tcp udp如何保证可靠传输
-
redis实现各种功能有什么用
-
华为一面
-
线程池的几种淘汰策略,
-
计算机网络io了解下,java中用的哪个
-
单点登录怎么做
-
分布式内容
-
java解决死锁
-
重新定义执行顺序,使用trylock
-
mysql优化
-
syxhronized和reentrantlock
-
为什么要有虚拟地址
-
华为文化,如何看待华为等
-
tor和普通代理区别
-
dns用什么协议
-
treemap也看一眼
-
java对象创建过程
-
工厂模式再看一下
-
select poll epoll
-
不用序列化怎么解决幻读
-
线程池为什么快,七大核心参数、饱和拒绝策略分别是什
-
MySQL优化,怎么优化慢查询
-
观察者模式 几种设计模式
-
spring事务传播行为
-
java 锁 细致一点的内容
-
表锁行锁以及更细致的
-
redis缓存击穿和穿透
-
redis使用场景 kafka
-
阻塞队列相关内容 延时队列
-
flink什么东西
-
mysql各种锁
-
可重入锁概念
-
线程池参数,时间有什么用
-
java读写锁
-
多态 动态绑定内容
-
union 和 union all
-
为什么要学设计模式
-
sql 查询 0-100 分 每十分一个段的人数
-
UDP的缩写是什么
-
TCP保证可靠性 如果使用UDP如何保证可靠性,微信用的是什么
-
Rpc和Http
-
sychronized在jdk1.6之后做了哪些优化
-
双亲委派模型如何打破?为什么需要打破?
-
阻塞状态和等待状态
-
垃圾回收器CMS
-
https有哪些加密
-
建索引都需要考虑什么