面试题(答案正在补录中)

面试题(答案正在补录中)

请说明一下Spring MVC注解的优点是什么?

注解相较于XML配置文件具有简洁,不容易出错的优点。通过注解可以使用创建对象或者注入属性等功能

Java容器有哪些?

主要有list,set,map
list分为linkedlist;arraylist;vector
set分为hashset;linkedset;treeset
map分为hashmap;hashtable;weakhashmap

如何实现一个IOC容器

1.配置一个包扫描路径
2.根据包扫描出来的路径获取class文件
3.映射,确定需要交给IOC容器管理的类
4.对需要注入的类经行依赖注入

Collection和collections的区别?

Collection是集合类的上级接口,继承与他有关的接口主要有List和Set
Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全等操作

List,set,map的区别

List

1.可以允许重复的对象。
2.可以插入多个null元素。
3.是一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序。
4.常用的实现类有 ArrayList、LinkedList 和 Vector。ArrayList 最为流行,它提供了使用索引的随意访问,而 LinkedList 则对于经常需要从 List 中添加或删除元素的场合更为合适。

Set

1.不允许重复对象
2. 无序容器,你无法保证每个元素的存储顺序,TreeSet通过 Comparator 或者 Comparable 维护了一个排序顺序。
3. 只允许一个 null 元素
4.Set 接口最流行的几个实现类是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基于 HashMap 实现的 HashSet;TreeSet 还实现了 SortedSet 接口,因此 TreeSet 是一个根据其 compare() 和 compareTo() 的定义进行排序的有序容器。

map

1.Map不是collection的子接口或者实现类。Map是一个接口。
2.Map 的 每个 Entry 都持有两个对象,也就是一个键一个值,Map 可能会持有相同的值对象但键对象必须是唯一的。
3. TreeMap 也通过 Comparator 或者 Comparable 维护了一个排序顺序。
4. Map 里你可以拥有随意个 null 值但最多只能有一个 null 键。
5.Map 接口最流行的几个实现类是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最常用)

Hashmap和hashtable和Treemap的区别

1.HashMap、HashTable和TreeMap都是map接口的子类,而在Map中我们通过对象来对对象进行索引,用来索引的对象叫做key,其对应的对象叫做value。这就是我们平时说的键值对。put时有相同的key,会覆盖该key对应的值。
2.HashMap通过hashcode对其内容进行快速查找,而 TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的)。
3.HashMap 非线程安全 HashTable 线程安全 TreeMap 非线程安全。

HashMap:

基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。为了优化HashMap空间的使用,您可以调优初始容量和负载因子。
(1)HashMap(): 构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap
(2)HashMap(Map m): 构造一个映射关系与指定 Map 相同的新 HashMap
(3)HashMap(int initialCapacity): 构造一个带指定初始容量和默认加载因子 (0.75) 的空 HashMap
(4)HashMap(int initialCapacity, float loadFactor): 构造一个带指定初始容量和加载因子的空 HashMap

HashTable:

实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值,即不允许null键null值。
(1)Hashtable(): 用默认的初始容量 (11) 和加载因子 (0.75) 构造一个新的空哈希表
(2)Hashtable(Map m): 构造一个与给定的 Map 具有相同映射关系的新哈希表
(3)Hashtable(int initialCapacity): 用指定初始容量和默认的加载因子 (0.75) 构造一个新的空哈希表
(4)Hashtable(int initialCapacity, float loadFactor): 指定初始容量和指定加载因子构造一个新的空哈希表

TreeMap:

基于红黑树实现。TreeMap没有调优选项,因为该树总处于平衡状态。不允许null键,允许null值。
(1)TreeMap():使用键的自然顺序构造一个新的、空的树映射
(2)TreeMap(Map m): 构建一个映像树,并且添加映像m中所有元素
(3)TreeMap(Comparator c): 构建一个映像树,并且使用特定的比较器对关键字进行排序
(4)TreeMap(SortedMap s): 构建一个映像树,添加映像树s中所有映射,并且使用与有序映像s相同的比较器排序

如何决定使用Hashmap和Treemap

TreeMap<K,V>的Key值是要求实现java.lang.Comparable,所以迭代的时候TreeMap默认是按照Key值升序排列的;TreeMap的实现也是基于红黑树结构。
而HashMap<K,V>的Key值实现散列hashCode(),分布是散列的均匀的,不支持排序,数据结构主要是桶(数组),链表或红黑树。
所以,查询的时候使用HashMap,增加、快速创建的时候使用TreeMap。

说一下Hashmap的实现原理

HashMap使用数组加链表实现。每个数组中储存着链表。
当使用put方法储存key-value键值对时,会先调用key的hashCode方法,得到此key经特定哈希运算后的值,然后将此值通过其他运算(?)得到一个值,将这个值与(length-1)做或操作(&),相当于对数组长度做取余操作。最终得到一个值作为此key在数组中的索引值,然后将key-value键值对储存进去。通过这种方法将储存的不同key-value键值对“散列”到数组的不同位置。
在储存的时候,如果索引位置尚无元素,那么直接储存。如果有元素,那么就调用此key的equals方法与原有的元素的Key进行比较。如果返回true,说明在这个equals定义的规则上,这两个Key相同,那么将原有的key保留,用新的value代替原来的value。如果返回false,那么就说明这两个key在equals定义的规则下是不同元素,那么就与此链表的下一个结点进行比较,知道最后一个结点都没有相同元素,再下一个是null的时候,就用头插法将此key-value添加到链表上。
HashMap对重复元素的处理方法是:key不变,value覆盖。
当使用get方法获取key对应的value时,会和储存key-value时用同样的方法,得到key在数组中的索引值,如果此索引值上没有元素,就返回null。如果此索引值上有元素,那么就拿此key的equals方法与此位置元素上的key进行比较,如果返回true。就返回此位置元素对应的value。如果返回false,就一直按链表往下比较,如果都是返回false,那么就返回null。
另外:HashMap在JDK1.8之后引入红黑树结构。HashMap是线程不安全的,线程安全的是CurrentHashMap,不过此集合在多线程下效率低。

说一下hashset的实现原理

首先,我们需要知道它是Set的一个实现,所以保证了当中没有重复的元素。
一方面Set中最重要的一个操作就是查找。而且通常我们会选择
HashSet来实现,因为它专门对快速查找进行了优化。
HashSet使用的是散列函数,那么它当中的元素也就无序可寻。当中是允许元素为null的。
先对实现原理进行一个总结:
1.基于HashMap实现的,默认构造函数是构建一个初始容量为16,负载因子为0.75 的HashMap。封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。
2.当我们试图把某个类的对象当成 HashMap的 key,或试图将这个类的对象放入 HashSet 中保存时,重写该类的equals(Object obj)方法和 hashCode() 方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的 hashCode() 返回值相同时,它们通过 equals() 方法比较也应该返回 true。通常来说,所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准。
3.HashSet的其他操作都是基于HashMap的。

Arraylist和linkedlist的区别

ArrayList和LinkedList都是实现了List接口的容器类,用于存储一系列的对象引用。他们都可以对元素的增删改查进行操作。
对于ArrayList,它在集合的末尾删除或添加元素所用的时间是一致的,但是在列表中间的部分添加或删除时所用时间就会大大增加。但是它在根据索引查找元素的时候速度很快。
对于LinkedList则相反,它在插入、删除集合中任何位置的元素所花费的时间都是一样的,但是它根据索引查询一个元素的时候却比较慢。

ArrayList和LinkedList的大致区别:

1.ArrayList是实现了基于动态数组的数据结构,LinkedList是基于链表结构。
2.对于随机访问的get和set方法,ArrayList要优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。

如何实现数组和list之间的转换

(1)数组转换为 List
方法一:可以直接使用Arrays类中的asList()方法,将数组转化为List,但是这个方法有很大的缺陷,List不像ArrayList类一样,不能进行添加和移除操作,这是因为ArrayList类继承了List接口,并且重写了里面的抽象方法
方法二:直接向 List 中添加数组的元素
(2)List 转换为数组
可以借助 List 中的 toArray() 方法来将 List 转换为数组,注意:toArray()方法内的如果没有参数返回的是object类型数组,如果要为string类型,则需要加:new String[list.size()];

Arraylist和rector的区别(未知)

Array和arraylist的区别

内容存储
Array数组可以包含基本类型和对象类型
ArrayList却只能存放对象类型,Array中存放的一定是同种类型的元素,ArrayList可以存放任何不同类型的数据因为他里面存放的都不装箱为Objuct型的对象
数组长度比较
Array数组空间长度是固定的,创建前需要给定长度
ArrayList长度是动态增长的,空间不够,他会创建一个空间比原空间大一倍的新数组,然后将所有元素都复制到新的数组中,旧的数组被抛弃,每次添加数据是都会检查数组内部空间是否够用,这里添加数据效率较低,可(参考List两个实例)
方法
ArrayList是Arry的增强版本,方法上比Array多很多如:移除,迭代器iterator等
用法方面
如果保存一个在程序运行时数据定长不会改变的,不向数据中插入数据选择ArryList,如果要插入数据,不建议使用,插入数据时要频繁移动元素位置,处理的数据量超大,性能很低,这种情况考虑使用LinkedList;

在queue中poll和remove的区别

poll() ,如果队列为空的时候,则会抛出异常
而remove()只会返回null

那些线程类是线程安全的

Vector:就比Arraylist多了个同步化机制(线程安全)。
Hashtable:就比Hashmap多了个线程安全。
ConcurrentHashMap:是一种高效但是线程安全的集合。

Stack:栈,也是线程安全的,继承于Vector(未知)

迭代器Iterator是什么,有什么特点

迭代器模式,它是 Java 中常用的设计模式之一。用于顺序访问集合对象的元素,无需知道集合对象的底层实现。
Iterator 是可以遍历集合的对象,为各种容器提供了公共的操作接口,隔离对容器的遍历操作和底层实现,从而解耦。
缺点是增加新的集合类需要对应增加新的迭代器类,迭代器类与集合类成对增加。
迭代器(也是一种设计模式),通常被称为轻量级对象
**:只因为创建它的代价小。
迭代器可以解决我们提出的问题。
它是一个对象,它的工作是遍历并选择序列中的对象,而客户端的程序员不必知道或关心该序列底层的结构。

例如Java的Iterator只能单向移动,它能用来:
(1)使用方法Iterator()要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素。
(2)使用next()获得序列中的下一个元素
(3)使用hasNext()检查序列中是否还有元素。
(4)使用remove()将迭代器新近返回的元素删除。
一个很强的用处:能够将遍历序列的操作与序列底层的结构分离。

Iteratorh和listIterator有什么区别

ListIterator 继承 Iterator
ListIterator 比 Iterator多方法
add(E e) 将指定的元素插入列表,插入位置为迭代器当前位置之前
set(E e) 迭代器返回的最后一个元素替换参数e
hasPrevious() 迭代器当前位置,反向遍历集合是否含有元素
previous() 迭代器当前位置,反向遍历集合,下一个元素
previousIndex() 迭代器当前位置,反向遍历集合,返回下一个元素的下标
nextIndex() 迭代器当前位置,返回下一个元素的下标
使用范围不同,Iterator可以迭代所有集合;ListIterator 只能用于List及其子类
ListIterator 有 add 方法,可以向 List 中添加对象;Iterator 不能
ListIterator 有 hasPrevious() 和 previous() 方法,可以实现逆向遍历;Iterator不可以
ListIterator 有 nextIndex() 和previousIndex() 方法,可定位当前索引的位置;Iterator不可以
ListIterator 有 set()方法,可以实现对 List 的修改;Iterator 仅能遍历,不能修改

怎么确保一个集合不能被修改

可以使用 Collections. unmodifiableCollection(Collection c) 方法来创建一个只读集合,这样改变集合的任何操作都会抛出 Java. lang. UnsupportedOperationException 异常(不支持的操作异常)。

异常问题

Throw和throws的区别

① throw代表动作,表示抛出一个异常的动作;throws代表一种状态,代表方法可能有异常抛出
② throw用在方法实现中,而throws用在方法声明中
③ throw只能用于抛出一种异常,而throws可以抛出多个异常

Final,finally,finalize的去别

1、final修饰符(关键字)。被final修饰的类,就意味着不能再派生出新的子类,不能作为父类而被子类继承。因此一个类不能既被abstract声明,又被final声明。将变量或方法声明为final,可以保证他们在使用的过程中不被修改。被声明为final的变量必须在声明时给出变量的初始值,而在以后的引用中只能读取。被final声明的方法也同样只能使用,即不能方法重写。
2、finally是在异常处理时提供finally块来执行任何清除操作。不管有没有异常被抛出、捕获,finally块都会被执行。try块中的内容是在无异常时执行到结束。catch块中的内容,是在try块内容发生catch所声明的异常时,跳转到catch块中执行。finally块则是无论异常是否发生,都会执行finally块的内容,所以在代码逻辑中有需要无论发生什么都必须执行的代码,就可以放在finally块中。
3、finalize是方法名。java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在object类中定义的,因此所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者被执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。

Try-catch-finally中那个可以省掉

catch可以省略掉

常见的异常类有哪些

算数异常类:ArithmeticExecption
空指针异常类型:NullPointerException
类型强制转换类型:ClassCastException
数组负下标异常:NegativeArrayException
数组下标越界异常:ArrayIndexOutOfBoundsException
违背安全原则异常:SecturityException
文件已结束异常:EOFException
文件未找到异常:FileNotFoundException
字符串转换为数字异常:NumberFormatException
操作数据库异常:SQLException
输入输出异常:IOException
方法未找到异常:NoSuchMethodException
下标越界异常:IndexOutOfBoundsExecption
系统异常:SystemException
创建一个大小为负数的数组错误异常:NegativeArraySizeException
数据格式异常:NumberFormatException
安全异常:SecurityException
不支持的操作异常:UnsupportedOperationException

网络部分

http响应码301和302分别是什么,有什么区别

301 表示被请求 url 永久转移到新的 url;302 表示被请求 url 临时转移到新的 url。
被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。除非额外指定,否则这个响应也是可缓存的。
301 搜索引擎会索引新 url 和新 url 页面的内容;302 搜索引擎可能会索引旧 url 和 新 url 的页面内容。
请求的资源现在临时从不同的 URI 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。

简述tcp和udp的区别

UDP 是面向无连接的通讯协议,UDP 数据包括目的端口号和源端信息。
优点:UDP 速度快、操作简单、要求系统资源较少,由于通讯不需要连接,可以实现广播发送
缺点:UDP 传送数据前并不与对方建立连接,对接收到的数据也不发送确认信号,发送端不知道数据是否会正确接收,也不重复发送,不可靠。
TCP 是面向连接的通讯协议,通过三次握手建立连接,通讯完成时四次挥手
优点:TCP 在数据传递时,有确认、窗口、重传、阻塞等控制机制,能保证数据正确性,较为可靠。
缺点:TCP 相对于 UDP 速度慢一点,要求系统资源较多
Tcp为啥那么要三次握手,两次不行吗,为什么

Get和post的区别

GET在浏览器回退时是无害的,而POST会再次提交请求。
GET产生的URL地址可以被Bookmark,而POST不可以。
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
GET请求只能进行url编码,而POST支持多种编码方式。
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
GET请求在URL中传送的参数是有长度限制的,而POST么有。
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
GET参数通过URL传递,POST放在Request body中。
HTTP是什么?HTTP是基于TCP/IP的关于数据如何在万维网中如何通信的协议。
HTTP的底层是TCP/IP。所以GET和POST的底层也是TCP/IP,也就是说,GET/POST都是TCP链接。GET和POST能做的事情是一样一样的。你要给GET加上request body,给POST带上url参数,技术上是完全行的通的
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

多线程

并行和并发的区别

并行:多个处理器或多核处理器同时处理多个任务。
并发:多个任务在同一个CPU核上,按细分的时间片轮流(交替)执行,从逻辑上来看并发的任务是同时执行;
简而言之:
并发=两个队列和一台处理器
并发=两个队列和两个处理器

线程和进程的区别

一个程序下至少有一个进程,
一个进程下至少有一个线程,一个进程下也可以有多个线程来增加程序的执行速度。

守护线程是什么

守护线程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。在Java中垃圾回收线程就是特殊的守护线程。

创建线程的几种方式

继承Thread重写run方法;
实现Runnable接口;
实现Callable接口。

线程有那几中运行状态

就绪(Runnable):线程准备运行,不一定立马就能开始执行。
运行中(Running):进程正在执行线程的代码。
等待中(Waiting):线程处于阻塞的状态,等待外部的处理结束。
睡眠中(Sleeping):线程被强制睡眠。
I/O阻塞(Blocked on I/O):等待I/O操作完成。
同步阻塞(Blocked on Synchronization):等待获取
死亡(Dead):线程完成了执行

Sleep和wait的区别

类的不同,sleep()来自Thred ,wait()来着Object,
释放锁,sleep()不释放锁,而wait()释放锁
用法不同,sleep()到时间会自动醒来,自动回复线程,而wait()需要用notify()/ notifyAll()来唤醒线程。

Notify和notifall的区别

notify() 方法随机唤醒对象的等待池中的一个线程,进入锁池;notifyAll() 唤醒对象的等待池中的所有线程,进入锁池。
等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到了该对象的等待池,等待池中的线程不会去竞该对象的锁。
锁池:只有获取了对象的锁,线程才能执行对象的 synchronized 代码,对象的锁每次只有一个线程可以获得,其他线程只能在锁池中等待

线程的run和stare的区别

start()方法用于启动线程,
run()方法用于执行线程的运行时代码,
run()可以重复使用,而start()只能调用一次。

什么是死锁,怎么眼防止

如果一组进程中每一个进程都在等待仅由该组进程中的其他进程才能引发的事件,那么该组进程是死锁的。
举例来说:有两个进程A和B,A持有资源a等待b资源,B持有资源b等待a资源,两个进程都在等待另一个资源的同时不释放资源,就形成死锁。
形成死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
处理死锁的思路如下:
预防死锁:破坏四个必要条件中的一个或多个来预防死锁
避免死锁:在资源动态分配的过程中,用某种方式防止系统进入不安全的状态。
检测死锁:运行时产生死锁,及时发现思索,将程序解脱出来。
解除死锁:发生死锁后,撤销进程,回收资源,分配给正在阻塞状态的进程。
预防死锁的办法:
破坏请求和保持条件:1.一次性的申请所有资源。之后不在申请资源,如果不满足资源条件则得不到资源分配。2.只获得初期资源运行,之后将运行完的资源释 放,请求新的资源。
破坏不可抢占条件:当一个进程获得某种不可抢占资源,提出新的资源申请,若不能满足,则释放所有资源,以后需要,再次重新申请。
破坏循环等待条件:对资源进行排号,按照序号递增的顺序请求资源。若进程获得序号高的资源想要获取序号低的资源,就需要先释放序号高的资源。
死锁的解除办法:
1、抢占资源。从一个或多个进程中抢占足够数量的资源,分配给死锁进程,以解除死锁状态。
2、终止(撤销)进程:将一个或多个思索进程终止(撤销),直至打破循环环路,使系统从死锁状态解脱。

Synchronized的底层原理

从语法的角度来看,synchronized的使用场景有

  1. 普通同步方法,锁是当前实例对象
  2. 静态同步方法,锁是当前类的class对象
  3. 同步方法块,锁是括号里面的对象**
    作用
    原子性:线程互斥的访问同步代码块,可以将小原子合成大原子。
    可见性:synchronized解锁之前,必须将工作内存中的数据同步到主内存,其它线程操作该变量时每次都可以看到被修改后的值。
    有序性:一个线程的加锁,必须等到其它线程将锁释放;一个线程要释放锁,首先要加锁

Synchronized和lock的区别

Lock是个接口,而synchronized是java关键字,synchronized是内置语言实现
2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象的发生;而Lock在发生异常时,如果没有主动通过unlock()去释放锁,则很有可能造成死锁现象,因此使用Lock时需要在finally块中释放锁
3)Lock可以让等待锁的线程相应中断;而synchronized不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断
4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到
5)Lock可以提高多个线程读操作的效率
在性能上来说,如果资源竞争不激烈的话,两者的性能是差不多的;而当资源竞争非常激烈(即有大量线程同时竞争)时,Lock的性能要远远优于synchronized

Synchronized和volatile的区别

反射

什么是反射

Java 反射,就是在运行状态中。
获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器等
获取任意对象的属性,并且能改变对象的属性
调用任意对象的方法
判断任意一个对象所属的类
实例化任意一个类的对象
Java 的动态就体现在这。通过反射我们可以实现动态装配,降低代码的耦合度;动态代理等。反射的过度使用会严重消耗系统资源**

什么是Java序列化,什么情况下使用

序列化:将 Java 对象转换成字节流的过程。
反序列化:将字节流转换成 Java 对象的过程。
当 Java 对象需要在网络上传输 或者 持久化存储到文件中时,就需要对 Java 对象进行序列化处理。
注意事项:
某个类可以被序列化,则其子类也可以被序列化
声明为 static 和 transient 的成员变量,不能被序列化。static 成员变量是描述类级别的属性,transient 表示临时数据
反序列化读取序列化对象的顺序要保持一致

动态代理是什么,有哪些应用

动态代理:当想要给实现了某个接口的类中的方法,加一些额外的处理。比如说加日志,加事务等。可以给这个类创建一个代理,故名思议就是创建一个新的类, 这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新类。这个代理类并不是定义好的,是动态生成的。具有解耦意义,灵活,扩展性强。
动态代理实现:首先必须定义一个接口,还要有一个InvocationHandler(将实现接口的类的对象传递给它)处理类。再有一个工具类Proxy(习惯性将其称为代理类,因为调用他的newInstance()可以产生代理对象,其实他只是一个产生代理对象的工具类)。利用到InvocationHandler,拼接代理类源码,将其编译生成代理类的二进制码,利用加载器加载,并将其实例化产生代理对象,最后返回。
动态代理的应用:Spring的AOP,加事务,加权限,加日志。

Java web

Jsp和servlet有什么区别

1.JSP的本质就是Servlet,JVM只能识别java的类,不能识别JSP的代码,Web容器将JSP的代码编译成JVM能够识别的java类。
JSP 工作原理:
JSP页面在执行的时候都会被服务器端的JSP引擎转换为Servelet(.java),然后又由JSP引擎调用Java编译器,将Servelet(.java)编译为Class文件(.class),并由Java虚拟机(JVM)解释执行。下面验证这一点:
有一个JSP页面Test.jsp,在浏览器地址栏中输入http://localhost:8080/Test.jsp,将会出现执行结果。同时在%CATALINA_HOME%/work/Catalina/localhost下多出两个文件:_Test_jsp.java和_Test_jsp.class,他们分别就是Servelet和Class文件。
2.Servlet的应用逻辑是在Java文件中,从Java代码中动态输出HTML,并且完全从表示层中的HTML里分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。JSP侧重于视图,Servlet主要用于控制逻辑。

Jsp有哪些内置对象,分别有什么作用

JSP共有以下9种基本内置组件
1、request请求对对象 客户端请求,此请求会包含来自GET/POST请求的参数通过它才能了解到客户的需求,然后做出响应。
2、response响应对象 响应客户请求的有关信息
3、session会话对象它指的是客户端与服务器的一次会话,从客户端连到服务器开始直到客户端与服务器断开连接为止。
4、out输出对象 它是JspWriter类的实例,是向客户端输出内容常用的对象
5、page页面对象 它是指向当前JSP页面本身,有点象类中的this指针,它是 java.lang.Object类的实例
6、application应用程序 它实现了用户间数据的共享,可存放全局变量。它开始于服务器的启动,直到服务器的关闭
7、exception异常对象 它是一个例外对象,当一个页面在运行过程中发生了例外,就产生这个对象。
8、pageContext页面上下文对象 它提供了对JSP页面内所有的对象及名字空间的访问
9、config配置对象 它是在一个Servlet初始化时,JSP引擎向它传递信息用的

说一下jsp的4中作用域

作用域规定的是变量的有效期限。
1、如果把变量放到pageContext里,就说明它的作用域是page,它的有效范围只在当前jsp页面里。
从把变量放到pageContext开始,到jsp页面结束,你都可以使用这个变量。
2、如果把变量放到request里,就说明它的作用域是request,它的有效范围是当前请求周期。
所谓请求周期,就是指从http请求发起,到服务器处理结束,返回响应的整个过程。在这个过程中可能使用forward的方式跳转了多个jsp页面,在这些页面里你都可以使用这个变量。
3、如果把变量放到session里,就说明它的作用域是session,它的有效范围是当前会话。
所谓当前会话,就是指从用户打开浏览器开始,到用户关闭浏览器这中间的过程。这个过程可能包含多个请求响应。也就是说,只要用户不关浏览器,服务器就有办法知道这些请求是一个人发起的,整个过程被称为一个会话(session),而放到会话中的变量,就可以在当前会话的所有请求里使用。
4、如果把变量放到application里,就说明它的作用域是application,它的有效范围是整个应用。
整个应用是指从应用启动,到应用结束。我们没有说“从服务器启动,到服务器关闭”,是因为一个服务器可能部署多个应用,当然你关闭了服务器,就会把上面所有的应用都关闭了。
application作用域里的变量,它们的存活时间是最长的,如果不进行手工删除,它们就一直可以使用。
与上述三个不同的是,application里的变量可以被所有用户共用。如果用户甲的操作修改了application中的变量,用户乙访问时得到的是修改后的值。这在其他scope中都是不会发生的,page, request, session都是完全隔离的,无论如何修改都不会影响其他人的数据。

Session和cookie的区别

1、Cookie和Session都是会话技术,Cookie是运行在客户端,Session是运行在服务器端。
2、Cookie有大小限制以及浏览器在存cookie的个数也有限制,Session是没有大小限制和服务器的内存大小有关。
3、Cookie有安全隐患,通过拦截或本地文件找得到你的cookie后可以进行攻击。
4、Session是保存在服务器端上会存在一段时间才会消失,如果session过多会增加服务器的压力。
session原理:
1、session是保存在服务器端,理论上是没有是没有限制,只要你的内存够大
2、浏览器第一次访问服务器时会创建一个session对象并返回一个JSESSIONID=ID的值,
创建一个Cookie对象key为JSSIONID,value为ID的值,将这个Cookie写回浏览器
3、浏览器在第二次访问服务器的时候携带Cookie信息JSESSIONID=ID的值,如果该JSESSIONID的session已经销毁,
那么会重新创建一个新的session再返回一个新的JSESSIONID通过Cookie返回到浏览器
4、针对一个web项目,一个浏览器是共享一个session,就算有两个web项目部署在同一个服务器上,针对两个项目的session是不同的
如:你在tomcat上同时部署了两个web项目,分别是web1、web2。当你在一个浏览器上同时访问web1时创建的session是A1访问web2时创建的session是A2。
后面你再多次访问web1使用的session还是A1,多次访问web2时使用session就是A2
5、session是基于Cookie技术实现,重启浏览器后再次访问原有的连接依然会创建一个新的session,
因为Cookie在关闭浏览器后就会消失,但是原来服务器的Session还在,只有等到了销毁的时间会自动销毁
6、如果浏览器端禁用了Cookie,那么每次访问都会创建一个新的Session,但是我们可以通过服务器端程序重写URL即可,如果页面多连接多,会增加不必要的工作量,

Session的工作原理

概念
客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。Session保存在服务器端。为了获得更高的存取速度,服务器一般把Session放在内存里。
二、生命周期
1、创建时机:
Session在用户第一次访问服务器的时候自动创建。需要注意只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session。如果尚未生成Session,也可以使用request.getSession(true)强制生成Session。
2、使用过程:
Session生成后,只要用户继续访问,服务器就会更新Session的最后访问时间,并维护该Session。用户每访问服务器一次,无论是否读写Session,服务器都认为该用户的Session“活跃(active)”了一次。
3、销毁时机:
1)Session超时:超时指的是连续一定时间服务器没有收到该Session所对应客户端的请求,并且这个时间超过了服务器设置的Session超时的最大时间。
2)程序调用HttpSession.invalidate()。
3)服务器关闭或服务停止。
原理
当客户端访问服务器时,服务器根据需求设置session,将会话信息保存在服务器上,同时将标示session的session_id传递给客户端浏览器,浏览器将这个session_id保存在内存中(还有其他的存储方式,例如写在url中),我们称之为无过期时间的cookie。浏览器关闭后,这个cookie就清掉了,它不会存在用户的cookie临时文件。以后浏览器每次请求都会额外加上这个参数值,再服务器根据这个session_id,就能取得客户端的数据状态。

客户端精致cookie,session还能使用吗

一般默认情况下,在会话中,服务器存储 session 的 sessionid 是通过 cookie 存到浏览器里。
如果浏览器禁用了 cookie,浏览器请求服务器无法携带 sessionid,服务器无法识别请求中的用户身份,session失效。
但是可以通过其他方法在禁用 cookie 的情况下,可以继续使用session。
通过url重写,把 sessionid 作为参数追加的原 url 中,后续的浏览器与服务器交互中携带 sessionid 参数。
服务器的返回数据中包含 sessionid,浏览器发送请求时,携带 sessionid 参数。
通过 Http 协议其他 header 字段,服务器每次返回时设置该 header 字段信息,浏览器中 js 读取该 header 字段,请求服务器时,js设置携带该 header 字段。

Spring mvc 和struts的区别

1.机制:spring mvc的入口是servlet,而struts2是filter(这里要指出,filter和servlet是不同的。以前认为filter是servlel的一种特殊),这样就导致了二者的机制不同,这里就牵涉到servlet和filter的区别了。
2. 性能:spring会稍微比struts快。spring mvc是基于方法的设计,而sturts是基于类,每次发一次请求都会实例一个action,每个action都会被注入属性,而spring基于方法,粒度更细,但要小心把握像在servlet控制数据一样。
spring3 mvc是方法级别的拦截,拦截到方法后根据参数上的注解,把request数据注入进去,在spring3 mvc中,一个方法对应一个request上下文。而struts2框架是类级别的拦截,每次来了请求就创建一个Action,然后调用setter getter方法把request中的数据注入;struts2实际上是通过setter getter方法与request打交道的;struts2中,一个Action对象对应一个request上下文。
3. 参数传递:struts是在接受参数的时候,可以用属性来接受参数,这就说明参数是让多个方法共享的。
4. 设计思想上:struts更加符合oop的编程思想, spring就比较谨慎,在servlet上扩展。

如何避免sql注入

严格限制 Web 应用的数据库的操作权限,给连接数据库的用户提供满足需要的最低权限,最大限度的减少注入攻击对数据库的危害
校验参数的数据格式是否合法(可以使用正则或特殊字符的判断)
对进入数据库的特殊字符进行转义处理,或编码转换
预编译 SQL(Java 中使用 PreparedStatement),参数化查询方式,避免 SQL 拼接
发布前,利用工具进行 SQL 注入检测
报错信息不要包含 SQL 信息输出到 Web 页面

什么是xss攻击如何避免

web 页面中可由用户输入的地方,如果对输入的数据转义、过滤处理
后台输出页面的时候,也需要对输出内容进行转义、过滤处理(因为攻击者可能通过其他方式把恶意脚本写入数据库)
前端对 html 标签属性、css 属性赋值的地方进行校验
XSS 攻击,即跨站脚本攻击(Cross Site Scripting),它是 web 程序中常见的漏洞。
原理
攻击者往 web 页面里插入恶意的 HTML 代码(Javascript、css、html 标签等),当用户浏览该页面时,嵌入其中的 HTML 代码会被执行,从而达到恶意攻击用户的目的。如盗取用户 cookie 执行一系列操作,破坏页面结构、重定向到其他网站等。

Java基础

Jdk和jre的区别

JDK:Java Development Kit 的简称,Java 开发工具包,提供了 Java 的开发环境和运行环境。
JRE:Java Runtime Environment 的简称,Java 运行环境,为 Java 的运行提供了所需环境。

== 和equals的区别

==
对于基本数据类型,比较的是 “值”是否相等;
对于引用类型,则比较的是所指向的对象的地址是否相同(String比较特殊)
equals
注意:equals方法不能作用于基本数据类型的变量,equals继承Object类,比较的是 是否是同一个对象
如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址值是否一样,和 ‘= =’ 一样;
如果对equals方法重写了,如String、Date等类对equals方法进行了重写,比较的是所指向的对象的内容是否相同。

Final在Java中有什么作用

final关键字可以用来修饰引用、方法和类。
1、用来修饰一个引用
如果引用为基本数据类型,则该引用为常量,该值无法修改;
如果引用为引用数据类型,比如对象、数组,则该对象、数组本身可以修改,但指向该对象或数组的地址的引用不能修改。
如果引用时类的成员变量,则必须当场赋值,否则编译会报错。
2.用来修饰一个方法
当使用final修饰方法时,这个方法将成为最终方法,无法被子类重写。但是,该方法仍然可以被继承。
3.用来修饰类
当用final修改类时,该类成为最终类,无法被继承。

Java中的math.round(-1.5)等于多少

-1,round()是Math类中的一个四舍五入的方法.Math类中对这个方法有重载,可以传入一个double类型的参数返回的是一个long类型的数值,也可以传入一个float类型的参数返回的是一个int类型的数值.

String是属于基础数据类型吗,数据类型有哪些

不属于
Java8种基础的数据类型:byte、short、char、int、long、float、double、boolean。

Java中操作字符串的都有那些类,有什么区别

String、StringBuffer、StringBuilder
String : final修饰,String类的方法都是返回new String。即对String对象的任何改变都不影响到原对象,对字符串的修改操作都会生成新的对象。
StringBuffer : 对字符串的操作的方法都加了synchronized,保证线程安全。
StringBuilder : 不保证线程安全,在方法体内需要进行字符串的修改操作,可以new StringBuilder对象,调用StringBuilder对象的append、replace、delete等方法修改字符串。

如何将字符串反转

使用StringBuffer或者StringBuilder中的reverse()方法

String str=‘i’和String str = new String(‘i’)一样吗

不一样
因为内存的分配方式不一样。String str="i"的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String(“i”)方式,则会被分到堆内存中。
Java 虚拟机会将其分配到常量池中:常量池不会重复创建对象。
分到堆内存中:堆内存会创建新的对象。

Mybatis

Mybatis #{}和${} 的区别

#可以防止Sql 注入,它会将所有传入的参数作为一个字符串来处理。
$ 则将传入的参数拼接到Sql上去执行,一般用于表名和字段名参数,$ 所对应的参数应该由服务器端提供,前端可以用参数进行选择,避免 Sql 注入的风险

Mybatis 有几种分页

一.数组分页
原理:进行数据库查询操作时,获取到数据库中所有满足条件的记录,保存在应用的临时数组中,再通过List的subList方法,获取到满足条件的所有记录。
二.借助Sql语句进行分页
在了解到通过数组分页的缺陷后,我们发现不能每次都对数据库中的所有数据都检索。然后在程序中对获取到的大量数据进行二次操作,这样对空间和性能都是极大的损耗。所以我们希望能直接在数据库语言中只检索符合条件的记录,不需要在通过程序对其作处理。这时,Sql语句分页技术横空出世。
实现:通过sql语句实现分页也是非常简单的,只是需要改变我们查询的语句就能实现了,即在sql语句后面添加limit分页语句。
三. RowBounds实现分页
原理:通过RowBounds实现分页和通过数组方式分页原理差不多,都是一次获取所有符合条件的数据,然后在内存中对大数据进行操作,实现分页效果。只是数组分页需要我们自己去实现分页逻辑,这里更加简化而已。
存在问题:一次性从数据库获取的数据可能会很多,对内存的消耗很大,可能导师性能变差,甚至引发内存溢出。
适用场景:在数据量很大的情况下,建议还是适用拦截器实现分页效果。RowBounds建议在数据量相对较小的情况下使用。
四. 拦截器分页

Mybatis逻辑分页和物理分页的区别

1.物理分页
物理分页依赖的是某一物理实体,这个物理实体就是数据库,比如MySQL数据库提供了limit关键字,程序员只需要编写带有limit关键字的SQL语句,数据库返回的就是分页结果。
2.逻辑分页
逻辑分页依赖的是程序员编写的代码。数据库返回的不是分页结果,而是全部数据,然后再由程序员通过代码获取分页数据,常用的操作是一次性从数据库中查询出全部数据并存储到List集合中,因为List集合有序,再根据索引获取指定范围的数据。

Mybatis是否支持延迟加载,原理是什么

1.mybatis仅支持关联对象association和关联集合对象collection的延迟加载,association是一对一,collection指的是一对多查询,在mybatis配置文件中可以配置lazyloadingEnable=true/false.
2.原理:使用CGLIB为目标对象建立代理对象,当调用目标对象的方法时进入拦截器方法。比如调用a.getb().getName(),拦截器方法invoke()发现a.getb()为null值,会单独发送事先保存好的查询关联b对象的sql语句,把b查询上来然后调用a.setB(b),于是a的对象的属性b就有值了,然后接着调用a.getb().getName(),这就是延迟加载的原理。

Mybatis一级缓存和二级缓存的区别

两个级别
SqlSession级别的缓存,实现在同一个会话中数据的共享
Statement级别的缓存,可以理解为缓存只对当前执行的这一个Statement有效,执行完后就会清空缓存
■ 一级缓存的生命周期和SqlSession一致
■ 设置为SqlSession级别的缓存,当执行insert/update/delete操作或close之后,缓存清空
■ MyBatis的一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据,建议设定缓存级别为Statement

二级缓存
■ SqlSessionFactory级别的缓存,实现不同会话中数据的共享,是一个全局变量
■ 存储作用域为Mapper的namespace级别
■ 可自定义存储源,如 EHCache(分布式缓存框架)
■ 当开启缓存后,数据的查询执行的流程就是二级缓存-> 一级缓存 -> 数据库
■ 不同于一级缓存,二级缓存可设置是否允许刷新和刷新频率
■ MyBatis的二级缓存不适应用于映射文件中存在多表查询的情况,如果多个映射文件对应的Sql操作都使用的是同一块缓存,那么缓存的粒度就变粗了,多个Mappernamespace下的所有操作都会对缓存使用造成影响。

Mybatis和hibernate的区别

Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句,不过mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。
Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。
Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。但是Hibernate的学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。

总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。

Mybatis有哪些执行器

ExecutorType.SIMPLE
这个类型不做特殊的事情,它只为每个语句创建一个PreparedStatement。
ExecutorType.REUSE
这种类型将重复使用PreparedStatements。
ExecutorType.BATCH
这个类型批量更新,且必要地区别开其中的select 语句,确保动作易于理解

Mybatis分页插件的实现原理

Mysql

如何获取数据库当前版本

Char和varchar的区别

HAR和VARCHAR区别
1)Char固定长度 ;
2)Varchar可变长度 ;
3)Char如果存入数据的实际长度比指定长度要小 会补空格至指定长度 如果存入的数据的实际长度大于指定长度 低版本会被截取 高版本会报错;
4)Varchar类型如果存入的数据的实际长度比指定的长度小 会缩短到实际长度 如果存入 数据的实际长度大于指定长度 低版本会被截取 高版本会报错;
5)Char 会浪费更多的存储空间 而Varchar不会 ;
6)Char效率会更高 Varchar 效率偏低 ;
7)Char和Varchar在指定时必须要制定后面的数字

Double 和float的区别

loat:单精度类型,精度是8位有效数字,取值范围是10的-38次方到10的38次方,float占用4个字节的存储空间
double:双精度类型,精度是17位有效数字,取值范围是10的-308次方到10的308次方,double占用8个字节的存储空间
若不声明的,默认小数都用double来表示,所以如果要用float的话,则应该在其后加上f
例如:float a=1.63;//会显示错误,正确的写法为float a=1.63f;
则会提示不能将double转化成float 这成为窄型转化
注意float是8位有效数字,第7位数字将会产生四舍五入
所以如果一个float变量 这样定义: float a=1.32344435; 则第7位将产生四舍五入(5及5以下的都将舍去)
一般开发中建议用double 修饰小数

内连接,左连接,右链接的区别

左连接:左边有的,右边没有的为null
右连接:左边没有的,右边有的为null
内连接:显示左边右边共有的

索引的实现原理

说一下数据库的事务隔离

原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。
原子性:就是操作的纯粹性,一个事务中的所有操作成功或失败。即事务操作的纯粹性,最小单元的完整性。
一致性:事务操作预期和结果保持一致。
隔离性:就是一种防止事务污染的一种机制。
持久性:数据一旦提交,写入磁盘。

Atomic(原子性):
事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成功,要么全部失败。
Consistency(一致性):
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是不一致的状态。
Isolation(隔离性):
事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正
确性和完整性。同时,一个事务的执行不能其它事务干扰,即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
Durability(持久性):
也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。

说一下mysql常用索引

普通索引:仅加速查询
唯一索引:加速查询 + 列值唯一(可以有null)
主键索引:加速查询 + 列值唯一(不可以有null)+ 表中只有一个
组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并
全文索引:对文本的内容进行分词,进行搜索

说一下mysql的行锁和表锁

表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般

Redis

Redis是什么,有哪些应用场景,有哪些功能

Redis是基于内存、可持久化的日志型、Key-Value数据库 高性能存储系统,并提供多种语言的API.
优点:
Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储
Redis支持数据的备份,即master-slave模式的数据备份
应用场景:
内存存储和持久化:redis支持异步将内存中的数据写到硬盘上,同时不影响继续服务
取最新N个数据的操作,如:可以将最新的10条评论的ID放在Redis的List集合里面
模拟类似于HttpSession这种需要设定过期时间的功能
发布、订阅消息系统
定时器、计数器

Redis支持的数据类型有哪些

string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
Redis的特点:
内存数据库,速度快,也支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
Redis支持数据的备份,即master-slave模式的数据备份。
支持事务
Redis的优势:
性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。(事务)
丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

Redis支持的Java的客户端有哪些

Redisson、Jedis、lettuce等等,官方推荐使用Redisson。

Redis 怎么保证缓存和数据库数据一致性

  1. 首先尝试从缓存读取,读到数据则直接返回;如果读不到,就读数据库,并将数据会写到缓存,并返回。
  2. 需要更新数据时,先更新数据库,然后把缓存里对应的数据失效掉(删掉)。
  3. 对于第一种,在读数据的时候,会自动把数据库的数据写到缓存,因此不一致自动消除.
  4. 对于第二种,数据最终变成了不相等,但他们之前在某一个时间点一定是相等的(不管你使用懒加载还是预加载的方式,在缓存加载的那一刻,它一定和数据库一致)。这种不一致,一定是由于你更新数据所引发的。前面我们讲了更新数据的策略,先更新数据库,然后删除缓存。因此,不一致的原因,一定是数据库更新了,但是删除缓存失败了。
  5. 对于第三种,情况和第二种类似,你把数据库的数据删了,但是删除缓存的时候失败了。
    因此,最终的结论是,需要解决的不一致,产生的原因是更新数据库成功,但是删除缓存失败。
    解决方案大概有以下几种:
  6. 对删除缓存进行重试,数据的一致性要求越高,我越是重试得快。
  7. 定期全量更新,简单地说,就是我定期把缓存全部清掉,然后再全量加载。
  8. 给所有的缓存一个失效期。
    第三种方案可以说是一个大杀器,任何不一致,都可以靠失效期解决,失效期越短,数据一致性越高。但是失效期越短,查数据库就会越频繁。因此失效期应该根据业务来定。
    并发不高的情况:
    读: 读redis->没有,读mysql->把mysql数据写回redis,有的话直接从redis中取;
    写: 写mysql->成功,再写redis;
    并发高的情况:
    读: 读redis->没有,读mysql->把mysql数据写回redis,有的话直接从redis中取;
    写:异步话,先写入redis的缓存,就直接返回;定期或特定动作将数据保存到mysql,可以做到多次更新,一次保存;

Redis怎么样实现分布式

Redis持久化有几种方式

众所周知,redis是一个高性能的key-value数据库,由于操作都是基于内存的,因此存取效率比其他数据要高不少,今天要说的是redis的一个比较重要的知识:持久化。
redis的高性能是基于内存的,因此自然需要考虑内存的特性,读写快,但不稳定,断电数据会全部丢失,因此redis用户数据存储时,就不得不考虑数据的持久性了,及将数据保存在磁盘上,当硬件断电,重新启动时自动从磁盘读取数据,笔者所知道的持久化方式有两种:RDB和AOF。
RDB
RDB笔者用的比较多,相信大家也是,因为RDB是redis默认开启的,可能有读者曾几何时发现硬件重启后,redis的数据竟然还在(redis版本不能太老),曾几何时,redis在更新版本后将rdb加入默认配置里面了。
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。过程比较简单,也不需要过多配置。
AOF
AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。可以说几乎是用户的每一个操作,都会记录。

相信大家看了两者的实现方式,心里大概了解了两者的基本机制和优劣势了,笔者就自己的使用心得来叙述一下两种方式

RDB方式备份的磁盘上只有一个文件,而且只保留数据原文件,因此体积不大,对于宕机恢复是相当快的,而AOF则是记录每次修改,因此体积自然比RDB大不少,对于恢复数据较慢。
RDB的性能比AOF高,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了,而AOF则需要跟踪每一个操作redis的进程,记录每一次的操作,如果操作频繁,可能会拖慢redis主进程的效率。
AOF的数据安全性要比RDB高不少,由于AOF的同步方式默认是每修改同步,如果服务器宕机,损失的数据几乎可以忽略不记,而RDB则是周期性备份数据,宕机可能会损失不少东西,因此对于一些数据安全较高的系统,AOF是较好的选择甚至是必须的选择

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值