2025年java面试题高级开发工程师考什么(java高级开发需要会什么)

java面试题高级开发工程师考什么(java高级开发需要会什么)概述 以 Java 编程基础 JVM 原理 SpringSpring Boot Redis Zookeeper 消息队列 Kafka Rocket MQ MySQL 等为主 也包括 Dubbo Tomcat 性能优化 容器化技术 Docker Kubernetes 微服务中监控 Linux 常用命令等问题 最为重要的当然是算法和数据结构 1 解决可见性问题 2 禁止指令重排序 1 线程解锁前 必须把共享变量的最新值刷新到主内存中 2 线程加锁时 将清空工作内存中共享变量的值




概述:以Java编程基础、JVM原理、SpringSpring Boot、Redis、Zookeeper、消息队列(Kafka、Rocket MQ)、MySQL等为主;也包括Dubbo、Tomcat性能优化、容器化技术(Docker、Kubernetes)、微服务中监控、Linux常用命令等问题;最为重要的当然是算法和数据结构。

  • 1.解决可见性问题
  • 2.禁止指令重排序

1.线程解锁前,必须把共享变量的最新值刷新到主内存中;

2.线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新获取最新的值;(注意:加锁与解锁需要是同一把锁)
通过以上两点,可以看到synchronized能够实现可见性。同时,由于synchronized具有同步锁,所以它也具有原子性

  1. select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。
  2. select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要一次拷贝,而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省不少的开销。

1.数据丢失
2.数据覆盖
3.死循环

  • 装载
  • 链接(验证,准备,解析)
  • 初始化
  • 修饰成员变量
  • 修饰方法
  • 修饰代码块
  • 方法区
  • 虚拟机栈
  • 本地方法栈
  • 程序计数器
  • 装载
  1. 通过一个类的全限定名获取定义此类的二进制字节流;
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构 ;
  3. 在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。
  • 链接
  • 验证
  1. 文件格式验证
  2. 元数据验证
  3. 字节码验证
  4. 符号引用验证
  • 准备
  1. 为类的静态变量分配内存,并将其初始化为默认值
  • 解析
  1. 把类中的符号引用转换为直接引用
  • 初始化
  1. 对类的静态变量,静态代码块执行初始化操作
  • 防止重复加载
  • 防止核心类不被篡改,保证数据安全
  • 引用计数法
  • 可达性分析算法
  • 吞吐量优先
  • 停顿时间优先

CMS

  • 优点:并发收集、低停顿
  • 缺点:产生大量空间碎 片、并发阶段会降低吞吐量
  • 使用“标记-清除”算法

G1

  • 并行与并发
  • 分代收集(仍然保留了分代的概念)
  • 空间整合(整体上属于“标记-整理”算法,不会导致空间碎片)
  • 可预测的停顿(比CMS更先进的地方在于能让使用者明确指定一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒)
  • JVM启动参数-XX:+HeapDumpOnOutOfMemoryError,当OutOfMemoryError发生时自动生成 Heap Dump 文件;
  • 或者通过jmap -heap 结合pid手动生成Heap Dump文件;
  • 分析Dump文件,使用JDK自带的jvisualVm或者Eclipse memory analyzer等工具进行日志分析
  • 可以查看哪些对象被平凡创建,哪些对象占用内存空间较大;
  • 查找pid
  • top 查看CPU占用率,找到消耗CPU最多的线程号 top -H -p pid
  • 使用jsatck生成 Thread Dump文件
  • 将线程号转化为16进制后查询日志
  • jstack
  • jconsole
  • 适当的增大堆内存大小
  • 选择合适的垃圾收集器
  • G1合理设置停顿时间
  • 合理设置并发线程数
  • 调整启动并发GC时堆内存占用百分比
  • 内存泄漏和内存溢出的区别,分别产生的原因,及解决方案
  • 代码缺陷,使用完对象后没有及时释放,导致某些不会再被使用的对象不能被垃圾回收器正常回收
  • 使用完对象后将其置为NULL;
  • 使用数据库连接,IO操作,网络连接后要关闭连接;
  • 使用集合类后需要将其remove或置为NULL;
  • 正确使用单例模式,如果单例对象持有外部的引用,那么这个对象将不能被JVM正常回收;
  • 内存泄漏到一定程度会导致内存溢出
  • 创建大对象时,无法分配足够大的空间会导致内存溢出
  • 应用程序并发较高时,
  • 内存中加载的数据过多
  • 在循环中不断创建实例对象
  • 启动参数内存值设置过小
  • 代码检查
  • 检查数据库查询语句,是否有一次获取全部数据的查询
  • 根据应用程序QPS峰值,合理部署集群节点数,合理进行负载均衡
  • 合理设置内存值大小,可根据峰值的请求量以及估算实例对象的大小,设置JVM启动参数内存值大小
  • System.gc()方法的调用,强烈影响系建议能不使用此方法就别使用,让虚拟机自己去管理它的内存,可通过通过-XX:+ DisableExplicitGC来禁止RMI(Java远程方法调用)调用System.gc。
  • 适当调整新生代的空间大小,让对象在新生代多存活一段时间,
  • 适当增加方法区的大小
  • 启用空间分配担保机制

执行Minor GC的时候,JVM会检查老年代中最大连续可用空间是否大于了当前新生代所有对象的总大小。

如果大于,则直接执行Minor GC(这个时候执行是没有风险的)。
如果小于了,JVM会检查是否开启了空间分配担保机制,如果没有开启则直接改为执行Full GC。
如果开启了,则JVM会检查老年代中最大连续可用空间是否大于了历次晋升到老年代中的平均大小,如果小于则执行改为执行Full GC。
如果大于则会执行Minor GC,如果Minor GC执行失败则会执行Full GC。

  • jconsole
  • jvisualvm
  • MAT 分析Heap Dump文件
  • perfma 在线
  • GC Viewer 分析GC日志
  • gceasy 在线
  • jps
  • jinfo
  • jstat
  • jstack
  • jmap
  • CMS收集器一般在内存使用空间占比达到某一比例的时候CMS 收集器就会被激活。现在默认比例一般90%以上
  • 通过参数设置,整理空间碎片
  • 适当调整内存大小
  • 调整最大停顿时间
  • 调整启动并发GC时堆内存占用百分比
  • 策略模式
  • 模板方法模式
  • 代理模式和委派模式的区别?
  • 动态代理和静态代理的区别 ?
  1. 所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
  2. 缺点:
    如果要代理的类型很多,势必要为每一种类型的方法都进行代理
    如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。显而易见,增加了代码维护的复杂度。
  3. 动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成
  4. 代理模式的优点:程序解耦,功能增强
  • 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
  • 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
  • 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  • 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
  • 通过构造器传入包装类实例,接口成员变量接收
  • 优点:
  1. 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
  2. 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
  3. 装饰器模式完全遵守开闭原则
  • 缺点:
  1. 装饰模式会增加许多子类,过度使用会增加程序得复杂性。
  1. public final class $Proxy0 extends Proxy implements Interface
  2. 受限制于Java仅支持单继承
  1. ImportSelector是将类的全路径名直接注册给Spring的IOC,而ImportBeanDefinitionRegistrar就是通过BeanDefinition注册给spring。
  2. MyBatis的底层就是通过实现了ImportBeanDefinitionRegistrar来实现的,在构建真正需要注册进入到IOC的实现类之前,MyBatis动态实现了我们指定的数据访问层的接口,然后将其构建BeanDefinition并注入到了IOC中
  1. Spring Boot 在启动后,会找到全部 CommandLineRunner 接口的实例并运行它们的 run 方法。
  2. 可以通过 @Order 注解或者实现 Order 接口来指定 CommandLineRunner 的执行顺序。
  1. 项目目录结构
  2. 加载指定后缀,指定格式的配置文件
  1. 大量热点数据同时过期,增加数据库的压力
  1. 缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库
  1. 缓存穿透是指缓存和数据库中都没有的数据 布隆过滤器
  • RDB
  • AOF
  • 两种区别
  • 定期删除
  • 惰性删除
  1. noeviction:当内存使用超过配置的时候会返回错误,不会驱逐任何键
  2. allkeys-lru:加入键的时候,如果过限,首先通过LRU算法驱逐最久没有使用的键
  3. volatile-lru:加入键的时候如果过限,首先从设置了过期时间的键集合中驱逐最久没有使用的键
  4. allkeys-random:加入键的时候如果过限,从所有key随机删除
  5. volatile-random:加入键的时候如果过限,从过期键的集合中随机驱逐
  6. volatile-ttl:从配置了过期时间的键中驱逐马上就要过期的键
  7. volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键
  8. allkeys-lfu:从所有键中驱逐使用频率最少的键

1、把所有相关的值聚集到一个 key 中,节省内存空间

2、只使用一个 key,减少 key 冲突

3、当需要批量获取值的时候,只需要使用一个命令,减少内存/IO/CPU 的消耗

  1. 便于数据迁移,平滑的扩容或缩容
  1. 哨兵中通过Raft算法选举出Leader节点,进行故障转移
  • pipeline
  • lua脚本

1.如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。

如上所述,在消息头中,最占空间的是 myslots[CLUSTER_SLOTS/8]。 当槽位为65536时,这块的大小是: 65536÷8÷1024=8kb因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。

2.redis的集群主节点数量基本不可能超过1000个。

如上所述,集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者,不建议redis cluster节点数量超过1000个。 那么,对于节点数在1000以内的redis cluster集群,16384个槽位够用了。没有必要拓展到65536个。

3.槽位越小,节点少的情况下,压缩率高

Redis主节点的配置信息中,它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中,会对bitmap进行压缩,但是如果bitmap的填充率slots / N很高的话(N表示节点数),bitmap的压缩率就很低。 如果节点数很少,而哈希槽数量很多的话,bitmap的压缩率就很低。

  • 先更新数据库,再删除缓存–缓存删除失败之后重试机制
  • 先删除缓存,再更新数据库–延时双删
  • ziplist
  • skiplist
  • 通过score进行排序
  • 原子广播
  • 崩溃恢复
  • 主从模式 多主多从
  • 数据同步
  • Topic 多个消息队列 默认一个topic在一个broker上有4个消息队列
  • Tag 过滤消息使用
  • 消费模式
  1. 集群消费 集群中只有一个broker节点能成功消费消息,消费失败之后有重试机制(Exception和连接 超时),衰减重试16次后,进入死信队列。
  2. 广播消费 能够保证消息至少被消费一次,消费失败之后没有重试机制
  • 如何保证消息一定会被消费成功,消费者链路中?
  • 消息重发机制
  • 零拷贝原理 磁盘中的消息在内核态中直接发送,不用拷贝到用户空间后在发送,减少拷贝次数
  • 消息存储
  • 日志文件
  • 索引文件
  • 事务消息原理
  • 集群模式
  • 数据同步机制
  • 数据分区
  • 数据副本
  • 消息存储
  • 日志文件
  • 索引文件
  • 消息消费机制
  • 分区分配策略
  1. range
  2. 随机
  3. 粘滞策略
  • rebalance原理
  • 程序连接kafka的参数设置
  • 生产者连接
  • 消费者连接
  • 事务隔离级别
  1. 脏读
  2. 不可重复读
  3. 幻读–如何解决
  • 索引结构
  • 聚簇索引
  • 回表
  • 主从数据同步
  • 集群模式如何数据不丢失
  • SQL优化的具体步骤
  1. Explain 查看执行计划
  2. type类型最好为ref或者range,index和all需要优化
  3. 查看查询是否走索引
  4. filtered:这个字段表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,它是一个百分比。
  5. Extra
  6. using index 使用到了覆盖索引
  7. using where 使用了 where 过滤,表示存储引擎返回的记录并不是所有的都满足查询条件,需要
  8. 在 server 层进行过滤(跟是否使用索引没有关系)。
  9. using index condition 索引条件下推 索引下推在非主键索引上的优化,可以有效减少回表的次数,大大提升了查询的效率。
  10. using temporary 用到了临时表
  • 小表驱动大表
  • 左连接以左表为准
  • 右连接以右表为准
  • 如果条件中有or,即使其中有部分条件带索引也不会使用(这也是为什么尽量少用or的原因),要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引;
  • 前导模糊查询不能利用索引(like ‘%xx’ 或者 like ‘%xx%’)
  • 违反组合索引最左匹配原则
  • 需要隐形类型装换(如数字转换成字符)
  • where 子句里对索引列上有数学运算,用不上索引
  • where 子句里对有索引列使用函数,用不上索引
  • 如果mysql估计使用全表扫描要比使用索引快,则不使用索引
  • 唯一性差;
  • 频繁更新的字段不用(更新索引消耗);
  • where中不用的字段;
  • 索引使用<>时,效果一般;
  • InnoDB支持事务,支持表级别锁和行级别锁,数据和索引绑定在一起
  • MyISAM不支持事务,只支持表级别锁,数据文件和索引文件分开存储

B+树的非叶子结点只包含导航信息,不包含实际的值,所有的叶子结点和相连的节点使用链表相连,便于区间查找和遍历。

B+ 树的优点在于:

  • IO次数更少:由于B+树在内部节点上不包含数据信息,因此在内存页中能够存放更多的key。 数据存放的更加紧密,具有更好的空间局部性。因此访问叶子节点上关联的数据也具有更好的缓存命中率。
  • 遍历更加方便:B+树的叶子结点都是相链的,因此对整棵树的遍历只需要一次线性遍历叶子结点即可。而且由于数据顺序排列并且相连,所以便于区间查找和搜索。而B树则需要进行每一层的递归遍历。相邻的元素可能在内存中不相邻,所以缓存命中性没有B+树好。

但是B树也有优点,其优点在于,由于B树的每一个节点都包含key和value,因此经常访问的元素可能离根节点更近,因此访问也更迅速。

1、 B+树的磁盘读写代价更低:B+树的内部节点并没有指向关键字具体信息的指针,因此其内部节点相对B树更小,如果把所有同一内部节点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多,一次性读入内存的需要查找的关键字也就越多,相对IO读写次数就降低了。

2、B+树的查询效率更加稳定:由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

3、B+树更便于遍历:由于B+树的数据都存储在叶子结点中,分支结点均为索引,方便扫库,只需要扫一遍叶子结点即可,但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,所以通常B+树用于数据库索引。

4、B+树更适合基于范围的查询:B树在提高了IO性能的同时并没有解决元素遍历的我效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只需要去遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作或者说效率太低。



  • 如果条件中有or,即使其中有部分条件带索引也不会使用(这也是为什么尽量少用or的原因)
  • 对于多列索引,不是使用的第一部分,则不会使用索引
  • like查询是以%开头
  • 存在索引列的数据类型隐形转换,则用不上索引,比如列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引
  • where 子句里对索引列上有数学运算,用不上索引
  • where 子句里对有索引列使用函数,用不上索引
  • 如果mysql估计使用全表扫描要比使用索引快,则不使用索引

1、如果你只需要结果集中的某几行,那么建议使用limit。这样这样的话可以避免抓取全部结果集,然后再丢弃那些你不要的行。

2、对于order by查询,带或者不带limit可能返回行的顺序是不一样的。

3、如果limit row_count 与 order by一起使用,那么在找到第一个***row_count***就停止排序,直接返回。

4、如果order by列有相同的值,那么MySQL可以自由地以任何顺序返回这些行。换言之,只要order by列的值不重复,就可以保证返回的顺序。

5、可以在order by子句中包含附加列,以使顺序具有确定性。

  • 项目中如何保证消息一定被消费成功?(例如:服务重启过程中,消息处理失败后,如何进行重发机制?)
  • 如何保证项目平稳运行?
  • 生产中项目有没有监控平台?‘
  • 监控平台数据埋点的原理?
  • 数据上报的方式?
  • 设计接口的幂等性如何实现?
  • 项目过程中,解决过遇到过哪些难题,如何解决的?
  • 根据实际参加的项目情况回答,提前准备好场景分析问题,或者你擅长的问题
  • ELK push
  • CAT 数据埋点
  • Prometheus 采用拉的模式(Pull)从应用中拉取数据
  • InfluxDB
  • 都自带面板展示,可与Grafana集成
  • 快速排序
  • 堆排序(时间复杂度和空间复杂度)
  • 获取数组中第K大的数(使用快速排序或者堆)
  • 将两个有序数组或链表合并成一个有序的数组或链表(归并排序)
  • 判断链表中是否有环
  • 链表反转
  • 数的前中后遍历
  • 二分查找
  • 手写实现Lock
  • 实现两个线程循环打印AB
  • 实现多线程中生产者和消费者模式
  • 如何实现两个有上一亿条数据的文件中,找出相同的数据
  • 线程:假设有一个存储大于500条字符串数据列表List,输入一个要查找的字符串,查找List中包括该字符串的数据并输出。
  • 多个线程同时遍历处理一个list集合
  • list集合拆分后由多个线程处理
  • 假设有一个整数型数组,存在先正序后逆序两部分数据,去除数组中存在重复的数字并输出新的数组。如:
    {1,3,5,6,8,8,15,10,9,7,6,5,3},则输出:{1,3,5,6,8,15,10,9,7}
  • 获取无序数组中查找最大的K个数
  • 输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值。要求时间复杂度为 O(n)。
  • 输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。
  • 二维数组问题


编程小号
上一篇 2025-04-10 18:17
下一篇 2025-03-22 13:27

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ri-ji/71130.html