面试官会问的面试题

本文涵盖了Java面试中常见的问题,包括底层代码如HashMap的工作原理、SpringBoot的配置与特性、SpringMVC执行流程、集合类的区别、MyBatis中的#{}与${}、Redis的应用与特性、线程创建方式、数据库设计细节、设计模式讲解、消息队列与RabbitMQ的用途、Spring事务管理、线程安全与并发控制,以及数据库和Linux命令等。全面解析了Java开发中的核心知识点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.底层代码

        比较简单的就是HashMap的原理了

首先它的数据结构是由:一维数组,单向链表以及1.8之后的红黑树组成。它的默认长度是16.

具体的步骤为:

  1. 通过键的HashCode值计算出数组的下标
  2. 判断出改下标位置是否为空,如果是空的,就直接添加
  3. 如果不是空的,就调用Key的equals方法进行比较Key的值
  4. 如果相等就覆盖Value的值;不相等就将键值对放到数据后面形成单向链表
  5. 在1.8之后,长度到达8后,就会转换成红黑树

2.SpringBoot的一些了解

        1.配置文件的引入

在一个叫做spring-boot-autoconfigure包下保存大量的自动配置类;在一个叫做META-INFO/spring.factotiries文件下保存类;自动配置类生效就要一些特定的条件,比如说依赖或者是注解,如@ComponentScan、@SpringBootConfiguration、@EnableAutoConfiguration

        2.所具备的特征有:

  1. 可以创建独立的Spring应用程序,并且基于其Maven或Gradle插件,可以创建可执行的JARs和WARs;

  2. 内嵌Tomcat或Jetty等Servlet容器;

  3. 提供自动配置的“starter”项目对象模型(POMS)以简化Maven配置;

  4. 尽可能自动配置Spring容器;

  5. 提供准备好的特性,如指标、健康检查和外部化配置;

  6. 绝对没有代码生成,不需要XML配置。

        3. 包含的一些注解

1.@Configuration

 用于定义配置类,指出该类是 Bean 配置的信息源。

2.@Repository

用于标注数据访问组件,即DAO组件。

使用@Repository注解可以确保DAO或者repositories提供异常转译,这个注解修饰的DAO或者repositories类会被ComponetScan发现并配置,同时也不需要为它们提供XML配置项。

3.@RestController

是REST风格的控制器;它是@Controller和@ResponseBody的合集。

4.@ResponseBody

异步获取json数据,加上@responsebody后,会直接返回json数据。

5.@Bean

相当于XML中的,放在方法的上面,而不是类,意思是产生一个bean,并交给spring管理。

6.@Component

泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

7.@AutoWired

byType方式。把配置好的Bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。

当加上(required=false)时,就算找不到bean也不报错

        4.ioc的了解

IOC(Inverse of Control)控制反转是一种程序设计思想。 反转是把对象的创建和管理交给容器完成,然后再交给开发者。

IOC的主要作用是:解耦。

通过反射机制+工厂模式实现IOC。之后可以根据自己的理解更加深入去阐述自己的理解

3.SpringMVC的执行流程

  1.  用户发送请求
  2. 前端控制器获得用户请求的URL,发送URL给处理器映射
  3. 处理器映射将Handler(包装方法信息)返回给前端控制器
  4. 前端控制器发送Handler给处理器适配器,适配器执行Handler方法
  5. 执行Handler方法后,返回ModelAndView(逻辑视图)给前端控制器
  6. 前端控制器将ModelAndView发送给视图解析器,解析出物理视图返回给前端控制器
  7. 前端控制器渲染视图,发送视图给用户

4.ArrayList 和 LinkedList 的区别是什么?HashMap 和 Hashtable 有什么区别?StringBuild跟StringBuffer的区别

ArrayList和LinkedList的区别

  1. 数据结构:ArrayList是数组,默认长度是10,扩容因子是0.75,然后扩容1.5倍;LinkedList是双向链表

  2. ArrayList查找性能高(因为通过下标快速定位),插入和删除性能低(移动大量数据)

  3. LinkedList查找性能低(因为要向前或向后依次查找),插入和删除姓高(只需要修改前后指针,不用移动)

HashTable和HashMap的区别

  1. Hashtable是线程安全的,HashMap是非线程安全的

  2. HashMap的性能高于Hashtable

  3. Hashtable不能接收null作为键和值,HashMap可以

StringBuffer和StringBuilder的区别

  1. StringBuffer对字符串的操作的方法都加了synchronized,保证线程安全;StringBuilder不保证线程安全,在方法体内需要进行字符串的修改操作,可以new StringBuilder对象,调用StringBuilder对象的append、replace、delete等方法修改字符串。
  2. 运行速度,或者说是执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer 
  3. 在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的
  4. StringBuilder适用于单线程下在字符缓冲区进行大量操作的情况;StringBuffer适用多线程下在字符缓冲区进行大量操作的情况

5.MyBatis中#{}和${}的区别

#{}应用PreparedStatement的占位符?插入,能防止SQL注入

${}应用字符串的拼接,不能防止SQL注入

6.讲讲Redis,项目中有用过Redis吗?

Redis是一个高性能的内存数据库,以key-value方式存储数据,可以作为缓存使用。

Redis的特点

  •         性能高(读的速度是110000次/s,写的速度是81000次/s,单机redis支撑万级并发)
  •         支持多种存储类型
  •         丰富的特性(发布订阅、事务、过期策略等)
  •         支持持久化
  •         单线程 (避免上下文切换,线程同步问题)

redis的几种问题跟解决方案

1)缓存击穿

高并发的情况下,短时间内缓存会被穿过,请求直接打到数据库上,可能导致数据库压力过大。

解决方案:对代码上锁(双重检查锁)

2)缓存穿透

高并发的情况下,如果查询不存在的数据,因为缓存和数据库都不存在,请求都会打到数据库上,可能导致系统崩溃。

解决方案:

1) 保存不存在的数据到缓存中,设置一定过期时间

2) 布隆过滤器(直接过滤掉不存在数据的请求) 不能准确判断是否存在数据,能准确判断数据不存在

3)缓存雪崩

高并发的情况下,缓存服务器重启或热点数据同时过期,全部访问数据库,导致数据库宕机

解决方案:

1)配置缓存集群

2)尽量给热点数据设置不一样的过期时间,相对均匀

7.一些常见集合的区别

1、list和set是实现了collection接口的。

2、list可以允许重复的对象。可以插入多个null元素。是一个有序容器,保持了每个元素的插入顺

序,输出的顺序就是插入的顺序。Set不允许重复对象,无序容器,你无法保证每个元素的存储顺

序,TreeSet通过 Comparator 或者 Comparable 维护了一个排序顺序。只允许一个 null 元素。

3、Map不是collection的子接口或者实现类。Map是一个接口。Map 的 每个 Entry 都持有两个对

象,也就是一个键一个值,Map 可能会持有相同的值对象但键对象必须是唯一的。TreeMap 也通

过 Comparator 或者 Comparable 维护了一个排序顺序。Map 里你可以拥有随意个 null 值但最多

只能有一个 null 键。

8.常见的设计模式

1.装饰者设计模式

开闭原则:程序开发过程中,对功能的扩展开放,对功能的修改关闭,提高程序的稳定性

目的:在不修改原有类的代码基础上,对类的功能进行扩展

实现

        1.装饰者和被装饰者都要实现相同的接口或继承相同的父类

        2.装饰者中定义一个被装饰者的对象

        3.给装饰者传入被装饰者对象

        4.调用装饰者的方法时,也调用被装饰者的方法,同时进行功能的扩展

2.单例设计模式

目的:保证一个类只有一个实例(对象)

应用场景:

对于某些大的对象,单例模式能节省系统资源

实现 :

  1. 将所有构造方法定义为private
  2. 在类中创建一个静态的对象
  3. 在类中定义一个静态方法来返回该对象

两种单例模式

饿汉式:类中一开始就创建对象,不管后面是否使用对象,都消耗了内存。

懒汉式:类中一开始不创建对象,调用返回对象方法时再创建对象。

3.代理模式

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。

作用

1)中介的作用,当调用者不能或不方便调用某个对象时,代理起到中介的作用,帮助调用者间接

的调用对象。

2)符合开闭原则,在不修改原有类代码的前提下,对类的功能进行增强。

代理模式分为两种:

1) 静态代理,在运行前,通过编写代码的方式生成代理类

2) 动态代理,在运行后,通过反射机制生成代理类

静态代理

1)代理者和被代理者都实现相同的接口

2)代理者包含被代理者的对象

3)创建代理对象时传入被代理对象

4)代理者执行方法时,会调用被代理者的方法,同时扩展新的功能

静态代理的问题:一个代理类只能代理一种业务,如果有多种业务,就必须创建大量的代理类。

动态代理

和静态代理不同,动态代理是在运行时,通过反射机制动态生成代理类。开发者不需要手动编写新

的代理类。

动态代理分类

JDK动态代理

JDK自带的,前提是:被代理类必须实现过接口。

实现步骤

  1. 实现InvocationHandler接口
  2. 实现invoke方法
  3. 通过Proxy.newProxyInstance方法返回代理对象

CGLib动态代理

需要引入CGLib依赖,它的原理是:通过反射+继承机制动态生成被代理类的子类,所以被代理类

不能是final的。

实现步骤

  1. 引入cglib
  2. 实现MethodInterceptor接口
  3. 实现intercept方法
  4. 通过Ehancer返回代理对象

9.消息队列

1.一对一

        一个生产者发送消息到一个队列,一个消费者从队列中取消息。

2.工作队列

        生产者将消息分发给多个消费者,如果生产者生产了100条消息,消费者1消费50条,消费者2消费50条。

        实现能者多劳:

channel.basicQos(1);限制队列一次发一个消息给消费者,等消费者有了反馈,再发下一条

channel.basicAck 消费完消息后手动反馈,处理快的消费者就能处理更多消息

basicConsume 中的参数改为false

3.发布/订阅模式

        发布/订阅模式和工作模式的区别是:工作模式只存在一个队列,多个消费者共同消费一个队列中的消息;而发布订阅模式存在多个队列,不同的消费者可以从各自的队列中处理完全相同的消息。

4.路由模式

        路由模式的消息队列可以给队列绑定不同的key,生产者发送消息时,给消息设置不同的key,这样交换机在分发消息时,可以让消息路由到key匹配的队列中。

5.主题模式

        主题模式和路由模式差不多,在key中可以加入通配符:

        * 匹配任意一个单词 com.*

        # 匹配.号隔开的多个单词 com.* 

10.RabbitMQ的用途

1)解耦

服务之间进行解耦,A服务调用B服务时,需要编写相关的代码,调用情况发生改变时,需要修改

调用的代码

2)异步

传统的同步调用方式,需要等待调用完成,才能进行其它业务

异步调用方法,将消息发送到队列中,就可以返回,执行其它业务,速度大大提升

3)削峰

出现流量激增的情况时,消息队列可以设置消息的最大数量,处理一部分消息,其它消息交给队列

排队处理

11.spring事务配置的具体流程

(1)事务的传播性:@Transactional(propagation=Propagation.REQUIRED) 

      如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)

(2)事务的超时性:@Transactional(timeout=30) //默认是30秒 

      注意这里说的是事务的超时性而不是Connection的超时性,这两个是有区别的

(3)事务的隔离级别:@Transactional(isolation = Isolation.READ_UNCOMMITTED) ,未提交

读,就是一个事务可以读取另一个未提交事务的数据。读取未提交数据(会出现脏读, 不可重复读)

基本不使用;

  @Transactional(isolation = Isolation.READ_COMMITTED),已提交读,就是一个事务要等另

一个事务提交后才能读取数据。可以解决脏读,可能会出现不可重复读问题;

  @Transactional(isolation = Isolation.REPEATABLE_READ),重复读,就是在开始读取数据

(事务开启)时,不再允许修改操作,重复读可以解决不可重复读问题。不可重复读对应的是修

改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不

是UPDATE操作。

  @Transactional(isolation = Isolation.SERIALIZABLE) ,是最高的事务隔离级别,在该级别下,

事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较

耗数据库性能,一般不使用。

12.创建线程的方式

1.继承Thread类

  1. 继承Thread类      
  2. 重写run方法      
  3. 调用start启动线程

2.实现Runnable接口

  1. 实现Runnable接口        
  2. 实现run方法
  3. 创建实现Runnable接口的对象,传入Thread对象中    
  4. 启动线程

3.实现Callable接口

  1. 实现Callable接口,实现call方法      
  2. 创建Callable对象,传入FutureTask对象
  3. 创建FutureTask对象,传入Thread对象      
  4. 启动线程
  5. 调用get方法得到返回结果

4.使用线程池 

13.数据库建表的时候int(1)和int(2)的区别

这个int(M)我们可以简单的理解为:

这个长度是为了告诉MYSQL数据库,我们这个字段的存储的数据的宽度为M位数, 当然如果你不是

M位数(只要在该类型的存储范围之内)MYSQL也能正常存储。

设置这个属性后往表时插入数据,系统会自动把uid字段M不够3位的在左侧用0来填充

14.Vector是如何实现的安全的

Vector与ArrayList容器的实现原理是一毛一样的,都是封装了一个数组,并且把数组的扩容、缩小交给容器自己管理。不过Vector支持多线程并发访问,因为修改容器的方法都加上了synchronized关键字修饰(this对象锁,锁住整个容器对象)。由于底层是数组,所以随机访问(通过下标访问)效率高,但是在删除节点、插入节点时比较麻烦,删除节点时需要进行大量元素的前移,插入节点时需要进行大量元素的后移,数组容量有限,还需要进行扩容(整个容器中的元素都要复制一遍),效率比较低。

15.讲讲你知道哪些排序方法,并说下他们的时间复杂度

16.讲讲Elasticsearch

Elasticsearch具备以下特点:

  • 分布式,无需人工搭建集群
  • Restful风格,一切API都遵循Rest原则,容易上手
  • 近实时搜索,数据更新在Elasticsearch中几乎是完全同步的。
  • 有多条件的查询方式,自定义排序

ElasticSearch常用的几种查询方式,terms 查询是term的扩展,可以支持多个vlaue匹配,只需要一个匹配就可以了。

1 term查询(精准查询)

term是ES中的精准查询,不会参与ES分词查询。

2 math查询(分词匹配查询)

match查询是按ES分词的倒排表进行查询,而keyword不会被分词,match的需要跟keyword的完全匹配可以。可以用于一般性的匹配查询。

3 fuzzy查询(模糊查询)

fuzzy查询可以用于纠正去拼写的问题,fuzziness是可以允许纠正错误拼写的个数

4  wildcard(通配符查询)

通配符查询允许我们指定一个模式来匹配,而不需要指定完整的trem,匹配的方式类似于match的

分词匹配查询。

?将会匹配如何字符;*将会匹配零个或者多个字符。

5 bool查询(布尔查询)

bool查询本身没有查询功能,而是基于逻辑值使用前面几种查询方式进行组合查询。

17.jq选择器,如何选中元素

  • 基本过滤选择器
  • 内容过滤选择器
  • 可见过滤选择器
  • 属性过滤选择器
  • 子标签过滤选择器
  • 表单属性过滤选择器
  • 表单元素过滤选择器

18.Linux常用命令

cd 目录名
ls 目录名(不写就是查看当前目录)
    -l  详细列表
    -a  所有文件

 查看目录详情  ll 目录名
mkdir 目录名

删除目录 rm    目录名
    -r  遍历所有子目录
    -f  强制删除
创建\打开文件  vi  文件名
三种模式:
    命令模式    能删除、复制、粘贴,无法编辑
    输入模式    编辑内容
    命令行模式  退出、保存文件
    操作方式:默认进入命令模式,按i进入输入模式,按esc回到命令模式,按:进入命令行模式
命令模式
    x   删除一个字符
    dd  删除一行
    yy  复制一行
    p   粘贴
    u   撤销
命令行模式
    wq  保存退出
    q   退出 (如果有修改,此方式退出会出错)
    q!  强制退出

cat  显示整个文件内容(不支持分页)
more 分页显示(只能向前分页查询)
less 分页显示,功能比more强大。(支持前后分页查询)
    支持文本查找,/查找内容  向下查询 ;  ?查找内容   向上查找内容
    退出less模式,点击q
    
    对比vi命令:cat、more、less仅仅是用来进行文本阅读命令,vi用来进行文本编辑的命令。
    
    查询当前文件中是否包含有java单词,如果包含有Java单词的所有行全部显示出来。
    cat 文件名 | grep 查询文字

find 查找目录 -name 文件名称  
find 查找目录 | grep 名称  

which 命令名称

移动文件
mv 原文件 目标文件
复制文件
cp 原文件 目标文件

解压指令:tar -zxvf  
压缩指令:tar -zcvf 
-z:表示压缩和解压缩的格式为gz压缩文件(gzip)
-c::表示压缩
-x:表示解压缩
-v:表示显示压缩或者解压缩的详细过程。
-f:表示指定压缩或者解压缩的文件,只能放在命令的最后
tar -zcvf demo.tar.gz demo2.txt 
tar -cvf 压缩后的文件名称  待压缩的文件 
tar -xvf 待解压的解压包名 

通过端口查看进程:netstat –apn | grep 8080
通过端口查看进程:lsof -i:3306
通过进程名称查询进程:ps -ef | grep redis
杀死进程:kill -9 PID(进程ID,-9表示强制杀死)

19.mysql跟oracle的区别

一、宏观上:
1、Oracle是大型的数据库而Mysql是中小型数据库;Mysql是开源的,Oracle是收费的,且价格昂贵。

2、Oracle支持大并发,大访问量,是OLTP的最好的工具。

3、安装占用的内存也是有差别,Mysql安装完成之后占用的内存远远小于Oracle所占用的内存,并且Oracle越用所占内存也会变多。

二、微观上:
1、对于事务的支持

Mysql对于事务默认是不支持的,只是有某些存储引擎中如:innodb可以支持;而Oracle对于事务

是完全支持的。

2、并发性

Mysql,既支持表锁,也支持行级锁。表锁,对资源锁定的力度很大,如果一个session对一个表加

锁时间过长,会让其他session无法更新此表的数据。

Oracle使用行级锁,对资源锁定的力度要小很多,只是锁定sql需要的资源,并且加锁是在数据库

中的数据行上,不依赖于索引。所以oracle对并发性的支持要好很多。

3、数据的持久性

Oracle保证提交的事务均可以恢复,因为Oracle把提交的sql操作线写入了在线联机日志文件中,

保存到磁盘上,如果出现数据库或者主机异常重启,重启Oracle可以靠联机在线日志恢复客户提交

的数据。

Mysql默认提交sql语句,但是如果更新过程中出现db或者主机重启的问题,也可能会丢失数据。

4、事务隔离级别

MySQL是repeatable read的隔离级别,而Oracle是read commited的隔离级别,同时二者都支持

serializable串行化事务隔离级别,可以实现最高级别的。

读一致性。每个session提交后其他session才能看到提交的更改。Oracle通过在undo表空间中构造

多版本数据块来实现读一致性,每个session 查询时,如果对应的数据块发生变化,Oracle会在

undo表空间中为这个session构造它查询时的旧的数据块。

MySQL没有类似Oracle的构造多版本数据块的机制,只支持read commited的隔离级别。一个

session读取数据时,其他session不能更改数据,但可以在表最后插入数据。session更新数据

时,要加上排它锁,其他session无法访问数据

5、提交方式

Oracle默认不自动提交,需要手动提交。Mysql默认自动提交。

6、逻辑备份

Mysql逻辑备份是要锁定数据,才能保证备份的数据是一致的,影响业务正常的DML(数据操纵语言

Data Manipulation Language)使用;Oracle逻辑备份时不锁定数据,且备份的数据是一致的。

7、sql语句的灵活性

mysql对sql语句有很多非常实用而方便的扩展,比如limit功能(分页),insert可以一次插入多行数

据;Oracle在这方面感觉更加稳重传统一些,Oracle的分页是通过伪列和子查询完成的,插入数据

只能一行行的插入数据。

8、数据复制

MySQL:复制服务器配置简单,但主库出问题时,丛库有可能丢失一定的数据。且需要手工切换

丛库到主库。

Oracle:既有推或拉式的传统数据复制,也有dataguard的双机或多机容灾机制,主库出现问题

是,可以自动切换备库到主库,但配置管理较复杂。

9、分区表和分区索引

MySQL的分区表还不太成熟稳定;Oracle的分区表和分区索引功能很成熟,可以提高用户访问db

的体验。

10、售后与费用

Oracle是收费的,出问题找客服;Mysql是免费的的,开源的,出问题自己解决。

11、权限与安全

Oracle的权限与安全概念比较传统,中规中矩;MySQL的用户与主机有关,感觉没有什么意义,

另外更容易被仿冒主机及ip有可乘之机。

12、性能诊断方面

Oracle有各种成熟的性能诊断调优工具,能实现很多自动分析、诊断功能。比如awr、addm、

sqltrace、tkproof等 ;MySQL的诊断调优方法较少,主要有慢查询日志。

20.索引的作用

加快查询速度,用户查询时,先找索引,通过索引找到实际数据的位置,再直接定位到实际数据上,极大提高查询速度

缺点:

  • 也是数据,需要占存储空间
  • 降低增删改的速度
  • 创建索引也需要一定的时间

21.深拷贝和浅拷贝的区别

1、浅拷贝:将原对象或原数组的引用直接赋给新对象,新数组,新对象数组只是原对象的一个引

用。

2、深拷贝:创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过

来,是“值”而不是“引用”。

22.sleep和wait的区别

  1. 调用对象不同

    wait() 由锁对象调用

    sleep() 由线程调用

  2. 锁使用不同

    执行wait后,自动释放锁

    执行sleep后,不会释放锁

  3. 唤醒机制不同

    执行wait后,可以被通知唤醒

    执行sleep后,只能等待时间结束后,自动唤醒

23.在jdbc中是使用什么实现占位符

Statement 接口的两个问题:
  1.使用 Statement 接口 对象发送的 sql 语句需要再数据库进行一次编译之后成为指令才能执行, 每条 sql 语句都需要编译一次, 这样是很慢的.
  2.使用 Statement 接口 操作的 sql 语句需要使用字符串的拼接方式实现,  这样的方式可能存在 sql 注入的安全风险并且拼接字符串比较麻烦.

PreparedStatement 接口的优点:
  1.使用该接口操作的 sql 语句会先预先编译成指令在发送给数据库, 数据库就执行指令即可, 这样就提高了一定速度,
  2.该接口可以避开 sql 需要使用字符串拼接的方式, 从而解决 sql 注入的安全风险,而是使用占位符 (?) 来代替原来的字符串拼接.

24.讲讲单点登录 

  1. 添加用户服务,实现用户登录(Security)

  2. 登录成功后,将用户信息转换为JWT格式,保存到Cookie中或者是在页面中,在将Token放入请求头中

  3. 在网关中添加全局过滤器,对请求进行拦截,获得其中的Token,进行解码,成功就放行

当然还有其他的解决方法,比如说将用户信息存放到Redis中,但是有2个问题

问题1:key的设置,区分前端客户端

问题2:属于有状态登录,在服务器端保存用户信息,需要额外的开销,REST风格不推荐使用有状态登录

同样的Token过期如何处理。

我的解决方法是在用户进行跳转页面的时候重新设置一个JWT的Token,再返回一个新时间的Token,可能比较粗糙,各位大佬可以各抒己见。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值