Java后端常见面试题

一面

数据结构

  • 链表有哪些分类?
    单向或双向、是否带头节点、是否循环
  • 树有哪些?
    平衡二叉树、二叉查找树、红黑树
  • 跳表结构
    在这里插入图片描述

跳表(SkipList)数据结构介绍

Java基础

  • final关键字的用途?
修饰位置作用备注
不可被继承
方法不可被子类重写
变量修饰只读变量,如果修饰对象,对象引用不能修改,对象值可修改

遗留问题:Integer对象

        Integer intObj = 129;
        final Integer intObjF = intObj;
        intObj = 130;
        System.out.println(intObjF);
  • final修饰类可以被spring管理吗?
    参考:不可以。
    Spring管理的类依赖动态代理实现。
    动态代理有两种实现,JDK和CGLIB,JDK动态代理只能代理实现了接口的类,CGLIB动态代理是通过生成被代理类的子类,来拦截被代理对象的方法调用。

  • final修饰类,可以对这个类做AOP切面编程吗?

  • synchronized关键字,在对象头的什么位置?
    在这里插入图片描述

  • ArrayList扩容实现

  • HashMap如何扩容的?

  • String+String相比StringBuilder的坏处体现在哪里?
    如果是显示的在代码里使用了+拼接字符串,JDK会优化成StringBuilder,但如果在for循环里使用+号拼接,JDK的优化是每次生成一个StringBuilder对象,每次开辟新的内存空间浪费内存。
    StringBuilder底层实现是字节数组,无线程安全设计。

  • 线程安全的集合,实现原理?

设计模式

  • 常用的设计模式都有哪些?
  • 结合Spring框架,说一些设计模式

Java框架

  • Bean的生命周期?
    Bean的生命周期
    在这里插入图片描述

Bean的完整生命周期经历了各种方法调用,这些方法可以划分为以下几类:

1、Bean自身的方法:这个包括了Bean本身调用的方法和通过配置文件中的init-method和destroy-method指定的方法。
2、Bean级生命周期接口方法:这个包括了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean这些接口的方法。
3、容器级生命周期接口方法:这个包括了InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“后处理器”。
4、工厂后处理器接口方法:这个包括了AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等非常有用的工厂后处理器  接口的方法。工厂后处理器也是容器级的。在应用上下文装配配置文件之后立即调用。(对应上图中的BeanFactoryPostProcessor)

参考:Spring常见面试题总结Spring Bean的生命周期(非常详细)

  • Spring AOP实现原理?
  • SpringBoot默认MySQL连接池?
    答案:HikariDataSource
  • MyBatis实现原理?
    动态代理

MySQL

  • MySQL有哪几种日志文件?(binlog、undolog、redolog)
    1)binlog:记录了包含DDL(表结构变更)和DML(数据变更)的操作。主要有3种格式,Statement(记录SQL语句)、ROW(每一行具体的变更)、Mixed(混合前面两者)
    2)undolog撤销日志:记录逻辑日志的SQL,在事物需要回滚时用到,保证原子性,比如事物执行了一条DELETE,则undolog记录一条INSERT。
    3)redolog重做日志:InnoDB引擎特有,记录页的修改,事物提交时将redolog按照刷盘策略刷到磁盘,(InnoDB使用页为单位管理存储空间,将磁盘上的页缓存到Buffer Pool提高性能),大小固定,采用循环写。

  • MySQL Explain执行结果中,type结果的枚举。
    参考:对应问题MySQL执行计划。
    Explain执行结果中几个重要的参数:

列名含义解释
type表的访问方法system:如果表使用的引擎对于表行数统计是精确的(如:MyISAM),且表中只有一行记录的情况下,访问方法是 system ,是 const 的一种特例。const:表中最多只有一行匹配的记录,一次查询就可以找到,常用于使用主键或唯一索引的所有字段作为查询条件。
eq_ref:当连表查询时,前一张表的行在当前这张表中只有一行与之对应。是除了 system 与 const 之外最好的 join 方式,常用于使用主键或唯一索引的所有字段作为连表条件。
ref:使用普通索引作为查询条件,查询结果可能找到多个符合条件的行。
index_merge:当查询条件使用了多个索引时,表示开启了 Index Merge 优化,此时执行计划中的 key 列列出了使用到的索引。
range:对索引列进行范围查询,执行计划中的 key 列表示哪个索引被使用了。
index:查询遍历了整棵索引树,与 ALL 类似,只不过扫描的是索引,而索引一般在内存中,速度更快。
ALL:全表扫描。
key
Extra
  • MySQL事务隔离级别?
  • MySQL索引失效的场景?
  • MySQL分库分表怎么做的?
  • MySQL in()查询条件索引失效的场景?
  • MySQL 分库分表是否做过?

Redis

  • Redis分布式锁实现方式
  1. setnx命令
# 设置某个 key 的值并设置多少毫秒或秒 过期。
set <key> <value> PX <多少毫秒> NX
或
set <key> <value> EX <多少秒> NX

setnx对应到Java里的实现为setIfAbsent:

// 1.生成唯一 id
String uuid = UUID.randomUUID().toString();
// 2. 抢占锁
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 10, TimeUnit.SECONDS);
if(lock) {
    System.out.println("抢占成功:" + uuid);
    // 3.抢占成功,执行业务
    List<TypeEntity> typeEntityListFromDb = getDataFromDB();
    // 4.获取当前锁的值
    String lockValue = redisTemplate.opsForValue().get("lock");
    // 5.如果锁的值和设置的值相等,则清理自己的锁
    if(uuid.equals(lockValue)) {
        System.out.println("清理锁:" + lockValue);
        redisTemplate.delete("lock");
    }
    return typeEntityListFromDb;
} else {
    System.out.println("抢占失败,等待锁释放");
    // 4.休眠一段时间
    sleep(100);
    // 5.抢占失败,等待锁释放
    return getTypeEntityListByRedisDistributedLock();
}

以上代码中,获取当前锁的值和清理锁两步非原子性,在并发场景下可能释放其他线程的锁(比如任务执行时间超过锁过期时间,此时另一个任务可以获取到锁)。

  1. Redisson
    redis官方对使用Redis实现分布式锁提供了一个通用算法Redlock,Java版的分布式锁实现就是Redisson
    在这里插入图片描述

Spring Boot 整合 Redisson

<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.15.5</version>
</dependency>
@Configuration
public class MyRedissonConfig {
    /**
     * 对 Redisson 的使用都是通过 RedissonClient 对象
     * @return
     * @throws IOException
     */
    @Bean(destroyMethod="shutdown") // 服务停止后调用 shutdown 方法。
    public RedissonClient redisson() throws IOException {
        // 1.创建配置
        Config config = new Config();
        // 集群模式
        // config.useClusterServers().addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001");
        // 2.根据 Config 创建出 RedissonClient 示例。
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        return Redisson.create(config);
    }
}

分布式可重入锁

RLock lock = redisson.getLock("anyLock");
// 最常见的使用方法
lock.lock();

(1)多个线程抢占锁,后面锁需要等待,可重入锁是阻塞的。
(2)如果抢占到锁的线程所在的服务停了,锁会被释放。Redisson内部提供了一个监控锁的”看门狗“,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。如果我们未指定 lock 的超时时间,就使用 30 秒作为看门狗的默认时间。只要占锁成功,就会启动一个定时任务:每隔 10 秒重新给锁设置过期的时间,过期时间为 30 秒。如果设置了锁的自动过期时间,则执行业务的时间一定要小于锁的自动过期时间,否则就会报错。

分布式读写锁
在这里插入图片描述

RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
// 最常见的使用方法
rwlock.readLock().lock();
// 或
rwlock.writeLock().lock();

Redis 分布式锁|从青铜到钻石的五种演进方案
分布式锁中的王者方案 - Redisson

  • 持久化的方式?
  • Redis数据结构?

MQ

  • RabbitMQ和Kafka的区别?
    参考:
    1)吞吐量的差别:Kafka吞吐量每秒几十万,RabbitMQ每秒几万。
    2)RabbitMQ对每个消费者创建一个队列。
    3)消息顺序:RabbitMQ通过多线程消费,失败会重新入队,有顺序问题。Kafka每个分区数据是有序的。
  • 消息积压如何处理,需要注意什么?
    参考:首先定位是生产积压还是消费积压,可以通过控制台快速定位,对于消费积压,排除消费失败重复消费的情况,可以考虑水平扩容、关闭一些不重要的业务。

二面

多线程

  1. 重排序有哪些分类?如何避免?
  2. Java 中新的Lock接口相对于同步代码块(synchronized block)有什么优势?如果让你实现一个高性能缓存,支持并发读取和单一写入,你如何保证数据完整性。
  3. 如何在Java中实现一个阻塞队列。
  4. 写一段死锁代码。说说你在Java中如何解决死锁。
  5. volatile变量和atomic变量有什么不同?
  6. 实现Runnable接口和Callable接口的区别
  7. 执行execute()方法和submit()方法的区别是什么呢?
  8. AQS的实现原理是什么?
  9. java API中哪些类中使用了AQS?

线程池

  • 线程池参数如何设置?
    参考:Java并发
  • 线程编排框架解决了什么问题?
    参考:1) 通过压测,找到最好的参数配置。 2)简化多线程开发。

分布式

  • 分布式
    CAP 理论 和 BASE 理论、Paxos 算法和 Raft 算法
  • RPC框架用的什么,OpenFeign是怎么做的?
    分布式事务
    分布式 ID
  • 高并发
    消息队列
    读写分离&分库分表
    负载均衡
  • 高可用
    限流
    降级
    熔断

JVM

中间件

  • SkyWalking实现原理

ES

  • 写ES时有没有遇到数据版本问题的报错
  • ES怎么部署的,如何实现高可靠,对ES有没有降级?

Kafka

  • Kafka如何实现零拷贝,为什么快?
    参考: Kafka使用了零拷贝,具体是通过sendFile实现零拷贝。
    零拷贝相比传统数据传输,减少了数据拷贝次数、减少了上下文切换。
    DMA,英文全称是 Direct Memory Access,即直接内存访问。DMA本质上是一块主板上独立的芯片,允许外设设备和内存存储器之间直接进行 IO 数据传输,其过程不需要 CPU 的参与。
    一般数据拷贝过程:
    在这里插入图片描述

整个过程发生了4次用户态和内核态的上下文切换和4次拷贝,具体流程如下:
用户进程通过read()方法向操作系统发起调用,此时上下文从用户态转向内核态
DMA控制器把数据从硬盘中拷贝到读缓冲区
CPU把读缓冲区数据拷贝到应用缓冲区,上下文从内核态转为用户态,read()返回
用户进程通过write()方法发起调用,上下文从用户态转为内核态
CPU将应用缓冲区中数据拷贝到socket缓冲区
DMA控制器把数据从socket缓冲区拷贝到网卡,上下文从内核态切换回用户态,write()返回

mmap零拷贝原理:
在这里插入图片描述
sendFile零拷贝原理
在这里插入图片描述
Java实现零拷贝的API

    public static void main(String[] args) {
        try {
            FileChannel readChannel = FileChannel.open(Paths.get("./a.txt"), StandardOpenOption.READ);
            FileChannel writeChannel = FileChannel.open(Paths.get("./b.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);

            //用于mmap做内存映射
            MappedByteBuffer data = readChannel.map(FileChannel.MapMode.READ_ONLY, 0, 1024 * 1024 * 40);
            //数据传输
            writeChannel.write(data);
            
/*            //sendfile方式零拷贝
            long len = readChannel.size();
            long position = readChannel.position();
            //数据传输
            readChannel.transferTo(position, len, writeChannel);*/
            readChannel.close();
            writeChannel.close();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

参考与感谢:阿里二面:什么是mmap?

  • Kafka架构怎么设计的?
  • Kafka相比RabbitMQ的区别

Flink

  • Flink任务执行过程

系统设计

  • 服务迁移应该考虑什么?
    参考:域名复用、切量。

  • 项目中有没有哪些指标,是通过技术手段优化得到提升的?

  • QPS很高,没有使用本地缓存的原因?

  • 简历里XXX项目瓶颈在哪里?

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值