*写在最前:以下面试题目均为本人自己结合面试java初中级工程师遇到的高频问题,做的总结
version:2.0
更新日期:2022-12-29
---------2023-1-9分界线-------------------
入职二线城市一家自研公司,亲测有效哈哈,近期可能更新不频繁,用到新的内容再更新~
基础部分
1. java中创建对象的方式:
- new创建新对象
- 通过反射创建新对象
- 采用clone机制
- 通过序列化机制
2. 线程池满了怎么办
- 当你使用的是无界队列(LinkedBlockingQueue)的时候,可以添加到阻塞队列中等待执行,无界队列可以无限的存放任务
- 当你使用的是有界队列(ArrayBlockingQueue)的时候,任务首先会往队列中添加,满了以后,根据maximumPoolSize 的值增加线程,如果还是处理不过来,则会使用拒绝策略处理满了的任务
- 线程池四大拒绝策略
1.AbortPolicy
直接丢弃任务,抛出RejectedExecutionException异常,是默认策略
2.CallerRunsPolicy
只用调用者所在的线程处理任务
3.DiscardOldestPolicy
丢弃等待队列中最旧的任务,并执行当前任务
4.DiscardPolicy
直接丢弃任务,但不抛出异常
3. 简述一下你对线程池的理解
- 第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁对资源的消耗
- 第二:提升响应速度。不需要等待新建线程就能立即执行
- 第三:提升线程的可管理性。避免无限制创建线程,造成资源浪费,降低系统的稳定性,使用线程池可以统一监控,调配和调优
3.1 创建一个线程的方法
new Thread(), new Runnable, new Callable
3.2 创建线程池的方法
-
new ThreadPoolExecutor() ,参数比较多,见3.3
-
Executors.newCachedThreadPool(线程池类型),参数帮我们设置好了
3.2.1 线程池有哪几种
- newCachedThreadPool
- newFixedThreadPool
- newScheduledThreadPool
- newSingleThreadExecutor
3.3 创建线程池有哪些参数
- CorePoolSize 核心线程数
- maximumPoolSize 最大线程数
- keepAliveTime 非核心线程闲置时的超时状态时长
- unit keepAliveTime 时长对应的单位
- workQueue 用来放提交的Runnable对象
- ThreadFactory 线程工厂,创建新线程
- RejectedExecutionHandler 超过最大容量的执行策略
3.4 线程执行的顺序,有什么方法a执行完必须才能执行b线程
线程执行的顺序是不一定的,如果需要有先后顺序,使用join()。Thread.join()
4. 类的初始化流程
-
记忆:(顺序如下)
1. 先加载父类的静态属性+静态代码块->加载子类的静态属性+静态代码块
2.子类普通属性(这里不知道为什么,暂且记住)
3. 父类的普通属性+普通代码块+构造方法,子类的普通属性+普通代码块+构造方法
-
通过设置场景,得出结果:
4.1类加载流程
5. 线程安全的list
- vector:比较老
- synchnorizedList:使用Collections.synchronizedList(list); 将list包装成SynchronizedList
6.hashmap、红黑树、B+树
- java8之前,hashmap采用的是数组+链表的结构存储数据,java8之后,引入了红黑树,变成了数组+链表+红黑树的数据结构,我们知道,在数组中可以根据hash值快速的找到对应的数组下标,但是之后的话,我们需要顺着链表一个个对比,此时时间复杂度O(n),当数组中的数据>64并且链表的长度>8时,这条链表会转化成红黑树,树化后可以将查找的时间复杂度缩短至O(log n)
- 红黑树:红黑树是一种弱平衡二叉树,在每个节点增加一个存储位表示节点的颜色,通过对任意一条从根到叶子节点的着色方式的限制,可以保证没有任何一条路径会比其他路径长2倍,所以相对严格要求的AVL树来说,他的旋转次数比较少,对于数据插入,删除,搜索较多的情况,使用红黑树
- B+树:B就是balance的意思,他的所有叶子节点存放在同一层,并且通过顺序指针的方式提高查询性能,对于查询比较多的情况,例如数据库来说,是比较划算的。如果需要插入或者删除,需要对树进行分裂,合并,旋转等操作来维护平衡性。主要是在所有的叶子结点中增加了指向下一个叶子节点的指针,因此 InnoDB 建议为大部分表使用默认自增的主键作为主索引。
7. java中IO流
- 按照流的流向划分,可以分为输入流和输出流;
- 按照操作单元划分,可以分为字符流和字节流
- 按照流的角色划分,可以分为节点流和处理流
8.四种线程池
- NewCachedThreadPool
- NewFixedThreadPool
- newScheduledThreadPool
- NewSingleThreadExecutor
9. sleep()和wait()有什么区别
- sleep是属于Thread类中的,而wait是属于Object类中的
- sleep方法会导致程序执行暂停指定时间,cpu让出该线程,但是依然保持监控状态,当指定时间到了又会自动进入运行状态
- sleep时,线程不会释放锁
- wait时线程会释放锁,只有调用Notify方法后才会进入对象锁定池获取对象进入运行状态
10. 数据库连接池
- 数据库的连接是非常消耗资源的,影响到程序的性能指标(每次建立连接都需要验证地址,用户名和密码)。连接池是用来存放数据库连接的,可以让应用程序重复的使用同一个数据库连接,而不是每次都创建一个新的连接。
- 通过释放长时间不用的连接,避免数据库因为创建太多的连接而导致的连接泄露问题,提高了程序的性能
11. 单例模式的简单实践
-
所谓的单例模式:就是采取一定的办法保证整个软件系统中,某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
-
单例模式有懒汉式和饿汉式:
饿汉式:构造器私有化,在类的内部提前创建好一个对象的实例,提供一个static方法,调用时返回
懒汉式:构造器私有化,提供一个static方法,调用时才会创建一个对象的实例返回
-
懒汉式和饿汉式区别:
- 最大的区别在于创建的时机不同,饿汉式在类加载的时候就创建了,而懒汉式在使用时才创建
- 饿汉式不存在线程安全问题,懒汉式存在
- 饿汉式可能存在浪费资源问题,因为如果我们不使用的话,就白创建这个实例对象了
12. HTTP协议跟RPC协议区别
- RPC可以理解为一种调用方式,目的是像调用本地方法一样调用远端的方法,而不需要过多的在意过程,甚至可以用HTTP作为RPC底层的实现
- RPC本身并不是一个具体的协议,适用于应用服务之间的调用,而HTTP协议适用于浏览器到服务器之间的调用
- HTTP协议分为请求头(Header)和请求体(Body)
13. list有哪几种遍历方式
普通for遍历,增强for循环,lambda遍历(2种,一种是stream流用到的,一种是::),迭代器+while,迭代器+for,自带迭代器listIterator循环遍历
14. 事物的ACID
事务的四大特性主要是:
- 原子性(Atomicity),强调事务的不可分割
- 一致性(Consistency),事务的执行的前后数据的完整性保持一致.
- 隔离性(Isolation),一个事务执行的过程中,不应该受到其他事务的干扰
- 持久性(Durability),事务一旦结束,数据就持久到数据库
15. synchornized关键字
面试官问题是:synchornized在不同的地方,三种方法加锁的区别
-
修饰实例方法:会给当前对象的实例加锁,进入同步代码之前要先获得当前实例的锁
-
修饰静态方法:会给当前的类上锁,因为静态成员属于类的成员,不管new多少个对象,都只有一份,所以访问被修饰的静态方法时需要获得这个class类的锁
-
修饰代码块:会给当前类上锁
总结:synchornized关键字作用在静态方法和代码块时,都是给类上锁,加到实例方法上是给对象上锁
15.1 synchronized 和 ReentrantLock 的区别(摘抄)
两者的共同点:
- 都是用来协调多线程对共享对象、变量的访问
- 都是可重入锁,同一线程可以多次获得同一个锁
- 都保证了可见性和互斥性
两者的不同点:
- ReentrantLock 显示的获得、释放锁,synchronized 隐式获得释放锁
- ReentrantLock 可响应中断、可轮回,synchronized 是不可以响应中断的,为处理锁的不可用性提供了更高的灵活性
- ReentrantLock 是 API 级别的,synchronized 是 JVM 级别的
- ReentrantLock 可以实现公平锁
- ReentrantLock 通过 Condition 可以绑定多个条件
- 底层实现不一样, synchronized 是同步阻塞,使用的是悲观并发策略,lock 是同步非阻塞,采用的是乐观并发策略
- Lock 是一个接口,而 synchronized 是 Java 中的关键字,synchronized 是内置的语言实现。
- synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而 Lock 在发生异常时,如果没有主动通过 unLock()去释放锁,则很可能造成死锁现象,因此使用 Lock 时需要在 finally 块中释放锁。
- Lock 可以让等待锁的线程响应中断,而 synchronized 却不行,使用 synchronized 时,等待的线程会一直等待下去,不能够响应中断。
- 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。
- Lock 可以提高多个线程进行读操作的效率,既就是实现读写锁等。多个读取线程使用共享锁,写线程使用排它锁/独占
16. @Getting能接收放到body中的数据吗?不能
- HTTP协议不允许
17. == 和 equals 的对比
- == 是一个比较运算符,既可以比较基本数据类型,也可以比较引用数据类型
- ==如果判断基本数据类型,判断的是值是否相等
- ==如果是判断引用数据类型,判断的是地址是否相等,即是否是同一个对象
- equals是object类中的方法,只能用来判断引用数据类型
- 默认判断的是地址是否相等,子类中往往会重写该方法,用于判断内容是否相等,比如String和Integer都会重写equals方法
18. 完了看看CopyOnWriteArrayList,CopyOnWriteArraySet
19. 进程跟线程
- 程序:用某种语言写的一组指令的集合
- 进程:运行中的程序,有产生、存在、消亡的过程。操作系统会给进程分配内存空间
- 线程:线程是由进程创建的,是进程的一个实体
框架部分
1. spring事务的传播机制:
- 事务的传播特性指的是不同方法的嵌套调用过程中,事务应该如何进行处理,是用同一个事务还是不同的事务,当出现异常的时候会回滚还是提交,两个方法之间的相关影响
-
-
默认的传播机制是:REQUIRED
常见的事务传播方法有两个:REQUIRED,REQUIRES_NEW
-
事务传播机制的设置方法
在方法上的@Transactional(中设置)
如果设置为requires_new的话,buyGoods2 事务出现错误,不会影响到buyGoods,反之亦然,也就说明他们之间时相互独立的
如果设置为required的话,buyGoods2 出现错误,会影响到buyGoods,反之亦然,也就说明他们之间不是相互独立的
-
2. spring的注入方式
- setter注入、构造器注入、注解注入、
3. mybatis里边的#跟$有什么区别
-
都是用来传入参数的,#传入的参数默认是字符串类型,$传入的参数直接显示为传入的值
-
#可以防止sql注入,$无法防止sql注入
-
多数时候选择用#,除非mybatis使用order by动态参数必须用$
另一个版本:
#{}是预编译处理,${}是字符串替换。
Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的set 方法来赋值;
Mybatis 在处理 时,就是把 {}时,就是把 时,就是把{}替换成变量的值。
使用#{}可以有效的防止 SQL 注入,提高系统安全性
4. spring 的生命周期
5. 过滤器filter,拦截器interceptor,监听器listener
-
过滤器和拦截器的区别
过滤器 和 拦截器 均体现了AOP的编程思想,都可以实现诸如日志记录、登录鉴权等功能,但二者的不同点也是比较多的。
-
实现原理不同:过滤器和拦截器 底层实现方式大不相同,过滤器 是基于函数回调的,拦截器 则是基于Java的反射机制(动态代理)实现的。
-
使用范围不同: 我们看到过滤器 实现的是 javax.servlet.Filter 接口,而这个接口是在Servlet规范中定义的,也就是说过滤器Filter 的使用要依赖于Tomcat等容器,导致它只能在web程序中使用。
而拦截器(Interceptor) 它是一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于Application、Swing等程序中。、
-
触发时机不同。过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。拦截器 Interceptor 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。
-
拦截的请求范围不同
过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对Controller中请求或访问static目录下的资源请求起作用。 -
访问范围
filter在servlet前后起作用,拦截器能够深入方法的前后,异常抛出前后
-
监听器
web监听器是Servlet中-种的特殊的类,能帮助开发者监听web中的特定事件,比如ServletContext,HttpSession, ServletRequest 的创建和销毁;变量的创建、销毁和修改等。可以在某些动作前后增加处理, 实现监控。例如可以用来统计在线人数等。
6.Spring核心注解
启动类的@SpringBootApplication,主要包含了以下三个注解:
- @CompomentScan:spring组件扫描
- @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfifiguration.class })。
- @SpringBootConfiguration:组合了@Configuration注解,实现配置文件的功能
数据库
1. 动态sql语句
-
if
-
where
-
choose/when/otherwise 类似与java的 if / else if / else
-
trim 标签应用实例【使用较少】,可以替换一些关键字
-
set–重点
面试中问了如何批量更新表中数据,当时没答上来
这里应该回答用mybatis动态sql语句:
update monster
<set>
<if test="age != null and age != '' ">
age = #{age},
</if>
<if test="name != null and name != '' ">
name = #{name},
</if>
</set>
where id = #{id}
2. mysql索引
2.1 创建索引的规则
3. mysql的存储引擎有哪些
- mysql常用引擎包括:MYISAM、Innodb、Memory、MERGE
- MYISAM:全表锁,拥有较高的执行速度,不支持事务,不支持外键,并发性能差,占用空间相对较小,对事务完整性没有要求,以select、insert为主的应用基本上可以使用这引擎
- Innodb:行级锁,提供了具有提交、回滚和崩溃恢复能力的事务安全,支持自动增长列,支持外键约束,并发能力强,占用空间是MYISAM的2.5倍,处理效率相对会差一些
- Memory:全表锁,存储在内存中,速度快,但会占用和数据量成正比的内存空间且数据在mysql重启时会丢失,默认使用HASH索引,检索效率非常高,但不适用于精确查找,主要用于那些内容变化不频繁的代码表
- MERGE:是一组MYISAM表的组合
4. 事物的ACID
事务的四大特性主要是:
- 原子性(Atomicity),强调事务的不可分割, undo-log来保证
- 一致性(Consistency),事务的执行的前后数据的完整性保持一致.
- 隔离性(Isolation),一个事务执行的过程中,不应该受到其他事务的干扰
- 持久性(Durability),事务一旦结束,数据就持久到数据库 redo-log来保证
5. sql优化
1、查询语句中不要使用select *
2、尽量减少子查询,使用关联查询(left join,right join,inner join)替代
3、减少使用IN或者NOT IN ,使用exists,not exists或者关联查询语句替代
4、or 的查询尽量用 union或者union all 代替(在确认没有重复数据或者不用剔除重复数据时,union all会更好)
5、应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
6、应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如: select id from t where num is null 可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: select id from t where num=0
6.大表如何优化
当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下:
- 限定数据的范围:禁止不带任何数据范围条件的查询,比如查询订单的时候,控制范围为一个月
- 读写分离。经典的数据库拆分方案,主库负责写,从库负责读;
- 垂直分区。根据数据库中数据的相关性,可以把原来比较多的列,拆分成不同的数据库
- 水平分区。表结构不变,把数据分别保存在不同的表,库中
7.MySQL 中有哪几种锁?
1、表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
2、行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
3、页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般
8.实践中如何优化 MySQL
最好是按照以下顺序优化:
1、SQL 语句及索引的优化
2、数据库表结构的优化
3、系统配置的优化
4、硬件的优化
9.优化数据库的方法
1、选取最适用的字段属性,尽可能减少定义字段宽度,尽量把字段设置 NOTNULL,例如’省份’、’性别’最好适用 ENUM
2、使用连接(JOIN)来代替子查询
3、适用联合(UNION)来代替手动创建的临时表
4、事务处理
5、锁定表、优化事务处理
6、适用外键,优化锁定表
7、建立索引
8、优化查询语句
10.sql注入的产生
程序开发过程中不注意规范书写 sql 语句和对特殊字符进行过滤,导致客户端可以通过全局变量 POST 和 GET 提交一些 sql 语句正常执行。
10.1防止sql注入
防止 SQL 注入的方式:
开启配置文件中的 magic_quotes_gpc 和 magic_quotes_runtime 设置 执行 sql 语句时使用 addslashes 进行 sql 语句转换
Sql 语句书写尽量不要省略双引号和单引号。
过滤掉 sql 语句中的一些关键词:update、insert、delete、select、 * 。
提高数据库表和字段的命名技巧,对一些重要的字段根据程序的特点命名,取不易被猜到的。
11.描述一下聚簇索引和非聚簇索引的区别?
innodb存储引擎在进行数据插入的时候必须要绑定到一个索引列上,默认是主键,如果没有主键,会选择唯一键,如果没有唯一键,那么会选择生成6字节的rowid,跟数据绑定在一起的索引我们称之为聚簇索引,没有跟数据绑定在一起的索引我们称之为非聚簇索引。
innodb存储引擎中既有聚簇索引也有非聚簇索引,而myisam存储引擎中只有非聚簇索引。
12. mysql主从复制
-
上图解读:master中的写操作会记录在Binarylog中,slave中的I/O thread定期会拉取master中的bin log,将其保存到relay log(中继日志)中,再由sql thread重新,保证主从数据的同步
12.1mysql主从复制主要有几种模式?
- 一主一从
- 主主复制
- 一主多从
springcloud && springcloud alibaba
一、seata
1.seata的执行流程
中间件
一、redis
1. RDB和 AOF 的区别
面试的时候问了,但是感觉思路不清晰,没有回答清楚
- RDB是redis默认的持久化方式,按照一定的时间周期策略把内存中的数据以快照的形式保存到硬盘中,对应产生的数据文件叫做dump.rdb,通过定义文件中的save来设置快照的周期
- AOF,redis会将收到的每一个写操作都通过writer函数追加到文件最后,类似于mysql的binlog,当redis重启后会重新执行文件中的写命令来重建整个数据库的内容。
- 当两种方式都开启的话,redis默认使用AOF恢复
3.redis五大数据结构:
String,list,set(无序去重),zset(有序去重),hash
4.怎么实现消息的顺序执行:
使用redis的事务,具体操作指令如下:
Multi、Exec、discard
从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行。组队的过程中可以通过discard来放弃组队。
- 如果组队阶段报错,那么所有的提交都会失败
- 如果执行阶段报错,那么只有报错的不会被执行,其他都会执行,不会回滚
5.redis解决超卖问题
通过lua脚本解决争抢问题,实际上是redis 利用其单线程的特性,用任务队列的方式解决多任务并发问题。
6.redis 过期键的删除策略?
-
redis采用的是定期删除+惰性删除策略。
1、定时删除:在设置键的过期时间的同时,创建一个定时器 timer). 让定时器在键的过期时间来临时,立即执行对键的删除操作。
2、惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返 回该键。
3、定期删除:每隔一段时间程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。
7. redis在项目中的应用?
- 项目中redis的使用:生成验证码用到了redis+会话缓存+计数器功能+分布式锁
具体操作:当调用发送验证码方法时,根据手机号信息先去redis中查找是否已经有数据,如果没有就说明是第一次发送,将其保存到redis中并且设置好过期时间以及发送次数,Random生成一个随机的数字验证码,返回给前端并且保存到redis中,设置60s过期时间。
会话缓存用于不是严格要求一致性的情况,比如购物车,redis的优势在于支持持久化
计数器功能用于统计浏览量
总结如下:(redis的可用场景)
-
计数器
可以对 String 进行自增自减运算,从而实现计数器功能。Redis 这种内存型数 据库的读写性能非常高,很适合存储频繁读写的计数量。
-
缓存
将热点数据放到内存中,设置内存的大使用量以及淘汰策略来保证缓存的命中率。
-
会话缓存
可以使用 Redis 来统一存储多台应用服务器的会话信息。当应用服务器不再存 储用户的会话信息,也就不再具有状态,一个用户可以请求任意一个应用服务 器,从而更容易实现高可用性以及可伸缩性。
-
分布式锁实现
在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。可 以使用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还可以使用官方提 供的 RedLock 分布式锁实现。
8. redis需要定制延期过期时间怎么操作
在申请锁时,设置一个延长过期时间,每到过期时间的三分之一就重新设置过期时间。具体就是加锁后将需要延期的任务信息放入一个map中,只要锁没有被释放,就脚本执行map中任务的延期操作
9. 锁丢失的问题有研究吗?
锁丢失的原因可能是:
在redis的master节点获取了锁,还没来得及同步到slave节点,此时master节点发生故障,导致锁丢失。
解决方案
使用分布式锁:Redlock(具体细节还要再看一下)
二、消息中间件
1. 怎么防止重复提交
一个面试官说可以在业务中指定如果规定间隔时间内重复收到2个相同的订单,则第二个被判定为重复提交
2.rabbitMq同时发送短信+邮件怎样更快
场景说明:用户注册后,需要发注册邮件和注册短信,传统的做法有两种 1.串行的方式 2.并行的方式
- 串行的方式,所有的任务都完成之后才返回给客户端
- 并行的方式,并发执行发邮件和发短信操作
-
最好的方式:引入消息队列
假设三个业务节点分别使用50ms,串行方式使用时间150ms,并行使用时间100ms。虽然并行已经提高的处理时间,但是,前面说过,邮件和短信对我正常的使用网站没有任何影响,客户端没有必要等着其发送完成才显示注册成功,应该是写入数据库后就返回. 消息队列: 引入消息队列后,把发送邮件,短信不是必须的业务逻辑异步处理。