Java面试题整理2

本文涵盖Redis数据结构与使用场景、缓存问题及解决方案、SpringBoot启动类注解原理、Docker常用命令、Linux工具、Java并发、MyBatis与MySQL索引、乐观锁、SpringCloud组件、负载均衡、并行与并发区别、哈希映射类比较、Spring框架自动注入与异常处理等内容,适合准备IT实习面试的学生参考。
摘要由CSDN通过智能技术生成


前言

本文为朋友2024年一次实习面试试题记录,很多都是基础八股文。


1.Redis数据结构及其使用场景

Redis支持5种数据类型作为其value,Redis的key都是字符串类型的。

  1. string:Redis中字符串value最大可为512M。可以用来做一些计数功能的缓存(也是实际工作中最常见的)。
  2. list:简单的字符串列表,按照插入顺序排序,可以添加一个元素到列表的头部(左边)或者尾部(右边),其底层实现是一个链表。可以实现一个简单消息队列功能,做基于Redis的分页功能等。
  3. set:是一个字符串类型的无序集合,可以用来进行全局去重等。
  4. sorted set:是一个字符串类型的有序集合,给每一个元素一个固定的分数score来保持排序。可以用来做排行榜应用或者进行范围查找等。
  5. hash:键值对集合,是一个字符串类型的key和value的映射表,也就是说其存储的value是一个键值对(key-value),可以用来存放一些具有特定结构的信息

其实,Redis还支持三种特殊的数据类型,分别是BitMap、Geo和HyperLogLog。一般情况下,可以认为Redis的支持的数据类型有上述5种。其底层数据结构包括:简单动态字符串、链表、字典、跳表、整数集合以及压缩列表。


2.Redis缓存穿透、击穿、雪崩如何发生及解决方案

  1. 缓存雪崩:在高并发下,大量的缓存key在同一时间失效,导致大量请求落到数据库上,如活动系统里面同时进行这非常多的活动,但在某个时间点所有的活动缓存全部过期。
    解决方案:
    缓存数据的过期时间设置随机,防止同一时间大量数据过期现象的发生。
    ②如果缓存数据库是分布式部署,将热点数据均匀分布在不同高的缓存数据库中
    设置热点数据永远不过期
  2. 缓存穿透:访问一个不存在的key,缓存不起作用,请求会穿透到DB,流量大时DB会挂掉。
    解决方案:
    接口增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截。
    ②从缓存取不到的数据,在数据库中也没有取到,这时可以将key-value对写为key-null,缓存有效时间可以设置短点,如30s。这样可以防止攻击用户反复用同一个id暴力攻击。
  3. 缓存击穿:一个存在的key,在缓存过期的那一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。
    解决方案:
    设置热点数据永远不过期
    加互斥锁:简单来说,就是在缓存失效的时候(判断拿出来的值是否为空),不是立即去加载数据库,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis 的 SETNX)去set一个mutex key,当操作返回成功时,再进行加载数据库的操作并回设缓存;否则,就重试整个get缓存方法。

3.SpringBoot启动类注解的底层实现原理

来源:Spring Boot 原理解析


4.docker常用命令

来源:docker常用命令大全

  1. 基础命令

启动docker:systemctl start docker
关闭docker:systemctl stop docker
重启docker:systemctl restart docker
docker设置随服务启动而自启动:systemctl enable docker
查看docker 运行状态(如果是在运行中 输入命令后 会看到绿色的active):systemctl status docker
查看docker 版本号信息:docker versiondocker info
帮助命令:docker --help

  1. 镜像命令

查看自己服务器中docker 镜像列表:docker images
搜索镜像:docker search 镜像名如:docker search --filter=STARS=9000 mysql 搜索 STARS >9000的 mysql 镜像
拉取镜像 不加tag(版本号) 即拉取docker仓库中 该镜像的最新版本latest 加:tag 则是拉取指定版本:docker pull 镜像名 docker pull 镜像名:tag
运行镜像:docker run 镜像名docker run 镜像名:Tag
删除镜像:

  • #删除一个: docker rmi -f 镜像名/镜像ID
  • #删除多个 其镜像ID或镜像用用空格隔开即可:docker rmi -f 镜像名/镜像ID 镜像名/镜像ID 镜像名/镜像ID
  • #删除全部镜像 -a 意思为显示全部, -q 意思为只显示ID:docker rmi -f $(docker images -aq)

强制删除镜像:docker image rm 镜像名称/镜像ID
保存镜像:docker save 镜像名/镜像ID -o 镜像保存在哪个位置与名字
加载镜像:docker load -i 镜像保存文件位置

  1. 容器命令

查看正在运行容器列表:docker ps
查看所有容器:docker ps -a
运行一个容器:# -it 表示 与容器进行交互式启动 -d 表示可后台运行容器 (守护式运行) --name 给要运行的容器 起的名字 /bin/bash 交互路径:docker run -it -d --name 要取的别名 镜像名:Tag /bin/bash
查看容器日志:docker logs -f --tail=要查看末尾多少行 默认all 容器ID

  1. 运维命令

查看docker工作目录:sudo docker info | grep "Docker Root Dir"
查看docker磁盘占用总体情况:du -hs /var/lib/docker/


5.Linux常用命令

  1. grep:Global Regular Expression Print,可以使用正则表达式搜索文本,并把匹配的行打印出来。

示例:查找文件file.log中“password”字段,并且统计出现的次数:
grep "password" file.log|wc -| 或者 grep "password" file.log -c

  1. awk:将一行分为多个字段做处理。

示例1:去掉第一列
awk -F "," '{print $2, $3}' test.txt
示例2:对第一列求和
awk '{a+$1}END{print a}' test.txt
示例3:去掉列数不为3的列
awk -F "," '{if(NF == 3){print $0}}' test.txt

  1. ps:默认只会显示运行在当前控制台下的属于当前用户的进程。

显示所有进程
ps -Aps -e
显示完整格式的所有进程
ps -ef
指定进程名,找出进程名中包括java的所有进程
ps -ef | grep 'java'

  1. top:实时监测进程,输出的第一部分显示系统的概括。

pstop命令的区别:

  • ps看到的是命令执行瞬间的进程信息,而top可以持续监视
  • ps只是查看进程,而top还可以监视系统性能,如平均负载、CPU和内存的消耗
  • top可以操作进程,如改变优先级(命令r)和关闭进程(命令k)
  • ps主要是查看进程的,关注点在于查看需要查看的进程
  • top主要看CPU,内存使用情况即占用资源最多的进程由高到低排序,关注点在于资源占用情况。
  1. sed:利用脚本处理文本文件。可以依照脚本的指令来处理、编辑文本文件。sed主要用来自动编辑一个或者多个文件、简化对文件的反复操作、编写转换程序等。

可以将文件的第二行和第三行裁剪出来
sed -n '2,3p' test.txt

  1. sort:对文本文件进行排序

正序排序
sort -n test.txt
反序排序
sort -nr test.txt

  1. tailhead命令

查看文件的最后2行
tail -n 2 file.log
实时查看文件的后边追加的部分
tail -f file.log
查看文件的开始2行
head -n 2 file.log


6.Java线程实现的几种方式

我这边列举了三种,更详细的介绍请参考:【Java】详细介绍Java实现线程的四种方式

  1. 继承自 Thread类:通过继承 Thread 类,可以创建⼀个新的线程。为了实现线程的执⾏逻辑,需要重写 run() 方法。
  2. 实现 Runnable接口
  3. 使用 Executor 框架:Executor 框架是 Java 并发编程中的高级工具,它提供了⼀种更为灵活的方式来管理和执⾏线程。通过 Executor ,可以将任务提交给线程池,由线程池来管理线程的⽣命周期和执行。

7.Mybatis框架中#{}和${}的区别

MyBatis中能用#就尽量不要使用$符号,它们的区别主要体现在以下几点:

  1. #将传入的数据都当做一个字符串,会对自动传入的数据加一个双引号
  2. $符号将传入的数据直接显示在生成的SQL语句中
  3. #存在预编译的过程,对问号赋值,防止SQL注入
  4. $符号是直译的方式,一般用在order by ${列名} 语句中

8.MySQL常见索引和区别

来源:MySQL常见索引和区别
MySQL索引常见的分类包括:普通索引、唯一索引、主键索引、全文索引等。

  1. 普通索引:普通索引是最基本的索引类型,不包含任何约束和限制条件。仅仅是为了提高查询速度而建立的索引。当我们使用 WHERE 字句进行搜索时,MySQL 会使用普通索引来定位符合条件的行。如果查询条件使用到了索引字段,那么 MySQL 可以更快速地定位所需的数据。普通索引可以在字符型、数字型、日期型等各种基本数据类型上创建。
  2. 唯一索引:唯一索引与普通索引相同,也是为了提高查询速度而建立的索引。唯一索引不允许其字段中出现重复的值,因此,唯一索引可以用于任何必须具有唯一性的字段上。MySQL 在执行 INSERT 和 UPDATE 操作时,会对唯一索引进行检测以确保其值的唯一性。
  3. 主键索引:主键索引是一种特殊的唯一索引,它具有唯一性约束,但是,主键索引要求所有索引列都不为空,即设置了 NOT NULL 约束。主键索引也是最常用的索引类型,主键索引可以单独一列或多列组成。
  4. 全文索引:全文索引是 MySQL 中针对文本类型数据(如文本、字符型、甚至二进制类型数据)的特殊索引。它可以帮助我们更快地查询到数据,主要用于诸如文章、博客、评论、产品描述等等文本信息的快速匹配。全文索引支持中文分词,并且可以确定文本中关键词合适的位置,从而快速定位相关文本。

9.乐观锁怎么实现

乐观锁就是对数据冲突保持乐观点态度,认为不会有其他线程同时修改数据。因此乐观锁不会上锁,只是在更新数据都时候判断是否有其他线程更新,如果没有其他线程修改则更新数据,有其他线程修改则放弃数据,重新读取数据处理。Java中的乐观锁主要有两种实现方式:CAS(Compare and Swap)和版本号控制。乐观态度+数据更新)

  1. CAS: CAS是实现乐观锁的核心算法,它通过比较内存中的值是否和预期的值相等来判断是否存在冲突。如果存在,则返回失败;如果不存在,则执行更新操作。CAS 它包含了 3 个参数:V(需要更新的变量)、E(预期值)和 N(最新值)。只有当需要更新的变量等于预期值时,需要更新的变量才会被设置为最新值,如果更新值和预期值不同,则说明已经有其它线程更新了需要更新的变量,此时当前线程不做操作,返回 V 的真实值。
  2. 版本号控制:版本号控制是乐观锁的另一种实现方式。每当一个线程要修改数据时,都会先读取当前的版本号或时间戳,并将其保存下来。线程完成修改后,会再次读取当前的版本号或时间戳,如果发现已经变化,则说明有其他线程对数据进行了修改,此时需要回滚并重试。

10.SpringCloud组件

  1. 服务发现——Netflix Eureka
  2. 客服端负载均衡——Netflix Ribbon
  3. 断路器——Netflix Hystrix
  4. 服务网关——Netflix Zuul
  5. 分布式配置——Spring Cloud Config

11.负载均衡的作用

  1. 当集群里的1台或者多台服务器down的时候,剩余的没有down的服务器可以保证服务的继续使用
  2. 使⽤了更多的机器保证了机器的良性使用,不会由于某一高峰时刻导致系统cpu急剧上升

12.并行和并发的区别

  1. 并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。
  2. 并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。
  3. 并行是在一台处理器上“同时”处理多个任务,并发是在多台处理器上同时处理多个任务,并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。

13.HashMap和Hashtable的区别

  1. HashMap没有考虑同步,是线程不安全的;Hashtable使用了synchronized关键字,是线程安全的
  2. HashMap允许null作为key;Hashtable不允许null作为key,Hashtable的value也不允许为null

14.Runnable和Callable的区别

来源:runnable 和 callable 有什么区别?

  1. 方法不同
    Runnable接口只有一个run()方法,该方法不返回任何值,因此无法抛出任何checked Exception。
    Callable接口则有一个call()方法,它可以返回一个值,并且可以抛出一个checked Exception。
  2. 返回值不同
    Runnable的run()方法没有返回值,只是一个void类型的方法。
    Callable的call()方法却必须有一个返回值,并且返回值的类型可以通过泛型进行指定。
  3. 异常处理不同
    在Runnable中,我们无法对run()方法抛出的异常进行任何处理。
    但在Callable中,自定义的call()方法可以抛出一个checked Exception,并由其执行者Handler进行捕获并处理。
  4. 使用场景不同
    Runnable适用于那些不需要返回值,且不会抛出checked Exception的情况,比如简单的打印输出或者修改一些共享的变量。
    Callable适用于那些需要返回值或者需要抛出checked Exception的情况,比如对某个任务的计算结果进行处理,或者需要进行网络或IO操作等。在Java中,常常使用Callable来实现异步任务的处理,以提高系统的吞吐量和响应速度。

15.Spring自动注入

详情查看:Spring自动注入

  1. 在 Spring 配置文件中对象名和 ref=”id”id 名相同使用自动注入,可以不配置
  2. 两种配置办法
    ① 在中通过 autowire=”” 配置,只对这个生效
    ②在中通过 default-autowire=””配置,表当当前文件中所有都是全局配置内容
    ③autowire=”” 可取值
      no: 不自动注入
      byName: 通过名称自动注入.在 Spring 容器中找类的 Id
      byType: 根据类型注入 注意这里如果有两个相同的注入类型会报错 
      constructor: 根据构造方法注入

16.@Autowired和@Resource的区别

来源:【最详细】@Autowired 和 @Resource 的区别

  1. @Autowired 是spring提供的注解,@Resource 是JDK提供的注解
  2. @Autowired 默认的注入方式是ByType(根据类型进行匹配),@Resource 默认的注入方式是 ByName (根据名称进行匹配)
  3. 当一个接口存在多个实现类的情况下,@Autowired 和 @Resource都需要通过名称才能匹配到对应Bean。@Autowired可以通过@Qualifier来显示指定的名称,@Resource 可以通过name来显示指定名称

17.SpringMVC执行流程

来源:SpringMVC执行流程

SpringMVC执行流程:

  1. 用户发送请求至前端控制器DispatcherServlet;
  2. DispatcherServlet收到请求调用处理器映射器HandlerMapping;
  3. 处理器映射器根据请求url找到具体的处理器,生成处理器执行链HandlerExecutionChain(包括处理器对象和处理器拦截器)一并返回给DispatcherServlet;
  4. DispatcherServlet根据处理器Handler获取处理器适配器HandlerAdapter执行HandlerAdapter处理一系列的操作,如:参数封装,数据格式转换,数据验证等操作
  5. 执行处理器Handler(Controller,也叫页面控制器);
  6. Handler执行完成返回ModelAndView;
  7. HandlerAdapter将Handler执行结果ModelAndView返回到DispatcherServlet
  8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器;
  9. ViewReslover解析后返回具体View;
  10. DispatcherServlet对View进行渲染视图(即将模型数据model填充至视图中);
  11. DispatcherServlet响应用户。

18.Java中的异常处理

主要是try-catch-finally和throw、throws关键字。
异常都是派生于Throwable类的一个实例,这个实例可以由JVM产生,也可以再程序中手动创建,用throw手动抛出。throw的对象必须是派生于Throwable类的实例,其他类型无法通过编译。受检异常只有两种选择:要么被捕获处理,要么抛出让调用者处理。而非受检异常没有此强制要求。

  1. throws是用来声明异常,只要是派生于Throwable类的都可以被声明。
  2. try-catch-finally用来捕获异常。
    所有派生于Throwable类都可以通过 catch 捕获,try 中放可能存在异常的方法,如果在 try语句块中的任何代码抛出了⼀个在 catch 子句中说明的异常类,那么程序将跳过 try 语句块的其余代码,并且执行 catch 子句中的处理器代码。如果在 try 语句块中的代码没有拋出任何异常,那么程序将跳过 catch 子句,如果方法中的任何代码拋出了⼀个在 catch 子句中没有声明的异常类型,那么这个方法就会立刻退出。在⼀个 try 语句块中可以捕获多个异常类型,并对不同类型的异常做出不同的处理。可以为每个异常类型使用一个单独的 catch 子句。需要注意,如果多个catch中的异常非继承关系,那么catch顺序不影响结果,如果catch异常存在类继承关系,那么⼦类的catch应该放在前面,父类的在后面。
  3. finally⼀般⽤来关闭所占用的资源。如果代码抛出异常,就会终止剩余代码的处理,并且退出这个方法。这样可能会导致⼀些程序占用的系统并不能被正确的释放。而不管是否有异常被捕获,finally中子句的代码都会被执行,可以在这里正确的释放资源。

19.Spring异常处理

来源:Spring 异常处理

  1. 简单的可以通过抛出特定异常,Spring 会自动转换为对应的 HTTP 状态码,或者自定义异常,添加对应状态码注解
  2. 在同一个文件中编写异常处理器,单独写一个方法,添加 @ExceptionHandler(XXXException.class) 注解
  3. 单独编写一个统一异常处理类
  4. 对于 Rest 等方式中的异常,可以在异常处理方法上面添加 @ResponseStatus、@ResponseBody 注解
  5. 更复杂的情况可以通过返回 ResponseEntity 解决

20.Spring框架中的常用类

来源:spring框架常用的类有哪些
ApplicationContext: 用来管理bean的配置文件并创建bean
BeanFactory: 为创建和管理bean提供基本服务
Configuration: 使用Java配置类来配置bean
Autowired: 用来自动装配bean
Component: 用来标记组件类
Service: 用来标记业务层组件
Repository: 用来标记数据访问层组件
Controller: 用来标记控制层组件
RestController: 用来标记RESTful风格的控制层组件
RequestMapping: 用来处理请求映射
RequestBody: 用来处理请求体
ResponseBody: 用来处理响应体
AOP:用来提供面向切面编程(AOP)的功能


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值