当前位置:   article > 正文

Java初级面试常见面试题_java面试题初级

java面试题初级

下面的这些不够看?可以访问我的语雀专栏
https://www.yuque.com/greedy-9i38g/tzpwui?# 《面试题》

文章目录

JavaSE

Java基本数据类型大小

一个字节等于8位 1byte = 8bit。int :4个字节 32位 (-231~231-1)。口诀:1248,4812

  • byte:1
  • short:2
  • int:4
  • long:8
  • float:4
  • double:8
  • boolean:1
  • char:2

JAVA中&&和||两种符号

  • &&可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都为true时,整个运算结果才为true,否则,只要有一方为false,则结果为false。
  • &&还具有短路的功能,即如果第一个表达式为false,则不再计算第二个表达式,例如,对于if(str != null && !str.equals(“”))表达式,当str为null时,后面的表达式不会执行,所以不会出现NullPointerException
  • ||可以作逻辑或运算符,表示逻辑或(or),当运算符有一边为true时,整个运算结果为true

抽象类不能创建对象,那么抽象类中是否有构造器

  • 抽象类中一定有构造器。构造器的作用:给子类初始化对象的时候要先super调用父类的构造器

抽象类是否可以被final修饰

  • 不能被final修饰,因为抽象类设计的初衷就是给子类继承用的。要是被final修饰了这个抽象类了,就不存在继承了,就没有子类
  • 抽象类可以被其他类继承:一个类继承一个抽象类,那么这个类可以变成抽象类,一般子类不会加abstract修饰,一般会让子类重写父类中的抽象方法,子类继承抽象类,就必须重写全部的抽象方法,子类如果没有重写父类全部的抽象方法,那么子类也可以变成一个抽象类

抽象方法可否被static修饰

  • 不可以:抽象方法是让子类去重写完善功能的,而static修饰的方法不能被重写

HashCode()、equals()区别

都是Object类中的方法:

  1. 如果类中不重写此方法
    • hashcode():属于是本地方法,返回的是对象的地址值
    • equals():用来比较两个对象的地址值是否相等
  2. 如果重写此方法
    • hashcode():放回的是根据对象的成员变量,计算出的一个整数
    • equals():比较的是两个对象的成员信息是否相同

类中重写hashCode()、equals()比肩两个对象是否相等,如何提高效率

  • 两个对象通过equals()比较是相等的,那么hashCode()肯定想等,两个对象通过hashCode()比较相等,但equals()不一定相等
  • 先将两个对象进行hashCode()比较,hashCode不相等则equals一定不相等,如果相等再进行equals比较

Error和Exception的区别

  • Error:通常是比较严重的错误,JVM无法解决的,常见的栈溢出StackOverflowError、内存溢出OutOfMemoryError
  • Exception:一般错误,程序员可以手动处理,常见的空指针异常NullPointException,数组越界ArrayIndexOutOfBoundsException

Java对异常默认的处理方式

  • Java默认处理方式,是将问题抛出给上一级,抛出之前,Java会根据错误产生的异常类,创建出该类的对象,底层通过thorw关键字讲异常抛出给上级,不断的向上抛出,直到抛给JVM,JVM拿到问题之后,讲错误原因和所在位置打印在控制台(method->main->jvm>控制台)

throw与throws

  • throw:将异常对象抛给调用者
  • throws:仅仅是声明作用,告诉调用者,此方法存在异常
  • 问题可以自己解决的:try-catch,不会影响后面代码继续执行
  • 问题自己解决不了的:throws抛出,通过throw关键字,将异常对象抛出给调用者,如果使用new throw抛出异常对象,则方法上必须进行throws声明
  • 如果抛出的异常对象是RuntimeException,则方法上无需throws声明

String、StringBuffer、StringBuilder

可变性线程安全
String不可变安全
StringBuilder可变不安全
StringBuffer可变安全,内部使用syvchronized进行同步

Java集合

LinkedList与ArrayList区别

  1. LinkedList
    • 基于双向链表,无需连续内存
    • 随机访问慢,因为要沿着链表遍历
    • 头尾插入删除性能高
    • 占用内存多
  2. ArrayList
    • 基于数组,需要连续内存
    • 随机访问快,根据下标访问
    • 尾部插入、删除性能可以,其他部分插入、删除都会移动数据,性能低
    • 可以利用CPU缓存,局部性原理

HashMap和Hashtable的区别

  • 都是一个键对应一个值,键不能重复,值可以
  • HashMap是JDK1.2出现,Hashtable是JDK1.0出现
  • HashMap线程不同步,Hashtable线程同步
  • HashMap容量是2的n次幂,hash分散性不好,需要二次哈希补偿,Hashtable没有采用这一设计
  • HashMap可以存储null键null值,Hashtable不能存储null键null值

JavaEE

请求转发与重定向区别

  • 重定向是浏览器发送请求并收到响应以后再次向一个新地址发请求;请求转发是服务器收到请求后为了完成响应转到另一个资源

  • 重定向中有两次请求对象,不共享数据;请求转发只产生一次请求对象且在组件间共享数据

  • 重定向后地址栏改变;请求转发不变

  • 重定向的新地址可以是任意地址;请求转发必须是同一个应用内的资源

在这里插入图片描述

获取servlet的转发和响应重定向的方式

转发的方法:

  1. 通过HttpServletRequest的getRequestDispatcher()方法获得
  2. 通过ServletCOntext的getRequestDispatcher()方法获得

重定向的方法:

  1. HttpServletResponse的sendRedirect()方法

XML与JSON区别

  • JSON数据体积更小,传输速度快
  • jJSON跟js交互更方便
  • JSON数据可读性比xml低

cookie、session、token的区别

  • cookie: 是存储在浏览器端的一小段文本数据(大小不超过4kb),cookie里的内容会在请求头里随着http请求一起发送到服务器端
  • session是存储在服务器端的一组数据,用来存储用户会话的数据,有的网址是采用session机制来验证用户身份的,通常会把session ID存储在cookie中
  • token通常用来代表一小段字符串,token可以存储在cookie里,也可以存储在服务器的内存里

有一种特殊的token :JSON WEB TOKEN(都叫它Jwt

JWT

在网络应用中传递一些小批量的安全数据时使用,这个token的特点是紧凑并且安全,特别适用于分布式站点的单点登录

jwt结构主要分为三部分:header头部、payload负载、signature签名

主要应用场景:身份认证:

  • 传统方法在服务器端存储一个session,给客户端返回一个cookie。把SessionID存储在cookie中,使用JWT就是当用户登陆系统之后,后台会返回一个jwt给用户(浏览器端)。用户只需要本地保存这个token
  • web应用通常使用cookie或者是local storage来存储
  • APP应用使用app自己的存储机制来存储,用户请求后台资源时,每次都要带上这个token,后台会对这个token进行验证

token在项目中的应用?token校验过程

token多用于单点登录:

  1. 客户端使用用户名密码请求登录
  2. 服务端收到请求,去验证用户名密码
  3. 验证成功后,服务端会签发一个token,再把这个token发送给客户端,客户端收到token以后把它存储起来,放在Cookie或者Local-Storage里,如果是单点登录应该存放到本地,比如Redis
  4. 客户端每次向服务器请求资源的时候需要带着服务端签发的token,服务端收到请求,对token进行解析验证

单点登录和跨域单点登录的区别?

有多个应用系统,如果在同一域名下这就是普通的单点登录,比如:username.greedy.compassword.greedy.com。同一域名下它们会共享本地的token

如果不在同一域名下,这时就需要用跨域单点登录的解决方案,最有名的规范就是JWT

Servlet生命周期

一般情况,servlet是在被请求的时候才去创建的,它提供了生命周期的方法:实例化,初始化,服务和销毁。比如我们可以在初始化方法设置一些初始数据等。使用servlet时,一般都是继承httpServlet,然后分别实现doGet或者dePost方法,但是在这里面要注意的是,servlet并不是线程安全的,多线程单实例执行的,当并发访问同一个资源的话,就有可能引发线程安全问题,不要写全局变量

JUC

创建线程的几种方式

  1. 继承Thread类
    • Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新的线程,并执行run()方法。这种方式实现多线程很简单,通过自己创建的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法
    • 优点:代码简单
    • 缺点:该类无法继承别的类
  2. 实现Runnable接口
    • Java中的类属于单继承,如果自己的类已经extend另一个类,就无法直接extends Thread,但是一个类继承一个类的同时,是可以实现多个接口的
    • 优点:继承其它类,统一实现该接口的实例可以共享资源
    • 缺点:代码复杂
  3. 实现Callable接口
    • 实现Runnable和实现Callable接口的方式基本相同,不过Callable接口中的call()方法有返回值,Runnable接口中的run()方法无返回值
  4. 线程池方式
    • 线程池就是一个容纳多个线程的容器,其中的线程可以重复使用,省去了频繁的创建线程对象的操作,反复创建线程是非常消耗资源的
    • 优点:实现自动化装配,易于管理,循环利用资源

Lock接口比synchronized块优势是什么

  1. 能够显示的获取锁lock()和释放锁unlock()
  2. 可以方便的实现公平锁

公平锁与非公平锁

  • 公平锁:表示线程获取锁的顺序是按线程加锁的顺序来进行的分配的,先来先得,先进先出
  • 非公平锁:一个获取锁的抢占机制,是随机拿到锁的,不一定先来的就能拿到锁,可能会导致一些线程一直拿不到锁

计算机网络

IP地址、MAC地址与DNS

IP地址: 就好比每个人的身份证一样,可以通过IP地址精准找到你的设备

有一种特殊的IP地址:内网IP地址。由于IP地址有限,IP地址不够用,于是每个局域网就对外共用同一个IP地址,内部的设备就用内网的IP地址,内网和外网采用路由器进行连接和转发消息,内网IP有三个保留的IP地址段:10,192,172

MAC地址: 也叫物理地址,是硬件厂家直接烧在网卡上的。理论上MAC地址是唯一的,但是MAC地址可以通过程序修改,可能会重复

IP地址与MAC地址在计算机里都是以二进制的方式进行表示的,IP地址是32位的,MAC地址是32位的

DNS: 由于IP地址是一堆数字不容易被记住,所以有了域名(域名就是相当于IP地址的一个别名),DNS就是把域名转换成IP地址的

从浏览器输入URL到页面展现的过程

第一步:专业说法:DNS 查询分级缓存策略(DNS Query)

找缓存顺序:浏览器 —> 操作系统 —> 本地服务商 —> 根服务器

  1. 先查询浏览器的本地缓存(通常在内存中)
  2. 本地没找到,查找操作系统的 host 文件,该文件在 Linux 中在 /etc/hosts 里
  3. 上述还没找到,DNS会查询本地服务提供商(ISP)
  4. ISP没找到,请求指向ROOT根服务器,返回顶级域名服务器地址
  5. 浏览器发送请求给顶级域名服务器,返回权威域名服务器地址
  6. 浏览器发送 Lookup 请求给权威域名服务器,找到具体DNS记录,返回给浏览器

第二步:向IP地址发起连接请求,进行TCP三次握手四次挥手

img

三次握手img

四次挥手img

第三步:页面展现

首先解析HTML文件创建DOM树,之后解析CSS形成CSS对象模型,将CSS模型跟DOM树合并成渲染树

什么是CDN

CDN中文名称:内容分发网络,用来做内容分发的一套网络体系,用来提升文件下载速度的一种机制,让用户在离自己最近的CDN服务器进行下载,减少路由次数,提升用户体验。(有点像在京东购物,会在距离你下单地址最近的仓库给你发货的意思)

在这里插入图片描述

CDN

TCP、UDP、HTTP协议

  • TCP协议提供安全可靠的网络传输服务,它是一种面向连接的服务,类似于打电话,必须先拨号,双方建立一个传输信息的通道进行传输
  • UDP协议是一种数据报协议,它传输的数据书分组报文,它是无连接的,不需要和目标通信才建立连接,UDP类似于写信,所以UDP传输不保证安全可靠。但适合大数据量的传输
  • HTTP协议是超文本传输协议,是一种相对于TCP来说更细致的协议,TCP和UDP协议规范的是网络设备之间的通信规范,HTTP是在TCP协议的基础上针对用户服务的协议,用户服务具体体现在应用程序之间的交互,比如我们的JavaWeb中的客户端与服务端体系就要用HTTP协议来规范通信

TCP/IP四层模型

  1. 应用层:为应用程序提供交互服务。在互联网中的应用层协议很多,如域名系统DNS、HTTP协
    议、SMTP协议等。
  2. 传输层:负责向两台主机进程之间的通信提供数据传输服务。传输层的协议主要有传输控制协议
    TCP和用户数据协议UDP。
  3. 网络层:选择合适的路由和交换结点,确保数据及时传送。主要包括IP协议。
  4. 数据链路层:在两个相邻节点之间传送数据时,数据链路层将网络层交下来的 IP 数据报组装成
    帧,在两个相邻节点间的链路上传送帧。

HTTP状态码

类别原因
1XX服务器收到请求,需要客户端继续发送请求
2XX请求正常处理完毕
3XX重定向,需要进一步操作
4XX请求无效,客服端错误,服务端无法处理请求
5XX服务器后端出错

详细:

  • 100:告诉客户端继续发送请求

  • 200:表示请求响应成功

  • 201:请求成功,服务器创建了新资源

  • 202:请求已经被处理还未做出响应

  • 301:请求的网页被永久移动到了新位置客户端收到301请求后,通常用户会向新地址发起GET请求

  • 308:永久重定向客户端收到308请求后,延用旧的method(POST/GET/PUT)到新地址

  • 400:请求无效,常见的情况是请求参数有误,HTTP头构建错误

  • 403:禁止访问

  • 404:访问不到资源

  • 500:服务器后端错误

  • 505: 服务器不支持请求中所使用的 HTTP 协议版本

POST和GET的区别

  • GET请求参数通过URL传递,POST的参数放在请求体中
  • GET产生一个TCP数据包;POST产生两个TCP数据包。对于GET方式的请求,浏览器会把请求头和请求体一并发送出去;而对于POST,浏览器先发送请求头,服务器响应100 continue,浏览器再发送请求体
  • GET请求会被浏览器主动缓存,而POST不会,除非手动设置
  • GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留
  • GET请求参数不安全,提交数据量小,POST提交数据比较安全提交的,数据量无限制

HTTP 与 HTTPS 的区别

区别HTTPHTTPS
协议运行在 TCP 之上,明文传输,客户端与服务器端都无法验证对方的身份身披 SSL( Secure Socket Layer )外壳的 HTTP,运行于 SSL 上,SSL 运行于 TCP 之 上, 是添加了加密和认证机制的 HTTP。
端口80443
资源消耗较少由于加解密处理,会消耗更多的CPU和内存资源
开销无需证书需要证书,而证书一般需要向认证机构购买
加密机制共享密钥加密和公开密钥加密并用的混合加密机制
安全性由于加密机制,安全性强

数据库

索引失效

  1. 模糊查询:使用like关键字的时候,以%号开头索引就会失效
  2. 数据类型错误,索引也会失效
  3. 对索引的字段使用内部函数,索引也会失效。这种情况应该建立基于函数的索引
  4. null,索引不存储空值,如果不限制索引列not null,数据库会认为索引列有可能存在空值,所以不会按照索引值进行计算
  5. 对索引列进行加减乘除运算,会导致索引失效
  6. 最左原则,在复合索引中,索引列的顺序非常重要,如果不是按照索引列最左列,开始进行查找,则无法使用索引
  7. 如果数据库预计使用全表扫描比使用索引更快,数据库不会使用索引

数据隔离级别

  • 脏读:一个事务读到另一个事务还没有提交的数据
  • 不可重复读:是指在对于数据库中的某行记录,一个事务范围内多次查询却返回了不同的数据值,这
    是由于在查询期间,另一个事务update数据并提交了。
  • 幻读:一个事务读到了另一个事务已经提交了insert的数据,导致在当前事务中多次查询的结果不一致

MySQL数据库为我们提供的四种隔离级别:(级别从低到高,级别越高越安全,效率越低)

  • Read committed (读已提交):一个事务只能看见已经提交事务所做的改变。可避免脏读的发生。

  • Read uncommitted (读未提交):Oracle默认事务隔离级别所有事务都可以看到其他未提交事务的执行结果。

  • Repeatable read (可重复读):MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行,解决了不可重复读的问题。

  • Serializable (串行化):通过强制事务排序(当前事务没结束,其它事务就得等着),使之不可能相互冲突,从而解决幻读问题。

JVM

类加载器

  • JDK自带三个类加载器:启动类加载器、扩展类加载器、应用类加载器

  • 假设有这样一段代码:String s = “abc”;

  • 代码开始执行前,会将所有需要的类全部加载到JVM中,通过类加载器加载,看到上述代码类加载器会找String.class文件,找到就加载。

  1. 首先通过"启动类加载器"加载:启动类加载器专门加载:“D:\JAVA\JDK\jdk1.8.0_251\jre\lib\rt.jar”,rt.jar中都是JDK最核心的类库
  2. 如果启动类加载器加载不到的时候,会通过"扩展类加载器"加载:扩展类加载器专门加载:“D:\JAVA\JDK\jdk1.8.0_251\jre\lib\ext*.jar”
  3. 如果扩展类加载器加载不到的时候,会通过"应用类加载器"加载:应用类加载器专门加载:classpath中的jar包(class文件)

在这里插入图片描述

类的实例化顺序

  1. 父类中的静态代码块,当前类的静态代码块
  2. 父类的普通代码块
  3. 父类的构造函数
  4. 当前类普通代码块
  5. 当前类的构造函数

类加载机制

  1. 加载:将class文件读入内存,并为之创建java.lang.Class对象
  2. 验证:检验被加载的类是否有正确的内部结构,并和其他类协调一致
  3. 准备:负责为类的类变量(static)分配内存,并设置默认初始化值
  4. 解析:将常量池的符号引用解析为直接引用
  5. 初始化:静态代码块、static 修饰的变量赋值、static final 修饰的引用类型变量赋值(static final 修饰的基本类型变量赋值,在链接阶段就已完成),初始化是懒惰执行

Java中内存分配

  1. 栈内存:存储的是局部变量,局部变量指的是方法中的变量
  2. 堆内存:new关键字出来的对象,有程序员手动释放,程序员不释放,最终会由OS回收
  3. 静态区:全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量在一块区域
  4. 常量池:字符串存储的区域v

常量池与运行时常量池

  • 常量池:就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量
    等信息
  • 运行时常量池:常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量
    池,并把里面的符号地址变为真实地址

SpringBoot

常用注解

  • @ConditionalOnClass,classpath 下存在某个 class 时,条件才成立
  • @ConditionalOnMissingBean,beanFactory 内不存在某个 bean 时,条件才成立
  • @ConditionalOnProperty,配置文件中存在某个 property(键、值)时,条件才成立
  • @ConfigurationProperties,会将当前 bean 的属性与配置文件中的键值进行绑定

SpringBoot自动配置原理

@SpringBootApplication是组合注解,由@ConponentScan、@EableAutoConfiguration、@SpringBootConfiguration和其它注解组成

  1. @SpringBootConfiguration与其它@Configuration注解功能一样,只不过@SpringBootConfiguration在全局唯一
  2. @ConponentScan中的excluderFilter用来在进行组件扫描的时候进行排除,也会排除自动配置类
  3. @EnableAutoConfiguration也是一个组合注解
    1. @AutoConfigurationPackage-用来记住扫描的起始包(就是以该包为起点扫描其子孙包)
    2. @Import(AutoConfigurationImportSelector.class)用来加载META-INF/spring.fanctories中的自动配置类

AutoConfigurationImportSelector.class会通过SpringFactoriesLoader.class扫描"META-INF/spring.factories"

为什么不用@Import直接引入自动配置类

  1. 让主配置类(程序员写的类)和自动配置类变成了强耦合,主配置类不应该知道有哪些自动配置类
  2. 直接用@Import(自动配置类.class),引入的配置解析优先级高,自动配置类的解析应该在主配置类(程序员写的类)没提供时作为默认配置

AutoConfigurationImportSelector.class实现了DeferredImportSelector接口,让自动配置类解析晚于主配置类

Spring

常用注解

  1. @Order:控制bean的执行顺序,数字越小优先级越高

  2. @Configuration

    • 配置类相当于一个工厂,标注@Bean注解的方法相当于工厂方法
    • @Bean不支持方法重载,如果有多个重载方法,仅有一个能入选为工厂方法,重载的工厂方法参数越多的优先级越高
    • @Configuration默认会为标注的类生成代理,其目的是保证@Bean方法相互调用的时候,仍能保证其单例
    • @Configuration中如果有BeanFanctory后处理器,则实例工厂方法会导致MyConfig提前创建,造成其依赖注入失败,解决方法是改用静态工厂方法或直接为 @Bean 的方法参数依赖注入, 针对 Mapper 扫描可以改用注解方式
  3. @Import

    1. 四种用法

      • 引入单个bean
      • 引入一个配置类
      • 通过Selector引入多个类
      • 通过beanDefinition注册器
    2. 解析规则

      • 同一配置类中,@Import优先级大于@Bean,就是@Import的类先解析再解析@Bean的类
      • 同名定义的,默认后面的解析会覆盖掉前面的解析
    3. 不允许覆盖的情况下, 如何能够让主配置类(程序员自己写的配置类)的配置优先?

      • 采用DeferredImportSelector,使主配置类实现DeferredImportSelector,因为它最后工作,可以理解为先解析@Bean,再@Import
    4. @Lazy

      • 加在类上,表示此类延迟实例化、初始化
      • 加在方法参数上,此参数会以代理方式注入

SpringMVC

常用注解

  • @RequestMapping,可以派生多个注解如 @GetMapping 等
  • @RequestBody:处理请求体中的JSON数据,把JSON数据转换为Java对象
  • @ResponseBody:把Java对象转换为JSON数据,写入到响应体,组合 @Controller => @RestController
  • @ResponseStatus:控制响应状态码
  • @ControllerAdvice,一般用于统一处理异常。组合 @ResponseBody => @RestControllerAdvice
  • @PathVariable:映射 URL 绑定的占位符

Mybatis

Mybatis中动态SQL语句是什么意思?

动态SQL:

  • SQL语句主体结构,在编译时无法确定,只有等到程序运行起来,在执行过程中才能确定
  • Mybatis中用于实现动态SQL的主要元素有:if、where、foreach

静态SQL:

  • 在应用程序运行前进行编译的,编译结果会存储在数据库内部,当程序运行时,数据库将直接执行编译好的SQL语句,降低开销

值得注意一点:

select * from user where age > ?这种语句是静态SQL,虽然个别参数的值不知道,但整个SQL的结构已经确定,数据库是可以将它编译的。动态SQL最明显的例子就是多条件查询,其中查询的条件每次并不确定有哪些

mybatis中的#和$的区别

  1. #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:where username=#{username},如果传入的值是111,那么解析成sql时的值为where username=“111”, 如果传入的值是id,则解析成的sql为where username=“id”.
  2. 将传入的数据直接显示生成在 s q l 中。如: w h e r e u s e r n a m e = 将传入的数据直接显示生成在sql中。如:where username= 将传入的数据直接显示生成在sql中。如:whereusername={username},如果传入的值是111,那么解析成sql时的值为where username=111;如果传入的值是;drop table user;,则解析成的sql为:select id, username, password, role from user where username=;drop table user;
  3. #方式能够很大程度防止sql注入,$方式无法防止Sql注入。
  4. $方式一般用于传入数据库对象,例如传入表名.
  5. 一般能用#的就别用 ,若不得不使用“ ,若不得不使用“ ,若不得不使用{xxx}”这样的参数,要手工地做好过滤工作,来防止sql注入攻击。
  6. 在MyBatis中,“ x x x ”这样格式的参数会直接参与 S Q L 编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“ {xxx}”这样格式的参数会直接参与SQL编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“ xxx这样格式的参数会直接参与SQL编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用{xxx}”这样的参数格式。所以,这样的参数需要我们在代码中手工进行处理来防止注入。
  7. 【结论】在编写MyBatis的映射语句时,尽量采用“#{xxx}”这样的格式。若不得不使用“${xxx}”这样的参数,要手工地做好过滤工作,来防止SQL注入攻击。

Redis

Redis为什么这么快

  1. 完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是 O(1);
  2. 数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的;
  3. 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
  4. 使用多路 I/O 复用模型,非阻塞 IO;
  5. 使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;

缓存与数据库同步怎么做的

缓存同步其实就是当缓存信息发生了变化,也就是后台对数据进行了 操作后,数据库中发生了变化的数据删除相应的缓存即可。当页面再次请求数据时,缓存不能命中,会从数据库中查询并且添加到缓存中,即实现了缓存同步

缓存穿透

缓存穿透是查不到,与缓存击穿有明显的区别。缓存击穿是查到的太多了!

redis缓存中没有该条查询结果,会去数据库中查询,查询一次没有就查询两次,如果这个时候秒杀系统(也可能是有人采用洪水攻击)大量给数据库发送请求,数据库可能会招架不住导致崩溃 ┗( T﹏T )┛

布隆过滤器解决缓存穿透

布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储层的查询压力。相比于传统的 List、Set、Map 等数据结构,它更高效、占用空间更少,但是缺点是其返回的结果是概率性的,而不是确切的

在这里插入图片描述

缓存空对象

当存储层不命中后,即使返回的空对象也能将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源。如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的空键

即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间的窗口不一致,这对于需要保持一致性的业务造成影响

缓存击穿

简述:

缓存击穿与缓存穿透不一样,缓存击穿是一个key非常热点,一直在扛着高并发。俗话说就是:对着一个点猛攻!

当这个Key在过期的那一瞬间,由于缓存过期会同时访问数据库来查询最新数据,并回写缓存,这会导致数据库瞬间压力过大

解决方案:

  1. 设置热点数据永不过期(不太可能实现,因为存着存着的它会满啊,满了自己删除一些Key)
  2. 加锁,使用分布式锁,保证对于每个Key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可,把压力给了分布式锁

缓存雪崩

简述:

是指在某一时间段内,缓存集体过期失效,或者是Redis宕机,就是上图中缓存层没有啦

但最致命的是缓存服务的某个节点宕机,对数据库造成的压力不可预知,很有可能瞬间就把数据库搞垮

解决方案:

  1. Redis高可用:多加几台Redis
  2. 限流降级:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量
  3. 数据预热:就是提前把可能会被大量访问的Key们,加载到缓存中,给它们设置不同的过期时间

哨兵模式

简述:

哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例

在这里插入图片描述

哨兵两个作用:

  1. 发送命令,让每个被监控的Redis服务器返回其运行状态
  2. 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机

一个哨兵监控的时候可能会有偷懒的时候 ,这个时候就需要多个哨兵,而且让哨兵之间互相监督不许偷懒

文字叙述一下故障转移过程:

假设哨兵A发现主机挂掉啦,不会马上进行故障转移,因为这仅仅是它自己主观认为主机挂掉了,这个现象网上称为主观下线。等到发现主机挂掉到达一定数量的其他哨兵,它们会集合起来开会,最后决定由一个哨兵去进行故障转移。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从机切换为主机,这个过程称为客观下线。这样对于外部而言,一切都是透明的

在这里插入图片描述

DRB与AOF

RDB可以看作为某一时刻Redis的快照,比较适合灾难恢复,可以看作快照

优点:主进程不进行任何IO操作,确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是很敏感,那么RDB方式要比AOF方式更加高效

缺点:最后一次持久化的时候如果宕机,数据可能丢失。fork进程的时候,会占用一定的内存空间

RDB触发机制

  1. save的规则满足的情况下,会自动触发RDB规则
  2. 执行flushall命令,也会触发RDB规则
  3. 退出Redis,也会产生RDB文件

恢复RDB文件

​ 只需要将RDB文件放在redis启动目录,redis启动的时候会自动检查dump.rdb恢复其中的数据

AOF是将所有命令都记录下来,恢复的时候把文件全部再执行一遍

AOF默认是关闭的,需要在配置文件中开启AOF。Redis支持AOF和RDB同时生效,如果同时存在,AOF优先级高于RDB(Redis重新启动时会使用AOF进行数据恢复)

监听执行的命令,如果发现执行了修改数据的操作,同时直接同步到数据库文件中

优点:相对RDB数据更加安全

缺点:相同数据集AOF要大于RDB,相对RDB可能会慢一些

机制**

  1. save的规则满足的情况下,会自动触发RDB规则
  2. 执行flushall命令,也会触发RDB规则
  3. 退出Redis,也会产生RDB文件

恢复RDB文件

​ 只需要将RDB文件放在redis启动目录,redis启动的时候会自动检查dump.rdb恢复其中的数据

AOF是将所有命令都记录下来,恢复的时候把文件全部再执行一遍

AOF默认是关闭的,需要在配置文件中开启AOF。Redis支持AOF和RDB同时生效,如果同时存在,AOF优先级高于RDB(Redis重新启动时会使用AOF进行数据恢复)

监听执行的命令,如果发现执行了修改数据的操作,同时直接同步到数据库文件中

优点:相对RDB数据更加安全

缺点:相同数据集AOF要大于RDB,相对RDB可能会慢一些

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号