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

  1. 用于获取请求体内容。 直接使用得到是 key=value&key=value…结构的字符串。
  2. 把获得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,冲吧。

目录

1、算法题:判断链表中是否有环

2、算法题:合并二叉树

3.什么是跳表?

4.Spring的特性?

5.mysql事务特性?mysql隔离级别?mysql怎么实现可重复读的呢?

6.讲一下B树,B+树,哈希表,AVL树,红黑树?

7.怎么保证HashMap的线程安全?

8. CAS(Compare And Swap) 、synchronized、volatile?

9.ThreadLocal是否可保证资源的同步?它会导致内存泄漏?

10.jvm 垃圾回收是什么时候触发?

11.线程池有哪几种类型?

12.TCP三次握手四次挥手?

13.OSI七层和TCP/IP四层?OSI五层?

14.算法题:两个有序链表合并?

15.算法题:反转链表


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备战秋招面试3

服务端开发之Java备战秋招面试篇2-HashMap底层原理篇

[Interview]Java 面试宝典系列之 Spring MVC

服务端开发之Java备战秋招面试篇5

服务端开发之Java备战秋招面试篇6-Java各种并发锁

进大厂必备的Java八股文大全(2022最强精简易懂版,八股文中的八股文)备战秋招,赶快转发收藏起来吧~