手把手教你如何玩转面试(高阶知识点)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Cs_hnu_scw/article/details/82216640

本篇是讲解关于JavaWeb岗位面试方面的一些对于数据结构知识的整理,当然,不只是需要知道这个方面的内容,还需要掌握其他方面的知识,我都根据自己的经历来进行了整理,方便大家进行系统化的学习,只有多复习多研究,才能对技术有更好的掌握,才能拿到更好的offer。
下面是其他方面的知识点,欢迎大家进行浏览
Spring的精华:https://blog.csdn.net/cs_hnu_scw/article/details/78677502
Hibernate的精华:https://blog.csdn.net/cs_hnu_scw/article/details/78762294
Java基础:https://blog.csdn.net/Cs_hnu_scw/article/details/79635874
计算机网络:https://blog.csdn.net/Cs_hnu_scw/article/details/79896621
Web方向:https://blog.csdn.net/Cs_hnu_scw/article/details/79896165
数据库:https://blog.csdn.net/Cs_hnu_scw/article/details/79896384
操作系统:https://blog.csdn.net/Cs_hnu_scw/article/details/79896500
数据结构:https://blog.csdn.net/Cs_hnu_scw/article/details/79896717

目录

1:Git中的常用命令?请说一下merge和rebase的作用和不同点?

答:常用命令的话,主要还是要掌握一些基本的,比如clone , commit,add,merge,rebase这些都还是需要了解的。
下面的两个图就显示了merge和rebase它们两者之间的差距,它们的功能都是进行分支的合并,但是处理的形式就有点不一样了。
这里写图片描述

2:软件开发的生命周期模型?

答:(1)瀑布模型———按照固定的顺序进行开发
(2)螺旋模型———-按照迭代的形式进行开发,基于瀑布模型
(3)喷泉模型———各个开发顺序之间没有明显的间隙,各个开发阶段是可以并行开发;

3:Web安全之XSS攻击?

(1)定义:跨站脚本攻击(Cross Site Scripting),缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的;
(2)原理:攻击者对含有漏洞的服务器发起XSS攻击(注入JS代码);诱使受害者打开受到攻击的服务器URL;受害者在Web浏览器中打开URL,恶意脚本执行。
(3)攻击方式:
反射型: 发出请求时,XSS代码出现在URL中,作为输入提交到服务器端,服务器端解析后响应,XSS随响应内容一起返回给浏览器,最后浏览器解析执行XSS代码,这个过程就像一次发射,所以叫反射型XSS。
存储型: 存储型XSS和反射型的XSS差别就在于,存储型的XSS提交的代码会存储在服务器端(数据库,内存,文件系统等),下次请求目标页面时不用再提交XSS代码。
(4)预防措施:
编码:对用户输入的数据进行HTML Entity编码
这里写图片描述
过滤:移除用户上传的DOM属性,如onerror等,移除用户上传的style节点,script节点,iframe节点等。
校正:避免直接对HTML Entity编码,使用DOM Prase转换,校正不配对的DOM标签。
(5)案例:
1.常见XSS漏洞1–get请求回显漏洞
当我们的网页使用的是get请求的时候,服务器后端将get请求的字符串编码(在浏览器上使用的是encode编码)回显到页面的时候,如果我们输入的是正常的一段字符,那么,他显示的是一段字符,然而,如果我们输入的是一段html代码呢???那么回显的就是一段html代码,当你的页面允许这样的时候,那么就要注意了,别人可以在你的链接上面加很长一段自己的html,然后他把这个伪造的连接发给别人,说你的官网最近有活动,事实上,是他生成在get请求上做了一点手脚,而你的服务端,回显的时候,产生的这样的问题。。。。
2.常见的XSS漏洞2–论坛回复
当我们再论坛输入的是字符串的时候,下面回显的是,你的回复的字符串信息,但当你输入的是一段html的时候,页面显示的是什么呢???甚至,我们还可以在上面加一段js代码,例如,别人问你这个页面的时候,就可能弹出这个哈哈对话框,这个还是好的,如果别人在js上加个js跳转到其他页面,哈哈,这只要以后任何人访问该页面的时候,是不是都会跳转到其他页面,而永远都进入不了你这个页面了呢???

4:XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式?

答:XML文档定义分为DTD和Schema两种形式,二者都是对XML语法的约束,其本质区别在于Schema本身也是一个XML文件,可以被XML解析器解析,而且可以为XML承载的数据定义类型,约束能力较之DTD更强大。对XML的解析主要有DOM(文档对象模型,Document Object Model)、SAX(Simple API for XML)和StAX(Java 6中引入的新的解析XML的方式,Streaming API for XML),其中DOM处理大型文件时其性能下降的非常厉害,这个问题是由DOM树结构占用的内存较多造成的,而且DOM解析方式必须在解析文件之前把整个文档装入内存,适合对XML的随机访问(典型的用空间换取时间的策略);SAX是事件驱动型的XML解析方式,它顺序读取XML文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过事件回调代码来处理XML文件,适合对XML的顺序访问;顾名思义,StAX把重点放在流上,实际上StAX与其他解析方式的本质区别就在于应用程序能够把XML作为一个事件流来处理。将XML作为一组事件来处理的想法并不新颖(SAX就是这样做的),但不同之处在于StAX允许应用程序代码把这些事件逐个拉出来,而不用提供在解析器方便时从解析器中接收事件的处理程序。

5:情景分析:秒杀一个奖品,让你设计一种软件模型。

可以参考看看这文章:https://blog.csdn.net/longcats_lily/article/details/70665929

6:在你的实际项目中,有用到哪些设计模式?简要的说一下。

答:(1)单例模式:应用于数据库连接池中,这样能够对连接数据库的资源进行管理,减少系统消耗;而且Spring中的bean就是单例
(2)模板模式:比如对于封装基础DAO层的时候,提供了操作数据库的基本方法,但是对于不同的实体类,那么要进行的实际操作又是不一样的;比如自定义实现同步队列器(AQS);
(3)代理模式:应用于Spring的事务管理和日志记录,并且实际的应用在对统计系统中,方法每天调用的次数;

7:对于Git中的命令操作,请说说不同的命令操作的是对应哪部分的仓库代码?

对于Git操作中,我们要非常清楚每种命令对应操作的是什么仓库中的内容,比如本地仓库?远程仓库?还是等等,下面通过一个图进行分析:
这里写图片描述
顺便再提一下一些基本的Git命令:
git init(初始化仓库)
git add 文件名 或者 git add. 或者 git add -A (将文件添加到仓库)
git commit -m “注释” (将暂存文件提交到本地仓库)
git status (查看仓库当前状态)
git log (查看所有commit记录)
git reset HEAD^ (恢复成上次提交的版本) git reset HEAD^^(恢复上上次提交的版本)
git push -u origin master (同步远程仓库)
git rm 文件名 (删除版本库文件)
git clone ssh地址或者https地址 (从远程仓库克隆到本地)
git remote add origin ssh地址或者https地址 (本地仓库内容推送到远程仓库)
git checkout -b dev (创建并切换分支)
git branch dev (创建分支)
git checkout dev (切换分支)
git branch (查看分支)
git merge dev (合并分支)
git branch -d dev (删除分支)
git remote (查看远程库信息)

8:分布式系统之消息队列

消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构。
使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ
消息队列应用场景:
(1)异步处理
场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种
1.串行的方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端
2.并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间
3.引用消息队列
这里写图片描述
(2)应用解耦
场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。如下图
这里写图片描述
引用消息队列描述:
订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功
库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作
假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦
这里写图片描述
(3)流量削锋
应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。
1:可以控制活动的人数:
2:可以缓解短时间内高流量压垮应用。
3:用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面
4:秒杀业务根据消息队列中的请求信息,再做后续处理
(4)日志处理
这里写图片描述
(5)消息通讯
消息通讯是指,消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。
这里写图片描述

9:Redis的优缺点?

优点:单线程redis速度快,可以减少访问数据库的压力,还能够进行分布式锁(当然zookpeer),原因如下
(1)纯内存操作
(2)单线程操作,避免了频繁的上下文切换
(2)采用了非阻塞 I/O 多路复用机制
缺点:
(1)缓存和数据库双写一致性问题
(2)缓存雪崩问题
(3)缓存击穿问题
(4)缓存的并发竞争问题

10:Redis如何解决分布式锁的问题?

场景:对于分布式架构的情况,不可避免的就会出现分布式数据不一致的情况和共享资源的同步。所以,分布式锁是一种比较好的解决方法。
方法一:通过设置key value的形式。通过判断key是否存在value,如果存在,那么就表示有其他进程已经获取到了对应的资源,那么就等待。
缺点:如果在中途,宕机了的话,那么这个资源就一直被锁了,而无法被释放。
方法二:通过redis的原生语句:SET my_key my_value NX PX milliseconds
缺点:如果在使用资源的方法中执行的时间大于预期设置的自动过期时间,那么就当真正释放锁的时候,可能是释放别的进程获取到的锁的资源了,这样导致释放混乱,而不匹配。
方法三:在二的基础上,对value进行随机值得生成,当释放资源的时候,判断是否key对应的value是否是之前当前进程所设置的值,如果不是,表示已经被其他进程所获取了key的锁。
缺点:在这个过程中由于获取锁和释放锁不是原子性的操作,所以,如果当进程A首先获取到锁之后,并且在释放锁的时候,由于网络原因超时,到了自动释放的时间,那么还是会存在释放了其他进程的资源的情况。
方法四:对释放锁的操作进行原子性控制。然而redis中并没有这样的实现方式,所以需要采取其他的方式。比如lua脚本。

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

缺点:对于这种脚步代码,不是每个人都熟悉的,所以普遍性不强,但是能够解决三种的问题;

11:为什么分布式一定要有延时任务?

延时任务引入:在开发中,往往会遇到一些关于延时任务的需求。例如(1)生成订单30分钟未支付,则自动取消(2)生成订单60秒后,给用户发短信。对于这样的任务,我们给一个专业的名字来形容,那就是延时任务。
延时任务和定时任务的区别:
(1)定时任务有明确的触发时间,延时任务没有
(2)定时任务有执行周期,而延时任务在某事件触发后一段时间内执行,没有执行周期
(3)定时任务一般执行的是批处理操作是多个任务,而延时任务一般是单个任务
延时任务的实现方法:(通过判断订单是否超时作为实例分析)
(1)数据库轮询:适用于小型项目
思路:通过一个线程定时的去扫描数据库,通过订单时间来判断是否有超时的订单,然后进行update或delete等操作。
实现方式:可以利用quartz来进行实现。
优缺点:
优点:简单易行,支持集群操作
缺点:(1)对服务器内存消耗大
   (2)存在延迟,比如你每隔3分钟扫描一次,那最坏的延迟时间就是3分钟
   (3)假设你的订单有几千万条,每隔几分钟这样扫描一次,数据库损耗极大
(2)JDK中的延迟队列(DelayQueue):
优缺点:
优点:效率高,任务触发时间延迟低。
缺点:(1)服务器重启后,数据全部消失,怕宕机
   (2)集群扩展相当麻烦
   (3)因为内存条件限制的原因,比如下单未付款的订单数太多,那么很容易就出现OOM异常
   (4)代码复杂度较高
(3)时间轮算法:
思路:时间轮算法可以类比于时钟,如上图箭头(指针)按某一个方向按固定频率轮动,每一次跳动称为一个 tick。这样可以看出定时轮由个3个重要的属性参数,ticksPerWheel(一轮的tick数),tickDuration(一个tick的持续时间)以及 timeUnit(时间单位),例如当ticksPerWheel=60,tickDuration=1,timeUnit=秒,这就和现实中的始终的秒针走动完全类似了。
如果当前指针指在1上面,我有一个任务需要4秒以后执行,那么这个执行的线程回调或者消息将会被放在5上。那如果需要在20秒之后执行怎么办,由于这个环形结构槽数只到8,如果要20秒,指针需要多转2圈。位置是在2圈之后的5上面(20 % 8 + 1)
实现方式:利用Netty的HashedWheelTimer来实现
这里写图片描述

优缺点:
优点:效率高,任务触发时间延迟时间比delayQueue低,代码复杂度比delayQueue低。
缺点:(1)服务器重启后,数据全部消失,怕宕机
   (2)集群扩展相当麻烦
   (3)因为内存条件限制的原因,比如下单未付款的订单数太多,那么很容易就出现OOM异常
(4)redis缓存
思路一:利用redis的zset,zset是一个有序集合,每一个元素(member)都关联了一个score,通过score排序来取集合中的值
思路二:该方案使用redis的Keyspace Notifications,中文翻译就是键空间机制,就是利用该机制可以在key失效之后,提供一个回调,实际上是redis会给客户端发送一个消息。是需要redis版本2.8以上。
优缺点:
优点:(1)由于使用Redis作为消息通道,消息都存储在Redis中。如果发送程序或者任务处理程序挂了,重启之后,还有重新处理数据的可能性。
   (2)做集群扩展相当方便
   (3)时间准确度高
缺点:(1)需要额外进行redis维护
(5)消息队列
思路:我们可以采用rabbitMQ的延时队列。RabbitMQ具有以下两个特性,可以实现延迟队列。
RabbitMQ可以针对Queue和Message设置 x-message-tt,来控制消息的生存时间,如果超时,则消息变为dead letter
lRabbitMQ的Queue可以配置x-dead-letter-exchange 和x-dead-letter-routing-key(可选)两个参数,用来控制队列内出现了deadletter,则按照这两个参数重新路由。
优缺点:
优点: 高效,可以利用rabbitmq的分布式特性轻易的进行横向扩展,消息支持持久化增加了可靠性。
缺点:本身的易用度要依赖于rabbitMq的运维.因为要引用rabbitMq,所以复杂度和成本变高
详细的可以参考这篇文章:https://mp.weixin.qq.com/s/Iii4niOLepOPJrJNyp0j-A

12:为什么需要定时任务?

定时任务定义:指的是在编程过程中无须做复杂控制的前提下执行简单的定时操作。
场景:我们常常需要在特定的时间执行一些任务,比如定时删除服务器存储的数据缓存,定时获取数据以及定时发送推送等等。
实现方法:
(1)Timer类
在java中一个完整的定时任务需要由Timer和TimerTask两个类配合完成。其中Timer是一种工具,线程用其安排在后台线程中执行的任务,可安排任务执行一次或者定期重复执行;而TimerTask是由Timer安排执行一次或者重复执行的任务。
情景一:在指定延迟时间进行执行命令
这里写图片描述
情景二:在指定时间执行定时任务
这里写图片描述
情景三:在延迟指定时间后以指定的间隔时间循环执行定时任务
这里写图片描述
小结:Timer类是一种简单实用的实现定时任务的方法,然而它存在着自身的缺陷:
1:Timer对调度的支持是基于绝对时间而不是相对时间,因此它对于系统时间的改变非常敏感;
2:Timer线程是不会捕获异常的,如果TimerTask抛出未检查的异常则会导致Timer线程终止,同时Timer也不会重新恢复线程的执行,它会错误的认为整个Timer线程都会取消,已经被安排但尚未执行的TimerTask也不会再执行了,新的任务也不能被调度。因此,如果TimerTask抛出未检查的异常,Timer将会产生无法预料的行为。
(2)ScheduledExecutorService
Timer是基于绝对时间的,对系统时间比较敏感,而ScheduledExecutorService则是基于相对时间;Timer内部是单一线程,而ScheduledThreadPoolExecutor内部是个线程池,可以支持多个任务并发执行。
ScheduledExecutor的设计思想是每一个被调度的任务都会由线程池中一个线程去执行,因此任务是并发的,相互之间不会受到干扰;只有当任务的时间到来时,ScheduledExecutor才会真正启动一个线程。

情景一:解决Timer类中的相对时间的问题(ScheduledExecutorService不会出现类似Timer类时间相对的问题,可以将下面代码会Timer类进行实现对比)
这里写图片描述
情景二:解决Timer类中某个抛出异常的问题(ScheduledExecutorService中某个出现异常,而没有出现异常的还会继续执行)
这里写图片描述
小结:可以看到针对Timer类存在的两个缺陷,ScheduledExecutorService可以很好地解决,其思路主要在于每一个被调度的任务都是由线程池中的一个线程去执行,任务之间是并发的,不会互相干扰。
(3)Quartz
Quartz是一个完全由Java编写的开源作业调度框架,为在Java应用程序中进行作业调度提供了简单强大的机制。与前两种方法相比,Quartz对于定时的配置更为丰富,实际应用的场景多。
Quartz最重要的3个基本要素:
(1)Scheduler——调度器,所有的调度都由它控制;
(2)Trigger——定义触发的条件,包括SimpleTrigger和CronTrigger等;
(3)JobDetail & Job——JobDetail定义的是任务数据,而真正的执行逻辑在Job中。Scheduler的每次执行都会根据JobDetail创建一个新的Job实例。
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
详情参考这篇文章:https://mp.weixin.qq.com/s/MbaMgkRfvMUVt1i3a_nFtg

13:分布式中的session一致性管理的方式有哪些?

  1. redis集中管理
    其原理是以SessionId作为Key,序列化后的HttpSession对象作为Value存储在Redis中,然后将SessionId返回给客户端,当下一次客户端发送HTTP请求到服务器的时候,会带上这个SessionId,服务器再根据SessionId从Redis拿到相应的Session数据并反序列化成HttpSession对象。由于对HttpSession对象进行了集中存储,而不是存储在服务器本地内存,所以即使同个用户的多次HTTP请求落到不同的服务器上,也能将SessionId与相应的HttpSession对象关联起来,从而实现分布式环境下的Session共享
  2. session复制
    当客户端在某台服务器上存储了Session数据的时候,我们可以手动地将该Session信息同步到集群中的其他服务器上面去,这样一来所有服务器就都存储了所有客户端的Session信息了,因此即使同个客户端的多次HTTP请求落到不同的服务器上面去,也还是能够拿到相应的Session信息,故而也解决了Session分布式问题
  3. IP过滤
    分布式环境下Session失效也可以说是因为同个客户端在多次请求之间落到不同的服务器上导致的。因此,如果能够保证同个客户端发起的HTTP请求都由相同的服务器进行处理,那么也可以避免Session失效的问题。比如说,Nginx有一种称为IP Hash的负载均衡策略,其可以实现相同IP发送的HTTP请求都路由到同一台服务器上面去,故而也能解决Session分布式问题
  4. cookie存储
    将Session数据存储在客户端,常见的方案是存储在Cookie中,这样一来服务器就无需维护客户端的状态,即服务器“无状态化”,无状态化的好处是利于服务器水平扩展。比如:JWT就是基于Cookie实现用户认证功能的,
    各种方法对应的缺点:
    (1)缺点:redis集中处理,那么就很明显有个问题,当redis挂了之后,那么所有的请求都要挂了,session都无法获取,所以可以采取类似主备思想进行解决。
    (2)缺点:复制就是把所有的服务器都同步,对于小型系统,服务器不多的情况下是比较好的,但是对于大型系统就不适合,因为浪费的资源多。
    (3)缺点:很明显,可用性变差了,就等价于单机系统(因为对应IP都只会请求一个固定服务器了),当对应的服务器一挂,那么整个请求就无法响应,这种可以结合方法二优化。
    (4)缺点:cookie客户端,大的缺点就是伪造性,所以,这个可能会被篡改数据信息,安全性不够好,可以采取加密或者增加服务器端的验证逻辑代码,来提高安全;

14:redis为什么是单线程的还执行效率这么快?

1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
2、数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4、使用多路I/O复用模型,非阻塞IO;
5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
我这里解释一下:多路 I/O 复用模型
多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。
这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响Redis性能的瓶颈,主要由以上几点造就了 Redis 具有很高的吞吐量。
这并不是终点,而是我刚刚的开始,我会一直不断将知识点进行更新,欢迎大家进行关注和阅读!!!

猜你喜欢

转载自blog.csdn.net/Cs_hnu_scw/article/details/82216640