Java面试八股文宝典之SpringMVC篇备战2023 查缺补漏 你越早准备 越早成功!!!——Day15
Posted 陶然同学
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java面试八股文宝典之SpringMVC篇备战2023 查缺补漏 你越早准备 越早成功!!!——Day15相关的知识,希望对你有一定的参考价值。
大家好,我是陶然同学,软件工程大三即将实习。认识我的朋友们知道,我是科班出身,学的还行,但是对面试掌握不够,所以我将用这100多天更新Java面试题🙃🙃。
不敢苟同,相信大家和我一样,都有一个大厂梦,作为一名资深Java选手,深知面试重要性,接下来我准备用100天时间,基于Java岗面试中的高频面试题,以每日3题的形式,带你过一遍热门面试题及恰如其分的解答。当然,我不会太深入,因为我怕记不住!!
因此,不足的地方希望各位在评论区补充疑惑、见解以及面试中遇到的奇葩问法,希望这100天能够让我们有质的飞越,一起冲进大厂!!,让我们一起学(juan)起来!!!
SpringMVC的执行流程
1.用户发送请求到前端控制器(DispatherServlet)
2.前端控制器(DispatherServlet)再找到处理器映射器(HandlerMapping)
3.处理器映射器(HandlerMapping)返回给前端控制器(DispatherServlet)一个执行链
(HandlerExecutionChain)
4.前端控制器(DispatherServlet)请求处理器适配器(HandlerAdapter)
5.处理器适配器(HandlerAdapter)找到对应的映射器(Handler)处理对应的请求
6.映射器(Handler)处理完成请求后返还给处理器适配器(HandlerAdapter)一个ModelAndView
7.处理器适配器(HandlerAdapter)将ModelAndView返回给前端控制器(DispatherServlet)
8.前端控制器(DispatherServlet)请求视图解析器(ViewResolver)进行视图解析
9.视图解析器(ViewResolver)返回给前端控制器(DispatherServlet)一个View对象
10.前端控制器(DispatherServlet)通过例如JSP,freemark等进行视图渲染
11.前端控制器(DispatherServlet)最后展示给用户
用户发送请求给前端控制器,前端控制器去找控制器映射器,控制器映射器返回给前端控制器一个
执行链,前端控制器请求处理器适配器,处理器适配器去找执行器执行处理,处理器执行完处理返
回给处理器适配器一个ModelAndView,处理器适配器再将ModelAndView返回给前端控制器,前端
控制器请求视图解析器,视图解析器返回给前端控制器View对象,前端控制器再对视图进行渲
染,最后响应给用户
SpringMVC常用注解
1、@Controller
@Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象。
2、@RequestMapping
用于处理请求 url 映射的注解,可用于类或方法上。
3、@Resource和@Autowired
@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它
的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。
4、@RequestParam
把请求中指定名称的参数给控制器中的形参赋值。
5、@RequestBody
- 用于获取请求体内容。 直接使用得到是 key=value&key=value…结构的字符串。
- 把获得json类型的数据转成pojo对象。
6、@ResponseBody
@ResponseBody用于将 Controller 的方法返回的对象,通过 HttpMessageConverter 接口转换为
指定格式的数据如: json,xml 等,通过 Response 响应给客户端
7、@ PathVariable
用于绑定 url 中的占位符。 例如:请求 url 中 /delete/id, 这个id就是 url 占位符。
8、@RequestHeader
用于获取请求消息头。
9、@CookieValue
用于把指定 cookie 名称的值传入控制器方法参数。
10、@ModelAttribute
代表的是:该Controller的所有方法在调用前,先执行此@ModelAttribute方法,可用于注解和方法
参数中,可以把这个@ModelAttribute特性,应用在BaseController当中,所有的Controller继承
BaseController,即可实现在调用Controller时,先执行@ModelAttribute方法。
SpringMVC怎么样设定重定向和转发的
(1)转发:在返回值前面加"forward:",例如"forward:user.do?name=method4"
(2)重定向:在返回值前面加"redirect:",例如"redirect:http://www.baidu.com"
服务端开发之Java备战秋招面试3
今天继续学习,先做两题算法题练练手,在继续整理八股文,深入理解,才能在面试的时候有更好地表现,一起加油吧,希望秋招多拿几个令人心动的offer,冲吧。
目录
5.mysql事务特性?mysql隔离级别?mysql怎么实现可重复读的呢?
8. CAS(Compare And Swap) 、synchronized、volatile?
9.ThreadLocal是否可保证资源的同步?它会导致内存泄漏?
1、算法题:判断链表中是否有环
题目链接:判断链表中是否有环_牛客题霸_牛客网
判断链表是否有环,用快慢指针即可,快指针每次走两步,慢指针每次走一步,如果慢指针不为空,快慢指针能相遇,则链表有环,否则,链表无环。
注意:边界值的处理,防止空指针异常。
/**
* Definition for singly-linked list.
* class ListNode
* int val;
* ListNode next;
* ListNode(int x)
* val = x;
* next = null;
*
*
*/
public class Solution
public boolean hasCycle(ListNode head)
if(head == null || head.next == null)
return false ;
ListNode fast = head;
ListNode slow = head ;
while(fast != null && fast.next != null)
fast = fast.next.next ;
slow = slow.next ;
if(slow == fast)
return true ;
return false ;
2、算法题:合并二叉树
可以使用递归法和迭代法合并,递归法如下,递归合并到树t1上。
import java.util.*;
/*
* public class TreeNode
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
*
*/
public class Solution
/**
*
* @param t1 TreeNode类
* @param t2 TreeNode类
* @return TreeNode类
*/
public TreeNode mergeTrees (TreeNode t1, TreeNode t2)
// write code here
//合并到t1上
if(t1 == null && t2 == null)
return null ;
if(t1 == null)
return t2 ;
if(t2 == null)
return t1 ;
t1.val = t1.val + t2.val ;
t1.left = mergeTrees(t1.left,t2.left) ;
t1.right = mergeTrees(t1.right,t2.right) ;
return t1 ;
迭代法如下:
建立一个队列,层次遍历的方式,累加相应的节点值。
import java.util.*;
/*
* public class TreeNode
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
*
*/
public class Solution
/**
*
* @param t1 TreeNode类
* @param t2 TreeNode类
* @return TreeNode类
*/
public TreeNode mergeTrees (TreeNode t1, TreeNode t2)
// write code here
//合并到t1上
if(t1 == null && t2 == null)
return null ;
if(t1 == null)
return t2 ;
if(t2 == null)
return t1 ;
Queue<TreeNode> queue = new LinkedList() ;
queue.offer(t1) ;
queue.offer(t2) ;
while(!queue.isEmpty())
TreeNode node1 = queue.poll() ;
TreeNode node2 = queue.poll() ;
node1.val = node1.val + node2.val ;
if(node1.left != null && node2.left != null)
queue.offer(node1.left) ;
queue.offer(node2.left) ;
if(node1.right != null && node2.right != null)
queue.offer(node1.right) ;
queue.offer(node2.right) ;
if(node1.left == null && node2.left != null)
node1.left = node2.left ;
if(node1.right == null && node2.right != null)
node1.right = node2.right ;
return t1 ;
3.什么是跳表?
1)跳表,又叫做跳跃表、跳跃列表,在有序链表的基础上增加了“跳跃”的功能。
2)跳表在原来的有序链表上加上了多级索引,通过索引来快速查找;可以支持快速的删除、插入和查找操作。
3)跳表实际上是一种增加了前向指针的链表,是一种随机化的数据结构。
4)Redis中 的 SortedSet、LevelDB 中的 MemTable 都用到了跳表。
5)对比平衡树, 跳表的实现和维护会更加简单, 跳表的搜索、删除、添加的平均时间复杂度是 O(logn)。
参考这篇博客:跳表(Skip List)_wink22的博客-CSDN博客_跳表
4.Spring的特性?
三大特性:依赖注入(DI),控制反转(IOC),面向切面编程(AOP).
可以参考这篇博客:【Spring篇】Spring的三大特性_我是不贪嘴吖的博客-CSDN博客_spring特性
5.mysql事务特性?mysql隔离级别?mysql怎么实现可重复读的呢?
ACID:原子性,一致性,隔离性,持久性
读未提交,读已提交,可重复读,串行化。
可重复读是Mysql的默认隔离级别,就是指在同一事务中多次读取的数据是一致的。
实现方法是 Mysql通过多版本并发控制(MVCC):我们常说的MVCC是由MySQL数据库InnoDB存储引擎实现的,并非是由MySQL本身实现的,不同的存储引擎,对MVCC都有不同的实现。就是在InnoDB引擎中,InnoDB在每行记录后面保存两个隐藏的列,分别保存了这个行的创建时间(版本号)和行的删除时间(版本号)。这里存储的并不是实际的时间值,而是系统版本号,当数据被修改时,版本号加1。在读取事务开始时,系统会给当前读事务一个版本号,然后,事务会读取版本号<=当前版本号的数据。此时如果其他写事务修改了这条数据(增删改),那么这条数据的版本号就会加1,从而比当前事务的版本号高,所以,当前事务读取的并不是更新后的数据,而是当前事务对应版本下的数据。
参考链接:mysql 可重复读的实现原理_Vance.的博客-CSDN博客_mysql可重复读实现原理
6.讲一下B树,B+树,哈希表,AVL树,红黑树?
这2篇介绍了红黑树,B树,B+树,讲的还可以:为什么要使用红黑树,B树和B+树_-停泊的博客-CSDN博客_索引 为什么用红黑树
B树、B+树、红黑树_prefect_start的博客-CSDN博客_b树 b+树 红黑树
这篇文章看完了,写的比较全面:面试题
7.怎么保证HashMap的线程安全?
通过ConcurrentHashMap线程安全类实现:JDK1.7通过对segment加锁,片段锁。JDK1.8降低了锁的粒度,通过对头结点加锁来保证线程安全的。
看完理论这篇博客,讲的还可以:ConcurrentHashMap是如何保证线程安全的?_Tom弹架构的博客-CSDN博客
8. CAS(Compare And Swap) 、synchronized、volatile?
1、 synchronized 是悲观锁,属于抢占式,会引起其他线程阻塞。
2、 volatile 提供多线程共享变量可见性和禁止指令重排序优化。
3、 CAS 是基于冲突检测的乐观锁(非阻塞)。
参考这篇,写的很好:基础篇:详解锁原理,synchronized、volatile+cas底层实现 - 知乎
9.ThreadLocal是否可保证资源的同步?它会导致内存泄漏?
- 当使用ThreadLocal声明变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本
- 从上面的概念可知,ThreadLocal其实并不能保证变量的同步性,只是给每一个线程分配一个变量副本
hreadLocalMap
是使用ThreadLocal
的弱引用作为Key
的,弱引用的对象在 GC 时会被回收。ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用来引用它,那么系统 GC 的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永远无法回收,造成内存泄漏。- 每次使用完
ThreadLocal
,都调用它的remove()
方法,清除数据,防止内存泄漏。
参考这篇博文:ThreadLocal使用注意:线程不安全,可能会发生内存泄漏_深山猿的博客-CSDN博客_threadlocal有什么隐患
10.jvm 垃圾回收是什么时候触发?
参考这篇博客:jvm :垃圾回收是什么时候触发? 垃圾回收算法? 有哪些垃圾回收器?_花和尚也有春天的博客-CSDN博客
11.线程池有哪几种类型?
1、newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理所需,可灵活回收空闲线程,若线程数不够,则新建线程。
2、newFixedThreadPool:创建一个固定大小的线程池,即指定线程数的线程。可控制并发的线程数量,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
3、newSingleThreadExecutor:创建一个单线程的线程池,即只创建唯一的工作者线程来执行任务,,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
4、newScheduleThreadPool:创建一个定长的线程池,支持定时及周期性任务执行。
推荐通过 new ThreadPoolExecutor() 的写法创建线程池,这样写线程数量更灵活,开发中多数用这个类创建线程。
12.TCP三次握手四次挥手?
TCP的三次握手和四次挥手实质就是TCP通信的连接和断开。
三次握手:为了对每次发送的数据量进行跟踪与协商,确保数据段的发送和接收同步,根据所接收到的数据量而确认数据发送、接收完毕后何时撤消联系,并建立虚连接。
四次挥手:即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。
三次握手过程详细说明:
1、客户端发送建立TCP连接的请求报文,其中报文中包含seq序列号,是由发送端随机生成的,并且将报文中的SYN字段置为1,表示需要建立TCP连接。(SYN=1,seq=x,x为随机生成数值);
2、服务端回复客户端发送的TCP连接请求报文,其中包含seq序列号,是由回复端随机生成的,并且将SYN置为1,而且会产生ACK字段,ACK字段数值是在客户端发送过来的序列号seq的基础上加1进行回复,以便客户端收到信息时,知晓自己的TCP建立请求已得到验证。(SYN=1,ACK=x+1,seq=y,y为随机生成数值)这里的ack加1可以理解为是确认和谁建立连接;
3、客户端收到服务端发送的TCP建立验证请求后,会使自己的序列号加1表示,并且再次回复ACK验证请求,在服务端发过来的seq上加1进行回复。(SYN=1,ACK=y+1,seq=x+1)。
四次挥手过程详细说明:
1、客户端发送断开TCP连接请求的报文,其中报文中包含seq序列号,是由发送端随机生成的,并且还将报文中的FIN字段置为1,表示需要断开TCP连接。(FIN=1,seq=x,x由客户端随机生成);
2、服务端会回复客户端发送的TCP断开请求报文,其包含seq序列号,是由回复端随机生成的,而且会产生ACK字段,ACK字段数值是在客户端发过来的seq序列号基础上加1进行回复,以便客户端收到信息时,知晓自己的TCP断开请求已经得到验证。(FIN=1,ACK=x+1,seq=y,y由服务端随机生成);
3、服务端在回复完客户端的TCP断开请求后,不会马上进行TCP连接的断开,服务端会先确保断开前,所有传输到A的数据是否已经传输完毕,一旦确认传输数据完毕,就会将回复报文的FIN字段置1,并且产生随机seq序列号。(FIN=1,ACK=x+1,seq=z,z由服务端随机生成);
4、客户端收到服务端的TCP断开请求后,会回复服务端的断开请求,包含随机生成的seq字段和ACK字段,ACK字段会在服务端的TCP断开请求的seq基础上加1,从而完成服务端请求的验证回复。(FIN=1,ACK=z+1,seq=h,h为客户端随机生成)
至此TCP断开的4次挥手过程完毕。
参考这篇博客:一文搞懂TCP的三次握手和四次挥手_不脱发的程序猿的博客-CSDN博客_三次握手和四次挥手
注意:TCP两次握手行不行?
因为TCP协议是双向的,第三次握手是为了使得sever知道客户答应了连接的请求。其中两次握手只能确定从客户端到服务端的网络是可达的,但却无法保证从服务端到客户端的网络是可达的。所以我们一定要保证双向的可达。
还有一个重要的原因:seq(序号)的初始化,要互相确定两方互相开始发送的序号,便于之后的发送、控制、纠错等等。
假如我们使用两次握手,则我们可以设想这么一种情况:客户向服务器进行请求,服务器同意并分配资源,向客户也发送信息,但此时这条信息因为网络原因丢失,客户便不了解服务器收没收到它发出的信息,则客户会继续向服务器发出请求,服务器若接收到,会认为是别的机器进行请求,继续分配资源,且也依然无法建立从服务器到客户的连接。
13.OSI七层和TCP/IP四层?OSI五层?
看下面一个图就可以大概了解了,如下:应用层、表示层、会话层可以归为应用层,数据链路层和物理层可以归结为网络接口层。
参照这篇博客:OSI七层和TCP/IP四层、五层协议 - 一切随风
14.算法题:两个有序链表合并?
思路1:迭代法,定义一个头节点为0的,依次遍历合并即可,最后再把头节点去掉。
/*
public class ListNode
int val;
ListNode next = null;
ListNode(int val)
this.val = val;
*/
public class Solution
public ListNode Merge(ListNode list1,ListNode list2)
//迭代法
if(list1 == null)
return list2 ;
if(list2 == null)
return list1 ;
ListNode pHead = new ListNode(0) ;
ListNode cur = pHead ;
while(list1 != null && list2 != null)
if(list1.val <= list2.val)
cur.next = list1 ;
list1 = list1.next ;
else
cur.next = list2 ;
list2 = list2.next ;
cur = cur.next ;
//哪个链表还未结束,接在后面
cur.next = list1 != null ? list1 : list2 ;
//去掉头节点的0
return pHead.next ;
思路2:递归法。递归合并。
/*
public class ListNode
int val;
ListNode next = null;
ListNode(int val)
this.val = val;
*/
public class Solution
public ListNode Merge(ListNode list1,ListNode list2)
//递归法
if(list1 == null)
return list2 ;
if(list2 == null)
return list1 ;
//若list1的值更小,则递归合并后接到list1上
if(list1.val <= list2.val)
list1.next = Merge(list1.next,list2) ;
return list1 ;
else//反之,递归合并接到list2上
list2.next = Merge(list1,list2.next) ;
return list2 ;
15.算法题:反转链表
题目链接:反转链表_牛客题霸_牛客网
思路1:在遍历链表时,将当前节点的next 指针改为指向前一个节点。由于节点没有引用其前一个节点,因此必须事先存储其前一个节点。在更改引用之前,还需要存储后一个节点。最后返回新的头引用。
/*
public class ListNode
int val;
ListNode next = null;
ListNode(int val)
this.val = val;
*/
public class Solution
public ListNode ReverseList(ListNode head)
ListNode pre = null ;
while(head != null)
//先定义下一个指针,防止被覆盖
ListNode head_next = head.next ;
//让当前元素指向pre
head.next = pre ;
//更新pre
pre = head ;
//更新当前元素
head = head_next ;
return pre ;
递归法:每次递归反转两个,如下:
/*
public class ListNode
int val;
ListNode next = null;
ListNode(int val)
this.val = val;
*/
public class Solution
public ListNode ReverseList(ListNode head)
//递归法
if(head == null || head.next == null)
return head ;
ListNode ans = ReverseList(head.next) ;
head.next.next = head ;
head.next = null ;
return ans ;
以上是关于Java面试八股文宝典之SpringMVC篇备战2023 查缺补漏 你越早准备 越早成功!!!——Day15的主要内容,如果未能解决你的问题,请参考以下文章
服务端开发之Java备战秋招面试篇2-HashMap底层原理篇