最新2021-2022年java实习校招秋招春招后端面试题(上)(持续更新)

Java面试题

**本面试题是我本人亲自整理,内包含了很多自己亲身经历的面试题,整理不易,请勿直接转载,有用的3连支持一波。 **

1、上半部分:分为基础、jvm、javaEE、框架、数据库、数据结构与算法、工具、计算机网络、操作系统
2、下半部分:地址跳转
3、java开发编程规范:地址跳转

一、java基础

1、java的8个基本数据类型

整形{long(8)、int(4字节)、short(2)、byte(1)}

浮点型{double(8)、float(4)}

字符型{char(2)}

布尔型{boolean(4)}。

2、&与&&区别

&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算

3、最有效率的2乘8

(2<<3)

4、String不能被继承,final用法

(1)修饰类:表示该类不能被继承;

(2)修饰方法:表示方法不能被重写;

(3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。

多线程操作字符串缓冲区下操作大量数据 StringBuffer:可变字符串、效率低、线程安全;
单线程操作字符串缓冲区下操作大量数据 StringBuilder:可变字符序列、效率高、线程不安全。

5、try{}里有一个return语句,那么紧跟在这个try后的finally{}里的代码会不会被执行,什么时候被执行,在return前还是后?

答:会执行,在方法返回调用者前执行。在finally中改变返回值的做法是不好的,因为如果存在finally代码块,try中的return语句不会立马返回调用者,而是记录下返回值待finally代码块执行完毕之后再向调用者返回其值,然后如果在finally中修改了返回值,就会返回修改后的值
纠正:这里错了,感谢评论区纠正,try在return时候已经将返回结果入栈,在finally中会修改变量值,但是return返回时的出栈还是try中的修改前已经计算完成的操作数的结果

6、事务的ACID是指

1、原子性(atomicity)

事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用

2、一致性(consistency)

一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中

3、隔离性(isolation)

可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏

4、持久性(durability)

事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中

7、用Java写一个冒泡排序
for(int i=0;i<arr.length-1;i++){//外层循环控制排序趟数
      for(int j=0;j<arr.length-1-i;j++){//内层循环控制每一趟排序多少次
        if(arr[j]>arr[j+1]){
          int temp=arr[j];
          arr[j]=arr[j+1];
          arr[j+1]=temp;
        }
      }
    } 
8、用Java写一个二分查找。

非递归实现:

public static int biSearch(int []array,int a){
        int lo=0;
        int hi=array.length-1;
        int mid;
        while(lo<=hi){
            mid=(lo+hi)/2;
            if(array[mid]==a){
                return mid+1;
            }else if(array[mid]<a){
                lo=mid+1;
            }else{
                hi=mid-1;
            }
        }
        return -1;
    }

递归实现:

public static int sort(int []array,int a,int lo,int hi){
        if(lo<=hi){
            int mid=(lo+hi)/2;
            if(a==array[mid]){
                return mid+1;
            }
            else if(a>array[mid]){
                return sort(array,a,mid+1,hi);
            }else{
                return sort(array,a,lo,mid-1);
            }
        }
        return -1;
    }
9、什么是多态

所谓的“多态”,简单的理解就是对象在不同情况下的不同表现,具体体现在定义和功能两个方面,简单的总结一下,多态可以用“三个定义和两个方法”来总结。三个定义分别是父类定义子类构建、接口定义实现类构建和抽象类定义实体类构建,而两个方法分别是方法重载和方法重写。

10、什么是继承

继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。

继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

11、重写(Override)和重载(Overload)

**重写 ** 是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。

重载 (overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。

12、接口的意义用三个词概括

规范,扩展,回调

13、==和equals面试题
String str1 = "abc";
String str2 = new String("abc");
String str3 = "abc";
String str4 =  "xxx";
String str5 = "abc" + "xxx";
String str6 = str3 + str4;

System.out.println("str1 == str2:" + (str1 == str2));//false,比较地址
System.out.println("str1.equals(str2):" + (str1.equals(str2)));//true,比较值
System.out.println("str5 == str6:" + (str5 == str6));//false,地址不同
System.out.println("str5.equals(str6):" + (str5.equals(str6)));//true,值相同
System.out.println("str1 == str2.intern():" + (str1 == str2.intern()));//true
14、数组(Array)和列表(ArrayList)有什么区别?什么时候应该使用Array而不是ArrayList?

Array可以包含基本类型,ArrayList只能包含对象类型

Array大小是固定的,ArrayList的大小是动态变化的。

ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等

对于基本类型数据,集合使用自动装箱来减少编码工作量,但是,当处固定大小的基本数据类型的时候,这种方式相对比较慢。

15、java有几种引用级别,有什么区别特点,谈谈你的理解。

强引用
强引用是平常中使用最多的引用,强引用在程序内存不足(OOM)的时候也不会被回收,使用方式:

String str = new String("str");//直接new

软引用
软引用在程序内存不足时,会被回收,使用方式:

SoftReference<String> wrf = new SoftReference<String>(new String("str"));

可用场景: 创建缓存的时候,创建的对象放进缓存中,当内存不足时,JVM就会回收早先创建的对象。
弱引用
弱引用就是只要JVM垃圾回收器发现了它,就会将之回收,使用方式:

WeakReference<String>wrf=newWeakReference<String>(str);

可用场景:Java源码中的java.util.WeakHashMap中的key就是使用弱引用,我的理解就是,一旦我不需要某个引用,JVM会自动帮我处理它,这样我就不需要做其它操作。
虚引用
虚引用的回收机制跟弱引用差不多,但是它被回收之前,会被放入ReferenceQueue中。注意哦,其它引用是被JVM回收后才被传入
ReferenceQueue中的。由于这个机制,所以虚引用大多被用于引用销毁前的处理工作。还有就是,虚引用创建的时候,必须带有ReferenceQueue,使用例子:

PhantomReference<String>prf=newPhantomReference<String>(new
String("str"),newReferenceQueue<>());

可用场景: 对象销毁前的一些操作,比如说资源释放等。Object.finalize() 虽然也可以做这类动作,但是这个方式即不安全又低效

16、什么是浅拷贝,和深拷贝的区别?

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。

深拷贝被复制对象的所有变量都含有与原来的对象相同的值.而那些引用其他对象的变量将指向被复制过的新对象.而不再是原有的那些被引用的对象.换言之.深拷贝把要复制的对象所引用的对象都复制了一遍.实现方法类继承Cloneable,重写clone().

17、a=a+b与a+=b有什么区别吗?

+= 操作符会进行隐式自动类型转换,此处a+=b隐式的将加操作的结果类型强制转换为持有结果的类型, 而a=a+b则不会自动进行类型转换.

如:short s1= 1;
s1 += 1;//正确
s2 = s1 + 1;//错误,右边是int类型
18、final、finalize、finally
  1. final为关键字;

    final为用于标识常量的关键字,final标识的关键字存储在常量池中

  2. finalize()为方法;

    finalize()方法在Object中进行了定义,用于在对象“消失”时,由JVM进行调用用于对对象进行垃圾回收,类似于C++中的析构函数;用户自定义时,用于释放对象占用的资源(比如进行I/0操作);

  3. finally为区块标志,用于try语句中;
    finally{}用于标识代码块,与try{}进行配合,不论try中的代码执行完或没有执行完(这里指有异常),该代码块之中的程序必定会进行;

19、JDBC操作的步骤

加载数据库驱动类
打开数据库连接
执行sql语句
处理返回结果
关闭资源

20、接口是否可继承(extends)接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concreteclass)?

接 口 可 以 继 承 接 口 , 而 且 支 持 多 重 继 承 。 抽 象 类 可 以 实 现 (implements)接 口 , 抽象 类 可 继 承 具 体 类 也 可 以 继 承 抽 象 类 。

21、了解java的反射吗,说说看

java一般通过new创建对象,也可以通过java的放射创建对象,有4种实现方式

1)Class.forName(“类的路径”);
2)类名.class
3)对象名.getClass()
4)基本类型的包装类,可以调用包装类的Type属性来获得该包装类的Class对象

jdbc加载mysql驱动的时候就是使用反射机制来实现Class.forName(‘com.mysql.jdbc.Driver.class’);

类加载后,可以实现java反射的类有以下4种

1)Class:表示正在运行的Java应用程序中的类和接口
注意: 所有获取对象的信息都需要Class类来实现。
2)Field:提供有关类和接口的属性信息,以及对它的动态访问权限。
3)Constructor:提供关于类的单个构造方法的信息以及它的访问权限
4)Method:提供类或接口中某个方法的信息

可以利用Class 对象的 newInstance()或者调用 Constructor 对象的 newInstance()来进行反射动态创建对象实例

//获取 Person 类的 Class 对象
Class clazz=Class.forName("reflection.Person");
//使用.newInstane 方法创建对象
Person p=(Person) clazz.newInstance();
//获取构造方法并创建对象
Constructor c=clazz.getDeclaredConstructor(String.class,String.class,int.class);
//创建对象并设置属性13/04/2018
Person p1=(Person) c.newInstance("李四","男",20);

放射虽然能够在程序运行时候动态获取类的实例,但是比new的效率低,先找查找类资源,使用类加载器创建,过程比较繁琐。

22、Class.forName()和ClassLoader.loadClass的区别?
Class.forName(className)方法,内部实际调用的方法是  Class.forName(className,true,classloader);2boolean参数表示类是否需要初始化,  Class.forName(className)默认是需要初始化。一旦初始化,就会触发目标对象的 static块代码执行,static参数也也会被再次初始化。

    
ClassLoader.loadClass(className)方法,内部实际调用的方法是  ClassLoader.loadClass(className,false);2boolean参数,表示目标对象是否进行链接,false表示不进行链接,由上面介绍可以,不进行链接意味着不进行包括初始化等一些列步骤,那么静态块和静态对象就不会得到执行
23、error和exception的区别?

两者继承自Throwable。

exception异常分为检查移仓和非检查异常。非检查异常(也称运行时异常–RuntimeException和其子类),常见有NPE异常,数组越界异常等,对于运行时异常,java编译器不要求必须进行异常捕获处理或者抛出声明;另一种是检查异常,编译异常(也称为非运行时异常),java编译器强制程序员必须进行捕获处理,常见有IOException等.(后者辨别方法就是编译器提示要try…catch,或者throws exception)

Error是程序无法处理的错误,大多数错误与代码编写者执行操作无关,而表示代码运行时jvm出现的问题,如oom等。

二、jvm

1、类的实例化顺序

首先是父类的静态变量和静态代码块(看两者的书写顺序);
第二执行子类的静态变量和静态代码块(看两者的书写顺序);
第三执行父类的成员变量赋值
第四执行父类的构造代码块
第五执行父类的构造方法
第六执行子类的构造代码块
第七执行子类的构造方法
总结,也就是说虽然客户端代码是new 的构造方法,但是构造方法确实是在整个实例创建中的最后一个调用。切记切记!!!
先是父类,再是子类;
先是类静态变量和静态代码块,再是对象的成员变量和构造代码块–>构造方法。
记住,构造方法最后调用!!!!成员变量优先构造代码块优先构造方法!!

2、内存泄漏和内存溢出的区别

内存泄漏:分配出去的内存无法回收(可达却无用的对象无法被GC回收)
内存溢出:程序要求的内存超出了系统能分配的范围(如栈满进栈,栈空出栈)

3、了解jvm吗
  • Java源文件经编译成字节码程序,通过JVM将每一条指令翻译成不同平台机器码,通过特定平台运行。

jvm体系结构:

类装载器ClassLoader:用来装载.class文件

执行引擎:执行字节码,或者执行本地方法

运行时数据区:方法区、堆、Java栈、程序计数器、本地方法栈

4、简述类被加载到虚拟机内存到卸载出内存的过程
  • 加载(重点):加载阶段是“类加载机制”中的一个阶段,这个阶段通常也被称作“装载”,根据一个类的全限定名来读取此类的二进制字节流到JVM中,然后转换为一个与目标类对应的java.lang.Class对象实例
  • 验证:验证是链接阶段的第一步,这一步主要的目的是确保class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全。
    验证阶段主要包括四个检验过程:文件格式验证、元数据验证、字节码验证和符号引用验证
  • 准备:准备阶段是正式为类变量分配内存并设置类变量初始值(零值)的阶段,这些内存都将在方法区中进行分配。这个阶段中有两个容易产生混淆的知识点,首先是这时候进行内存分配的仅包括类变量(static 修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在java堆中。
  • 解析:解析阶段是虚拟机常量池内的符号引用替换为直接引用的过程。
  • 初始化:类加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量(如前面只初始化了默认值的static变量将会在这个阶段赋值,成员变量也将被初始化)。
  • 使用
  • 卸载
5、你了解类加载器吗?知道什么是双亲委派加载机制吗?

类加载分类

1、启动类加载器 bootstrap classloader :加载jre/lib/rt.jar

2、扩展类加载器 extension classloader :加载jre/lib/ext/*.jar

3、应用程序类加载器 application classloader:加载classpath上指定的类库

双亲委派机制

双亲委派机制是指当一个类加载器收到一个类加载请求时,该类加载器首先会把请求委派给父类加载器。每个类加载器都是如此,只有在父类加载器在自己的搜索范围内找不到指定类时,子类加载器才会尝试自己去加载。

双亲委派模型工作工程:

1.当Application ClassLoader 收到一个类加载请求时,他首先不会自己去尝试加载这个类,而是将这个请求委派给父类加载器Extension ClassLoader去完成。

2.当Extension ClassLoader收到一个类加载请求时,他首先也不会自己去尝试加载这个类,而是将请求委派给父类加载器Bootstrap ClassLoader去完成。

3.如果Bootstrap ClassLoader加载失败(在<JAVA_HOME>\lib中未找到所需类),就会让Extension ClassLoader尝试加载。

4.如果Extension ClassLoader也加载失败,就会使用Application ClassLoader加载。

5.如果Application ClassLoader也加载失败,就会使用自定义加载器去尝试加载。

6.如果均加载失败,就会抛出ClassNotFoundException异常。

三、javaEE

1、Servlet的运行过程?

Web容器加载Servlet并将其实例化后,Servlet生命周期开始,容器运行其init()方法进行Servlet的初始化;请求到达时调用Servlet的service()方法,service()方法会根据需要调用与请求对应的doGet或doPost等方法;当服务器关闭或项目被卸载时服务器会将Servlet实例销毁,此时会调用Servlet的destroy()方法。

2、转发(forward)和重定向(redirect)的区别?

答:forward是容器中控制权的转向,是服务器请求资源,服务器直接访问目标地址的URL,把那个URL 的响应内容读取过来,然后把这些内容再发给浏览器,浏览器根本不知道服务器发送的内容是从哪儿来的,所以它的地址栏中还是原来的地址。redirect就是服务器端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,因此从浏览器的地址栏中可以看到跳转后的链接地址,很明显redirect无法访问到服务器保护起来资源,但是可以从一个网站redirect到其他网站。forward更加高效,所以在满足需要时尽量使用forward(通过调用RequestDispatcher对象的forward()方法,该对象可以通过ServletRequest对象的getRequestDispatcher()方法获得),并且这样也有助于隐藏实际的链接;在有些情况下,比如需要访问一个其它服务器上的资源,则必须使用重定向(通过HttpServletResponse对象调用其sendRedirect()方法实现)。

3、JSP有哪些内置对象?作用分别是什么?

答:JSP有9个内置对象:
- request:封装客户端的请求,其中包含来自GET或POST请求的参数;
- response:封装服务器对客户端的响应;
- pageContext:通过该对象可以获取其他对象;
- session:封装用户会话的对象;
- application:封装服务器运行环境的对象;
- out:输出服务器响应的输出流对象;
- config:Web应用的配置对象;
- page:JSP页面本身(相当于Java程序中的this);
- exception:封装页面抛出异常的对象。

4、Tomcat容器是如何创建servlet类实例?用到了什么原理?

当容器启动时,会读取在webapps目录下所有的web应用中的web.xml文件,然后对xml文件进行解析,
并读取servlet注册信息。然后,将每个应用中注册的servlet类都进行加载,并通过反射的方式实例化。
(有时候也是在第一次请求时实例化)在servlet注册时加上如果为正数,则在一开始就实例化,
如果不写或为负数,则第一次请求实例化。

四、框架

1、MyBatis

1、#{}和${}的区别是什么

#{}是预编译处理,${}是字符串替换。
Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理${}时,就是把${}替换成变量的值。
使用#{}可以有效的防止SQL注入,提高系统安全性。

2、一对多、多对一、多对多

Mybatis的Mapper编写一对多,就是在resultMap标签中配置标签,用来存储查询到的文章列表。

Mybatis的Mapper编写多对一,就是在resultMap标签中配置标签关联所属的用户实体。

2、Shiro

1、Shiro 架构 3 个核心组件:1

Subject: 正与系统进行交互的人, 或某一个第三方服务. 所有 Subject 实例都被绑定到(且这是必须的)一个SecurityManager 上。
SecurityManager: Shiro 架构的心脏, 用来协调内部各安全组件, 管理内部组件实例, 并通过它来提供安全管理的各种服务,当 Shiro 与一个 Subject 进行交互时, 实质上是幕后的 SecurityManager 处理所有繁重的 Subject 安全操作。
Realms: 本质上是一个特定安全的 DAO. 当配置 Shiro 时, 必须指定至少一个 Realm 用来进行身份验证和/或授权,Shiro 提供了多种可用的,Realms 来获取安全相关的数据. 如关系数据库(JDBC), INI 及属性文件等,也可以定义自己 Realm 实现来代表自定义的数据源。

2、Shiro认证过程

①. 应用程序代码调用 Subject.login 方法,传递创建好的包含终端用户的 Principals(身份)和 Credentials(凭证)的 AuthenticationToken 实例
②. Subject 实例: 通常为 DelegatingSubject(或子类)委托应用程序的 SecurityManager 通过调用securityManager.login(token) 开始真正的验证。
③. SubjectManager 接收 token,调用内部的 Authenticator 实例调用 authenticator.authenticate(token).Authenticator 通常是一个 ModularRealmAuthenticator 实例, 支持在身份验证中协调一个或多个Realm 实例
④. 如果应用程序中配置了一个以上的 Realm, ModularRealmAuthenticator 实例将利用配置好的AuthenticationStrategy 来启动 Multi-Realm 认证尝试. 在Realms 被身份验证调用之前, 期间和以后,AuthenticationStrategy 被调用使其能够对每个Realm 的结果作出反应.
⑤. 每个配置的 Realm 用来帮助看它是否支持提交的 AuthenticationToken. 如果支持, 那么支持 Realm 的 getAuthenticationInfo 方法将会伴随着提交的 token 被调用. getAuthenticationInfo 方法有效地代表一个特定 Realm 的单一的身份验证尝试。

3、SpringMVC

1、SpringMVC流程

(1)用户发送请求至前端控制器DispatcherServlet;
(2) DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;
(3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;
(4)DispatcherServlet 调用 HandlerAdapter处理器适配器;
(5)HandlerAdapter 经过适配调用 具体处理器(Handler,也叫后端控制器);
(6)Handler执行完成返回ModelAndView;
(7)HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
(8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;
(9)ViewResolver解析后返回具体View;
(10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
(11)DispatcherServlet响应用户。最新2021-2022年java实习校招秋招春招后端面试题(上)(持续更新)

2、SpringMvc怎么和AJAX相互调用的?

通过Jackson框架就可以把Java里面的对象直接转化成Js可以识别的Json对象。具体步骤如下 :

(1)加入Jackson.jar

(2)在配置文件中配置json的映射

(3)在接受Ajax方法里面可以直接返回Object,List等,但方法前面要加上@ResponseBody注解。

3、如何实现HandlerExceptionResolver接口的自定义异常处理器

通过使用@ControllerAdvice来进行统一异常处理,@ExceptionHandler(value = Exception.class)来指定捕获的异常 ,这个异常的处理,是全局的,所有类似的异常,都会跑到这个地方处理。

4、Spring

每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效。

5.global-session:全局session作用域,仅仅在基于Portlet的Web应用中才有意义,Spring5中已经没有了。Portlet是能够生成语义代码(例如HTML)片段的小型Java Web插件。它们基于Portlet容器,可以像Servlet一样处理HTTP请求。但是与Servlet不同,每个Portlet都有不同的会话。

2、将一个类声明为Spring的bean的注解有哪些?

我们一般使用@Autowired注解去自动装配bean。而想要把一个类标识为可以用@Autowired注解自动装配的bean,可以采用以下的注解实现:

1.@Component注解。通用的注解,可标注任意类为Spring组件。如果一个Bean不知道属于哪一个层,可以使用@Component注解标注。

2.@Repository注解。对应持久层,即Dao层,主要用于数据库相关操作。

3.@Service注解。对应服务层,即Service层,主要涉及一些复杂的逻辑,需要用到Dao层(注入)。

4.@Controller注解。对应Spring MVC的控制层,即Controller层,主要用于接受用户请求并调用Service层的方法返回数据给前端页面。

3、你知道事务有哪些属性?谈谈你对事务的理解?

事务属性的种类: 传播行为、隔离级别、只读、事务超时和回滚规则

a)事务传播行为(propagation behavior)

指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。 有7种
例如:methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。

@Transactional(propagation=Propagation.REQUIRED)

b) 隔离级别

在操作数据时可能带来 3 个副作用,分别是脏读、不可重复读、幻读。为了避免这 3 中副作用的发生,在标准的 SQL 语句中定义了 4 种隔离级别,分别是未提交读、已提交读、可重复读、可序列化。而在 spring 事务中提供了 5 种隔离级别来对应在 SQL 中定义的 4 种隔离级别

@Transactional(isolation = Isolation.READ_UNCOMMITTED)

c) 只读

如果在一个事务中所有关于数据库的操作都是只读的,也就是说,这些操作只读取数据库中的数据,而并不更新数据,那么应将事务设为只读模式( READ_ONLY_MARKER ) , 这样更有利于数据库进行优化 。因为只读的优化措施是事务启动后由数据库实施的,因此,只有将那些具有可能启动新事务的传播行为 (PROPAGATION_NESTED 、 PROPAGATION_REQUIRED 、 PROPAGATION_REQUIRED_NEW) 的方法的事务标记成只读才有意义。如果使用 Hibernate 作为持久化机制,那么将事务标记为只读后,会将 Hibernate 的 flush 模式设置为 FULSH_NEVER, 以告诉 Hibernate 避免和数据库之间进行不必要的同步,并将所有更新延迟到事务结束。

@Transactional(readOnly=true)

d) 事务超时

如果一个事务长时间运行,这时为了尽量避免浪费系统资源,应为这个事务设置一个有效时间,使其等待数秒后自动回滚。与设置“只读”属性一样,事务有效属性也需要给那些具有可能启动新事物的传播行为的方法的事务标记成只读才有意义。

@Transactional(timeout=30)

e)回滚

在默认设置下,事务只在出现运行时异常(runtime exception)时回滚,而在出现受检查异常(checked exception)时不回滚。不过,可以声明在出现特定受检查异常时像运行时异常一样回滚。同样,也可以声明一个事务在出现特定的异常时不回滚,即使特定的异常是运行时异常。

指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
4、spring中@Autowrite注解和@Resource的区别

@Resource的作用相当于@Autowired,@Autowired按byType自动注入,而@Resource默认按 byName自动注入。不过,可以声明在出现特定受检查异常时像运行时异常一样回滚。同样,也可以声明一个事务在出现特定的异常时不回滚,即使特定的异常是运行时异常。

5、自动装配有什么局限?

覆盖的可能性 - 您始终可以使用 和 设置指定依赖项,这将覆盖自动装配。基本元数据类型 - 简单属性(如原数据类型,字符串和类)无法自动装配。令人困惑的性质 - 总是喜欢使用明确的装配,因为自动装配不太精确。

6、@Component, @Controller, @Repository,@Service 有何区别?

@Component :这将 java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。spring 的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。
@Controller :这将一个类标记为 Spring Web MVC 控制器。标有它的Bean 会自动导入到 IoC 容器中。
@Service :此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用@Service 而不是
@Component:因为它以更好的方式指定了意图。
@Repository :这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IoC 容器,并使未经检查的异常有资格转换为 Spring DataAccessException。

五、数据库

数据库8个优化:

1、选取最适合的字段属性

创建表字段时,宽度设得尽可能小,例如邮编字段,设置CHAR(6),对于文本字段,例如汉族少数民族,性别男女可以用ENUM类型,数据库会当作数据来处理,速度快。字段设置not null,查询时数据库不用去比较。

2、使用join来代替子查询

因为MySQL不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作

3、使用联合(UNION)来代替手动创建的临时表

在客户端的查询会话结束的时候,临时表会被自动删除,从而保证数据库整齐、高效。使用union来创建查询的时候,我们只需要用UNION作为关键字把多个select语句连接起来就可以了,要注意的是所有select语句中的字段数目要想同。

4、事务

防止需要执行一组sql时出现一些不可避免的意外而导致的表中的数据不一致或不完整,破坏预想的业务逻辑。

5、锁定表

尽管事务是维护数据库完整性的一个非常好的方法,但却因为它的独占性,有时会影响数据库的性能,尤其是在很大的应用系统中。由于在事务执行的过程中,数据库将会被锁定,因此其它的用户请求只能暂时等待直到该事务结束。如果一个数据库系统只有少数几个用户来使用,事务造成的影响不会成为一个太大的问题;但假设有成千上万的用户同时访问一个数据库系统,例如访问一个电子商务网站,就会产生比较严重的响应延迟。

其实,有些情况下我们可以通过锁定表的方法来获得更好的性能。下面的例子就用锁定表的方法来完成前面一个例子中事务的功能。

LOCK TABLE inventory WRITESELECT Quantity FROM inventory WHERE Item='book';
...
UPDATE inventory SET Quantity=11 WHERE Item='book';
UNLOCKTALES

这里,我们用一个select语句取出初始数据,通过一些计算,用update语句将新值更新到表中。包含有WRITE关键字的LOCKTABLE语句可以保证在UNLOCKTABLES命令被执行之前,不会有其它的访问来对inventory进行插入、更新或者删除的操作。

6、使用外键

保证每一条新增记录都指向某个表中已存在的keyid,使得没有不合法的记录被更新或插入。

7、使用索引

索引应建立在那些将用于join,where判断和order by排序的字段上。尽量不要对数据库中某个含有大量重复的值的字段建立索引。对于一个enum类型的字段来说,出现大量重复值是很有可能的情况。

8、优化查询语句

函数

数据库外键约束:

1、当取值为No Action或者Restrict时,则当在父表(即外键的来源表)中删除对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除。当取值为No Action或者Restrict时,则当在父表(即外键的来源表)中更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许更新。

2、当取值为Cascade时,则当在父表(即外键的来源表)中删除对应记录时,首先检查该记录是否有对应外键,如果有则也删除外键在子表(即包含外键的表)中的记录。当取值为Cascade时,则当在父表(即外键的来源表)中更新对应记录时,首先检查该记录是否有对应外键,如果有则也更新外键在子表(即包含外键的表)中的记录。

3、当取值为Set Null时,则当在父表(即外键的来源表)中删除对应记录时,首先检查该记录是否有对应外键,如果有则设置子表中该外键值为null(不过这就要求该外键允许取null)。当取值为Set Null时,则当在父表(即外键的来源表)中更新对应记录时,首先检查该记录是否有对应外键,如果有则设置子表中该外键值为null(不过这就要求该外键允许取null)。

9、myisam和innodb不同点

Mysql5.5以后默认使用InnoDB为搜索引擎

MyISAM是表锁,不支持事务和主外键

InnoDB默认可以创建16个索引

  • InnoDB支持事务,MyIsam不支持事务,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放到begin 和 commit之间,组成一个事务;
  • InnoDB支持外键,而MyIsam不支持,对一个包含外键的InnoDB表转成MyIsam表会失败
  • InnoDB是聚集索引,数据文件和索引绑定在一块,必须要有主键,通过主键索引效率很高,但是辅助索引需要两次查询,先查询到主键,然后在通过主键查询到数据。因此主键不应该过大。主键过大的时候,其它索引也会很大。而MyIsam是非聚集索引,数据和文件是分离的,索引保存的是数据文件的指针,主键索引和辅助索引是独立的。
  • InnoDB不支持全文检索,而MyIsam支持全文检索,查询效率上MyIsam要高

六、数据结构与算法

1、你知道哪些排序算法?8种
  1. 插入排序:直接插入排序、希尔排序

  2. 选择排序:简单选择排序、堆排序

  3. 交换排序:冒泡排序、快速排序

  4. 归并排序

  5. 基数排序

最新2021-2022年java实习校招秋招春招后端面试题(上)(持续更新)

2、HashMap在1.7和1.8以上的区别

在jdk1.8之前,HashMap 的实现是 数组+链表。但是即使哈希函数取得再好,也很难做到所有元素均匀分布。

当 HashMap 中有大量的元素都存放到同一个桶中时,这个桶下有一条长长的链表,这个时候 HashMap 就相当于一个单链表,假如单链表有 n 个元素,遍历的时间复杂度就是 O(n),完全失去了它的优势。

所以,在jdk1.8版本后,Java对HashMap做了改进,在链表长度大于8的时候,将后面的数据存在红黑树中(查找时间复杂度为 O(logn)),以加快检索速度。

3、HashMap为什么长度为8才转化为红黑树

当链表长度为8时,使用以红黑树代替链表,红黑树的结点是链表长度的两倍,当比较短的时候,使用红黑树的效率其实并不高,根据泊松分布公式的统计结果,在结点数达到8时,适合使用红黑树

补充:红黑树转为链表的阈值(长度<6)| 插入结点默认为红结点

4、红黑树的见解
  1. 每个节点非红即黑
  2. 根节点总是黑色的
  3. 如果节点是红色的,则它的子节点必须是黑色的(反之不一定)
  4. 每个叶子节点都是黑色的空节点(NIL节点)
  5. 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)

七、工具

git

资料地址

linux

一般自己下载的软件放在/opt,

一般自己的文件放在/home/自己用户名下,

一般用户应用程序放/usr

用完就丢的文件放/tmp,日志文件放/var

sync 将数据从内存同步到硬盘

shutdown -h 10 十分钟后关机

rm -rf / 删库跑路,危险命令

touch/mkdir创建文件/目录

chmod 777 filename/chmod rwxrxrwx filename 修改权限(例子为所有用户可读写执行)

cat filename/ tac filename /nl filename正向阅读/方向阅读/带行号正向阅读

more filename分页(空格下一页,回车下一行,不可往前翻页)

less filename分页(上下键可翻页,退出q)

head -n xx filename /tail -n xx filename显示头/尾xx行的内容

ln filename1 filename2硬链接:允许一个文件拥有多个路径,防止误删

ln -s filename1 filename3软链接:类似win的快捷方式,删除源文件则快捷方式访问不了

useradd -m 用户创建用户

passwd 用户修改密码名(在root账号下修改,若普通用户则直接输入passwd)

userdel -r 用户 删除用户

su 用户名切换用户

ps -aux 查询所有进程

ps -aux|grep java查找文件中符合条件的字符串grep,管道符|,命令表示过滤后的进程信息

pstree -pu进程树

kill -9 进程号杀死进程

八、计算机网络

1、网络分层

最新2021-2022年java实习校招秋招春招后端面试题(上)(持续更新)
最新2021-2022年java实习校招秋招春招后端面试题(上)(持续更新)

2、tcp三次握手、4次挥手(画图)

确认序号标志ACK:当ACK=1时,确认字段才有效。当ACK=0时,确认号无效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置1。
  同步序号SYN:在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK=1。故SYN置为1,就表示这是一个连接请求和连接接收报文。
  序列号seq:当发送一个数据时,数据是被拆成多个数据包来发送,序列号就是对每个数据包进行编号,这样接受方才能对数据包进行再次拼接。
  下一个数据包的编号ack:这也就是为什么第二请求时,ack是seq+1

FIN:finish标志,用于释放连接

最新2021-2022年java实习校招秋招春招后端面试题(上)(持续更新)

整个流程为:

  1. 建立连接时,,客户端发送SYN包(seq=x),然后进入SYN_SEND状态,等待服务器确认(确认客户端具有发送功能)
  2. 服务器收到SYN包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(seq=y),即SYN+ACK包,进入SYN_RECV状态,这个状态被称为半连接状态(确认服务端具有接收和发送功能)
  3. 客户端再进行一次确认,收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态(确认客户端具有接收功能)

最新2021-2022年java实习校招秋招春招后端面试题(上)(持续更新)

整个过程为:

  1. 客户端发送一个报文给服务端(没有数据),其中FIN设置为1,用来关闭Client到Server的数据传送,客户端进入FIN_WAIT_1状态(客户端Client发送FIN,用来关闭Client到Server的数据传送)
  2. 服务端收到来自客户端的FIN,发送一个ACK给客户端,确认序号为收到序号+1 ,(与SYN相同,一个FIN占用一个序号),服务端进入CLOSE_WAIT状态(此时TCP链接处于半关闭状态,即客户端已经没有要发送的数据了,但服务端若发送数据,则客户端仍要接收。)
  3. 服务端发送一个FIN给客户端,用来关闭服务端到客户端的数据传送,服务端进入LAST_ACK状态(服务端Server发送一个FIN,用来关闭Server到Client的数据传送)
  4. 客户端收到FIN后,进入TIME_WAIT状态,接着发送一个ACK给服务端,确认序号为收到序号+1,最后客户端和服务端都进入CLOSED状态
3、地址栏中输入URL会发生什么?

在这整个过程中,大致可以分为以下几个过程

  • DNS域名解析:浏览器本身是一个客户端,当你输入URL的时候,首先浏览器会去请求DNS服务器(从近及远,浏览器缓存、操作系统缓存、路由器缓存、IPS服务器缓存、根域名服务器缓存【迭代查询】),通过DNS获取相应的域名对应的IP
  • TCP连接:然后通过IP地址找到IP对应的服务器后,要求建立TCP连接
  • HTTP请求:浏览器发送完HTTP Request(请求)包后,服务器接收到请求包之后才开始处理请求包
  • 服务器处理请求返回HTTP响应:在服务器收到请求之后,服务器调用自身服务,返回HTTP Response(响应)包
  • 浏览器页面渲染:客户端收到来自服务器的响应后开始渲染这个Response包里的主体(body)
  • 关闭连接:等收到全部的内容随后断开与该服务器之间的TCP连接
4、Cookie和Session的区别

Cookie

  • 是由服务器发送给客户端的特殊信息,以文本的形式存放在客户端
  • 客户端再次请求时,会把Cookie回发
  • 服务器接收到后, 会解析Cookie生成与客户端相对应的内容

Session

  • 服务器端的机制,在服务器上保存的信息
  • 解析客户端请求并操作session id,按需保存状态信息
5、请简单解释下ARP协议和ARP攻击

ARP(Address Resolution Protocol)地址解析协议,目的是实现IP地址到MAC地址的转换。ARP攻击的第一步就是ARP欺骗。通过监听广播P1与P2广播通信,P3(攻击者)接收不属于发送给自己的广播数据,然后连续不断的做出回应,让P1(广播发起者)误认为攻击对象是P2(目标对象)。于是建立P1和P3的ARP缓存条目,以后当PC1给PC2发送信息的时候,PC1依据OSI模型从上至下在网络层给数据封装目的IP为IP2的包头,在链路层通过查询ARP缓存表封装目的MAC为MAC3的数据帧,送至交换机,根据查询CAM表,发现MAC3对应的接口为Port3,就这样把信息交付到了PC3,完成了一次ARP攻击。

6、简单说一下路由器和交换机的区别

路由器在网络层,路由器根据IP地址寻址,路由器可以处理TCP/IP协议,交换机不可以。
交换机在中继层,交换机根据MAC地址寻址。

九、操作系统

1.进程和线程以及他们的区别
  • 进程是对运行时程序的封装,是系统进行资源调度和分配的基本单位,实现了操作系统的并发
  • 线程是进程的子任务,是CPU调度和分派的基本单位,用于保证程序的实时性,实现进程内部的并发
  • 一个程序至少一个进程,一个进程至少一个线程,线程依赖于进程而存在
  • 进程拥有独立的内存单元,而线程共享进程的内存单元
2、死锁预防:打破死锁四个必要条件中的一个就可以了
  • 打破互斥条件:允许进程同时访问某些资源,但是某些资源因为自身属性问题,不能被同时访问,所以这种方法实用性不大
  • 打破占用并等待条件:可以实行资源先分配策略(进程在运行前一次性向系统申请它需要的全部资源,若所需全部资源得不到满足,则不分配任何资源,此进程暂不运行,只有当系统能满足当前线程所需全部资源时,才一次性将所需资源全部分配给该进程)或者只允许进程在没有占用资源时间才允许申请资源(一个进程可以申请一些资源并使用他们,但是在当前进程申请更多资源之前,它必须全部释放当前所占用的资源,但是这种策略也存在一些缺点:很多情况下,无法预知一个进程所需要的全部资源,因为进程是动态运行的,不可预知,同时会降低资源的利用效率,降低进程的并发性
  • 打破非抢占条件:允许某个进程在资源得不到满足时强行从其他进程那里获取资源,实现起来困难,会降低系统性能
  • 打破循环等待条件:实行资源有序分配策略,对所有资源排序编号,所有进程对资源的请求必须严格按照资源序号递增的顺序提出,即只有占用了小号资源才能占用大号资源,这样就不产生回环路,预防死锁
  • 3、如何根据逻辑地址查到物理地址

已知某个分页系统,页面大小为1K(即1024字节),某一个作业有4个页面,分别装入到主存的第3、4、6、8块中,求逻辑地址2100对应的物理地址。

解:
第一步:求该逻辑地址的页号 = 2100/1024=2 (整除)
第二步:求它的页内偏移量 = 2100 % 1024 =52 (取余)
第三步:根据题目产生页表:
页号 页框号/帧号
0 3
1 4
2 6
3 8
第四步:根据逻辑地址的页号查出物理地址的页框号/帧号:
如上图,逻辑地址的第2页对应物理地址的第6块。
第五步:求出物理地址 = 6*1024 + 52 = 6196

十、中间件

一、Redis面试题

1、使用 Redis 有哪些好处?

1、速度快,因为数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是 O(1)
2、支持丰富数据类型,支持 string,list,set,Zset,hash 等
3、支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
4、丰富的特性:可用于缓存,消息,按 key 设置过期时间,过期后将会自动删除

2、Redis有什么持久化的机制吗?

RDB:是Redis默认的持久化方式。按照一定的时间周期策略把内存的数据以快照的形式保存到硬盘的二进制文件。即Snapshot快照存储,对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。( 快照可以是其所表示的数据的一个副本,也可以是数据的一个复制品。)
AOF:Redis会将每一个收到的写命令都通过Write函数追加到文件最后,类似于MySQL的binlog。当Redis重启是会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。

3、能谈谈什么是缓存雪崩么?

我们可以简单的理解为:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。
解决办法:
大多数系统设计者考虑用加锁( 最多的解决方案)或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就是将缓存失效时间分散开。

4、知道什么是缓存穿透吗?

缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。
解决办法;
最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。

5TB的硬盘上放满了数据,请写一个算法将这些数据进行排重。如果这些数据是一些32bit大小的数据该如何解?如果是64bit的呢?
对于空间的利用到达了一种极致,那就是Bitmap和布隆过滤器(Bloom Filter)。
Bitmap: 典型的就是哈希表
缺点是,Bitmap对于每个元素只能记录1bit信息,如果还想完成额外的功能,恐怕只能靠牺牲更多的空间、时间来完成了

5、了解布隆过滤器吗

就是引入了k(k>1)个相互独立的哈希函数,保证在给定的空间、误判率下,完成元素判重的过程。
它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
Bloom-Filter算法的核心思想就是利用多个不同的Hash函数来解决“冲突”。
Hash存在一个冲突(碰撞)的问题,用同一个Hash得到的两个URL的值有可能相同。为了减少冲突,我们可以多引入几个Hash,如果通过其中的一个Hash值我们得出某元素不在集合中,那么该元素肯定不在集合中。只有在所有的Hash函数告诉我们该元素在集合中时,才能确定该元素存在于集合中。这便是Bloom-Filter的基本思想。
Bloom-Filter一般用于在大数据量的集合中判定某元素是否存在。

6、如何解决秒杀开始场景的高并发问题?

缓存预热
缓存预热这个应该是一个比较常见的概念,相信很多小伙伴都应该可以很容易的理解,缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
解决思路:
1、直接写个缓存刷新页面,上线时手工操作下;
2、数据量不大,可以在项目启动的时候自动进行加载;
3、定时刷新缓存

7、缓存更新

除了缓存服务器自带的缓存失效策略之外(Redis默认的有6种策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:
(1)定时去清理过期的缓存;
(2)懒性删除:当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。
两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡

8、什么是缓存降级

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
以参考日志级别设置预案:
(1)一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
(2)警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
(3)错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降
级或者人工降级;
(4)严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户

9、热点数据和冷数据是什么

热点数据,缓存才有价值
对于冷数据而言,大部分数据可能还没有再次访问到就已经被挤出内存,不仅占用内存,而且价值不大。频繁修改的数据,看情况考虑使用缓存。举两个反例,寿星列表、导航信息都存在一个特点,就是信息修改频率不高,读取通常非常高的场景。
对于热点数据,比如我们的某IM产品,生日祝福模块,当天的寿星列表,缓存以后可能读取数十万次。再举个例子,某导航产品,我们将导航信息,缓存以后可能读取数百万次。
数据更新前至少读取两次,缓存才有意义。这个是最基本的策略,如果缓存还没有起作用就失效了,那就没有太大价值了。
那存不存在,修改频率很高,但是又不得不考虑缓存的场景呢?有!比如,这个读取接口对数据库的压力很大,但是又是热点数据,这个时候就需要考虑通过缓存手段,减少数据库的压力,比如我们的某助手产品的,点赞数,收藏数,分享数等是非常典型的热点数据,但是又不断变化,此时就需要将数据同步保存到Redis缓存,减少数据库压力

10、单线程的redis为什么这么快

(一)纯内存操作
(二)单线程操作,避免了频繁的上下文切换
(三)采用了非阻塞I/O多路复用机制

11、redis中有事务吗?

有的,Redis事务功能是通过MULTI、EXEC、DISCARD和WATCH 四个原语实现的

1)MULTI命令用于开启一个事务,它总是返回OK。 MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执
行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。
2)EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。当操作被打断时,返回空值 nil 。
3)通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务, 并且客户端会从事务状态中退出。
4)WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。 可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令

12、Redis 的同步机制了解么?

Redis 可以使用主从同步,从从同步。第一次同步时,主节点做一次 bgsave(即做rdb文件备份),并同时将后续修改操作记录到内存 buffer,待完成后将 rdb文件全量同步到复制节点,复制节点接受完成后将 rdb 镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。

13、Redis 集群最大节点个数是多少?

16384 个

14、Redis 的内存用完了会发生什么?

如果达到设置的上限,Redis 的写命令会返回错误信息(但是读命令还可以正常返回。)或者你可以将 Redis 当缓存来使用配置淘汰机制,当 Redis 达到内存上限时会冲刷掉旧的内容。

15、MySQL 里有 2000w 数据,redis 中只存 20w 的数据,如何保证 redis 中的数据都是热点数据?

Redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。

相关知识:Redis 提供 6 种数据淘汰策略:

volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-enviction(驱逐):禁止驱逐数据

16、假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以某个固定的已知的前缀开头的,如果将它们全部找出来?

使用 keys 指令可以扫出指定模式的 key 列表。

对方接着追问:如果这个 redis 正在给线上的业务提供服务,那使用 keys 指令会有什么问题?

这个时候你要回答 redis 关键的一个特性:redis 是单线程的。keys 指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,
服务才能恢复。这个时候可以使用 scan 指令,scan 指令可以无阻塞的提取出指定模式的 key 列表,但是会有一定的重复概率,在客户端做
一次去重就可以了,但是整体所花费的时间会比直接用 keys 指令长。

17、使用过 Redis 分布式锁么,它是什么回事

先拿 setnx 来争抢锁,抢到之后,再用 expire 给锁加一个过期时间防止锁忘记了释放。
这时候对方会告诉你说你回答得不错,然后接着问如果在 setnx 之后执行 expire之前进程意外 crash 或者要重启维护了,那会怎么样?
这时候你要给予惊讶的反馈:唉,是喔,这个锁就永远得不到释放了。紧接着你需要抓一抓自己得脑袋,故作思考片刻,好像接下来的结果是你主动思考出来的,然后回答:我记得 set 指令有非常复杂的参数,这个应该是可以同时把 setnx 和expire 合成一条指令来用的!对方这时会显露笑容,心里开始默念:摁,这小子还不错。

二、RabbitMQ面试题

1、什么是 rabbitmq

采用 AMQP 高级消息队列协议的一种消息队列技术,最大的特点就是消费并不需要确保提供方存在,实现了服务之间的高度解耦

2、为什么要使用 rabbitmq

1、在分布式系统下具备异步,削峰,负载均衡等一系列高级功能;
2、拥有持久化的机制,进程消息,队列中的信息也可以保存下来。
3、实现消费者和生产者之间的解耦。
4、对于高并发场景下,利用消息队列可以使得同步访问变为串行访问达到一定量的限流,利于数据库的操作。
5.可以使用消息队列达到异步下单的效果,排队中,后台进行逻辑下单。

3、使用 rabbitmq 的场景

1、服务间异步通信
2、顺序消费
3、定时任务
4、请求削峰

4、如何保证RabbitMQ不被重复消费?

答:先说为什么会重复消费:正常情况下,消费者在消费消息的时候,消费完毕后,会发送一个确认消息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除;

但是因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将消息分发给其他的消费者。

针对以上问题,一个解决思路是:保证消息的唯一性,就算是多次传输,不要让消息的多次消费带来影响;保证消息等幂性;

比如:

让每个消息携带一个全局的唯一ID,即可保证消息的幂等性,具体消费过程为:

  1. 消费者获取到消息后先根据id去查询redis/db是否存在该消息
  2. 如果不存在,则正常消费,消费完毕后写入redis/db
  3. 如果存在,则证明消息被消费过,直接丢弃。

相关文章

暂无评论

暂无评论...