一、基础
1、创建对象的方法
new、反射、clone、反序列化
Class cl = Class.forName("TestMe");
2、有哪些引用类型
强引用、软引用、弱引用、虚引用
3、JVM 类加载机制
双亲委派机制(Parent-Delegate Model)是Java类加载器中采用的一种类加载策略,如果一个类加载器收到了类加载请求,默认先将该请求委托给其父类加载器处理。
只有当父级加载器无法加载该类时,才会尝试自行加载。避免重复加载,提高安全性
4、反射怎么实现
因为Java虚拟机会把类加载到方法区,并且保留Class对象作为每个类的元对象,通过这个对象就可以获取该类的信息,从而获取它的方法,构造函数,变量等内容。
5、JDK11的新特性
局部变量类型推断 var message = "Hello, World!";
Java JDK 11引入了一个新的HTTP Client API,用于进行HTTP通信
6、JVM类加载过程
JVM类加载过程分为:加载 、链接 、初始化 、使用 、卸载 这五个阶段,其中链接阶段又包括: 验证 、 准备 、 解析
7、JVM内存模型
JVM 内存模型主要分为堆、方法区、栈区(程序计数器、虚拟机栈和本地方法栈)。其中,堆和方法区被所有线程共享需要定期被GC,虚拟机栈、本地方法栈、程序计数器
是线程私有的,在超出作用域后会被JVM自动释放掉
二、数据库
1、redis的优点和可以存储的数据结构,zset的底层实现
速度快(数据存在内存)、持久化(允许将内存中的数据定期写入磁盘)、支持事务操作(原子性);string、list、set、zset、hash
2、MySql索引的实现原理,为什么用B+树不用二叉树
索引的实现一般是B树或B+树(InnoDB使用)
B+ 树的特点是能够保持数据稳定的顺序,并且非叶子节点只存储键值信息,而叶子节点存储了完整的数据行,同时每个叶子节点都包含了指向下一个叶子节点的指针,形成了一个有序的数据链表
减少了磁盘IO的次数(树的高度决定)
树的高度较低,提高了查找效率
叶子节点之间有链指针,方便范围查询
3、什么是分库分表?怎么分?为什么要分?
分库是指在表数量不变的情况下对库进行切分。
分库分表是指库和表都切分,数量都发生变化。
水平分(数据结构不变)、垂直分(数据结构变)
出现性能瓶颈需要分库分表,增加维护难度
4、怎么解决并发带来的问题
事务隔离、乐观锁、查询优化、读写分离
5、脏读、幻读、不可重复读以及数据库的事务隔离机制
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据
幻读是指在一个事务内,多次读同一数据,由于第二个事务的修改,导致两次读到的数据不一致
事务A查询数据的时候,事务B删除或增加了多个数据,导致事务A查询的数据集发生了变化
读未提交、读已提交(防止脏读)、可重复读(避免不可重复读)、串行化
6、分页的方法
使用LIMIT(SELECT * FROM example_table LIMIT 10;)
使用LIMIT OFFSET(SELECT * FROM example_table LIMIT 10 OFFSET 10;)
动态构建分页查询
SET @page := 2; -- 当前页码
SET @pageSize := 10; -- 每页显示的记录数
SELECT * FROM example_table LIMIT @pageSize OFFSET (@page - 1) * @pageSize;
7、mysql的行锁有几种,什么情况下会锁住整张表
锁按照操作粒度从小到大可以划分为行锁、页面、表锁(锁整张表)
8、MyISAM 与 InnoDB
INNODB会支持一些关系数据库的高级功能,如事务功能和行级锁,MYISAM不支持。MYISAM的性能更优,占用的存储空间少
8、慢查询
缺乏索引、复杂的查询、大量的数据、硬件限制和配置不当
使用mysqldumpslow工具分析慢查询日志,做sql优化,将慢查询日志中的SQL语句复制到查询窗口,在前面加上EXPLAIN,
运行查看查询的执行计划,包括是否使用了索引、数据扫描的行数等
9、怎么解决死锁
SHOW PROCESSLIST查看当前运行的进程,死锁会被标记为waiting for lock
三、springboot
1、springboot和spring的区别
简化了配置
2、spring常用注解
@SpringBootApplication,复合注解
3、SpringBoot如何启动的Tomcat
内嵌的Tomcat服务器是通过Spring Boot Starter Web模块提供的功能来启动的
tomcat的作用:解析用户的http请求
4、Spring Boot 如何定义多套不同环境配置?
使用不同的配置文件、使用 @Profile 注解来加载不同的配置文件
5、spring的核心思想
IOC(控制反转):将组件间的关系从程序内部转移至外部容器(xml文件)中进行管理。
DI(依赖注入):组件间的依赖关系由系统运行期间决定。外部容器将带有依赖关系的目标对象实例动态注入到系统中的各个组件中。
AOP(面向切面编程):AOP可以进一步将业务无关的但是与业务模块共同调用的逻辑操作单独封装起来,减少重复代码,降低耦合性,提高维护性。
通过spring来判断数据的合法性
6、spring启动流程
Spring启动流程主要包括以下几个步骤:
启动Spring容器:通过ApplicationContext实例化Spring容器,可以是ClassPathXmlApplicationContext、FileSystemXmlApplicationContext或者
基于注解的AnnotationConfigApplicationContext。
读取配置文件或注解配置:Spring容器读取配置文件或注解,并解析Bean定义。
实例化Bean:基于Bean定义,Spring创建Bean的实例。
设置Bean属性:为Bean的属性设置值和对其他Bean的引用。
初始化Bean:如果Bean实现了InitializingBean接口,调用afterPropertiesSet()方法,如果在配置文件中定义了init-method,调用指定的初始化方法。
注册Bean到Spring容器:Bean现在可以被Spring容器使用了。
四、数据结构
1、hashmap的底层实现
将k,v封装到Node对象当中,算出k的hash值,将hash值转换成数组的下标,下标位置如果不止一个元素,拿k和链表上每个节点的k进行equal,为什么要比较k值
初始size是16,每次扩容为2的n次幂,怎么扩容的
2、ArrayList 和 LinkedList、Vector的区别是什么
ArrrayList 底层的数据结构是数组,支持随机访问,而 LinkedList 的底层数据结构书链表,不支持随机访问,双向链表,怎么插入数据的
3、哪些集合类是线程安全的
集合主要分为Collection和Map两大接口,
Vector(方法同步,处理大量数据性能不行)、Hashtable(synchronized关键字)、ConcurrentHashMap(分段锁机制)
4、Iterator迭代器的实现原理
5、hashmap和hashtable的区别
Hashtable 不允许有个空的 key,但是 HashMap 允许出现一个 null key
Hashtable是线程安全的,每个方法都加了synchronized 关键字
6、ArrayList的底层?为什么加载因子是0.75?如何扩容
考虑到性能和容量的平衡
7、ConcurrentHashMap怎么实现线程安全
采用分段锁机制,类似悲观锁的实现,每一段操作都需要对应的锁
五、垃圾回收
1、常用GC算法
引用计数器:为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题;
可达性分析: 从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Root 没有任何引用链相连时,则证明此对象是可以被回收的。
根搜索算法、标记-清除算法、分代算法
2、介绍下垃圾回收器CMS
如果说垃圾收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现;
采用标记-清除算法,产生大量的内存碎片
3、频繁产生Full GC是什么原因,怎么排查
系统并发高、执行耗时过长,或者数据量过大
存在内存溢出的情况
程序一次性加载过多对象到内存
排查:通过jmap来获取dump文件,通过JDK自带的jvisualvm工具分析
4、堆内存和栈内存的区别,堆区怎么做内存管理
对象都在堆内存,定期被GC,
堆内存会分成young区和old区,young区又分成eden区和两个surviver,new对象时,会分配在eden区,对象经过gc后分代年龄增加,达到阈值进入old区
5、StackOverFlowError的场景,怎么排查
调用(没有结束递归条件的)递归函数
创建大量的线程或方法,使得栈帧超量
检查代码中是否有递归调用,并确保有适当的退出条件
如果是多线程,检查线程切换是否正确
6、OutOfMemoryError(内存溢出)
栈内存溢出:创建了太多线程,没有足够的空间为一个新的线程分配空间
堆内存溢出:创建了太多对象,没有足够的空间为一个新的对象分配空间
检查代码中是否有大对象分配,如大数组或大字符串
申请更多内存,调整虚拟机的启动参数
使用内存分析工具(如 VisualVM, JProfiler, MAT)来找出内存泄漏
-Xms1500m(起始) -Xmx1500m(最大) -XX:PermSize=125M -XX:MaxPermSize=256M
7、内存泄露和内存溢出的区别
后者是持续累计的结果,内存灭有手动释放
解决内存溢出的方法通常包括优化数据结构和算法、减少内存消耗或增加系统可用内存
解决内存泄漏则需要定位泄漏源头,修复代码中的内存管理问题,确保不再使用的内存能够被及时释放
六、多线程
1、线程和进程的区别,线程有哪些状态
初始、运行、阻塞、等待、超时等待、终止
继承Thread类、实现Runnable接口、实现Callable接口、使用线程池
怎么知道某个线程的执行情况,future
3、线程池的参数介绍下,有什么拒绝策略
corePoolSize:核心线程数
maximumPoolSize:最大线程数
keepAliveTime:空闲线程存活时间
TimeUnit:时间单位
BlockingQueue:线程池任务队列
ThreadFactory:创建线程的工厂
RejectedExecutionHandler:拒绝策略
将任务添加到队列中,分为有界队列和无界队列,使用submit或者execute来触发队列中任务的执行
4、wait和notify介绍下
5、什么是死锁,怎么避免,乐观锁和悲观锁
乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。
版本号机制。为数据表增加一个版本号(version)字段,记录数据被修改的次数。当数据被读取时,版本号也随之读出。在更新数据前,需要比对当前读取到的版本号与数据库中记录的版本号。只有当两者相同时,数据才会被更新。如果版本号不同,说明数据在读取后被其他线程修改过,此时更新操作会重试或被拒绝。
时间戳机制。与版本号机制类似,但使用时间戳(timestamp)字段来记录数据最后一次更新的时间。更新操作时,需要比对提交的时间戳与数据库中记录的时间戳。只有当两者相同时,更新才会被执行。
悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻止,直到这个锁被释放。
6、什么是线程安全问题,怎么实现线程安全
多个线程同时操作同一个共享资源的时候可能会出现业务安全问题(买票,不同窗口卖同一张票)
线程同步,synchronized同步方法或者同步代码块,一前一后执行,不竞争;核心思想是把共享资源加锁,每次只能一个线程访问完毕以后解锁
七、设计模式
1、策略模式
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换
2、工厂模式
解决接口选择的问题
3、手写单例模式
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
八、网络协议
1、websocket有什么优点
双向通信、实时性、长连接、
2、session和cookie的区别,cookie被禁了怎么办
cookie数据存放在客户的浏览器上(不安全),session数据放在服务器上
URL重写:URL重写要求将站点中的所有超链接都进行改造,在超链接后用一个特殊的参数JSESSIONID保存当前浏览器对应session的编号
用文件、数据库等形式保存Session ID,在跨页过程中手动调用
3、tcp粘包
由于接收方和发送方的缓冲区大小不一致,导致接收方一次性收了一个半的数据
两个数据之间设定特殊的符号、包头添加数据包的长度
4、tcp为什么要三次握手
如果采用两次握手,那么只要服务器发出确认数据包就会建立连接,但由于客户端此时并未响应服务器端的请求,
那此时服务器端就会一直在等待客户端,这样服务器端就白白浪费了一定的资源。若采用三次握手,服务器端没
有收到来自客户端的再此确认,则就会知道客户端并没有要求建立请求,就不会浪费服务器的资源。
5、http和https的区别
安全版、加密传输