2024 亲自经历面试的初中级java面试题(持续更新)

1 篇文章 6 订阅

面试题

基础题

集合

说一下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 key是自己定义的类,有没复写过hashcode或者equals这些方法 ?

线程安全问题

ArrayList、LinkedList、HashSet、HashMap是非线程安全的
HashTable,Vector是线程安全的;
StringBuilder是非线程安全的,StringBuffer是线程安全的。

List

ArrayList
优点: 底层数据结构是数组,查询快,增删慢。
缺点: 线程不安全,效率高
LinkedList
优点: 底层数据结构是链表,查询慢,增删快。
缺点: 线程不安全,效率高
Vector
优点: 底层数据结构是数组,查询快,增删慢。
缺点: 线程安全,效率低

ArrayList 和 LinkedList 的区别是什么?

数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。
随机访问效率:对于随机访问get和set,ArrayList觉得优于LinkedList,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。
增加和删除效率:对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。 这一点要看实际情况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。。
综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。

Set

HashSet
底层数据结构是哈希表。(无序,唯一)
如何来保证元素唯一性?
1.依赖两个方法:hashCode()和equals()
LinkedHashSet
底层数据结构是链表和哈希表。(FIFO插入有序,唯一)
1.由链表保证元素有序
2.由哈希表保证元素唯一
TreeSet
底层数据结构是红黑树。(唯一,有序)

HashSet的原理(怎么保证不重复)?

HashSet保证元素不重复是通过两部分实现的,第一步通过比较两个对象的哈希码是否相同如果相同,只能怀疑是相同对象,那么进而就会调用equals就行二次确认,如果确认完毕之后相同,那么就会排除第二个,否则的话就会插入该元素。因此,如果要保证存入对象的内容不同的时候就需要同时重写hash()和equals()方法自己定义比较的规则,一定要保证相同内容的对象的哈希码是相同的,不同对象的哈希码是不同的。

Map

Map接口有三个比较重要的实现类,分别是HashMap、TreeMap和HashTable。
TreeMap是有序的,HashMap和HashTable是无序的。
Hashtable是线程安全的,HashMap不是线程安全的。
HashMap效率较高,Hashtable效率较低。
如果对同步性或与遗留代码的兼容性没有任何要求,建议使用HashMap。 查看
Hashtable的源代码 就可以发现,除构造函数外,Hashtable的所有 public 方
法声明中都有 synchronized关键字,而HashMap的源码中则没有。
Hashtable不允许null值,HashMap允许null值(key和value都允许)
父类不同:Hashtable的父类是Dictionary,HashMap的父类是AbstractMap

hashMap

hashMap比较好的一些问题https://www.cnblogs.com/heqiyoujing/p/11143298.html

hashMap的原理(怎么存储键值对的 /怎么put进去的)

第一种(简单):
HashMap 基于 Hash 算法实现的,我们通过 put(key,value)存储,get(key)来获取。当传入 key 时,HashMap 会根据 key. hashCode() 计算出 hash 值,根据 hash 值将 value 保存在 bucket 里。下一次key传入时,同样计算hash值,并用equals比较。当计算出的 hash 值相同时,我们称之为 hash 冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的 value。当 hash 冲突的个数比较少时使用链表,否则使用红黑树。

第二种(这种听着比较牛):
调用哈希函数获取Key对应的hash值,再计算其数组下标;
如果没有出现哈希冲突,则直接放入数组;如果出现哈希冲突,则以链表的方式放在链表后面;
如果链表长度超过阀值( TREEIFY THRESHOLD==8),就把链表转成红黑树,链表长度低于6,就把红黑树转回链表;
如果结点的key已经存在,则替换其value即可;
如果集合中的键值对大于12,调用resize方法进行数组扩容。”

HashMap和HashTable区别

(1)线程安全性不同
HashMap是线程不安全的,HashTable是线程安全的,其中的方法是Synchronize的,在多线程并发的情况下,可以直接使用HashTable,但是使用HashMap时必须自己增加同步处理。
(2)是否提供contains方法
HashMap只有containsValue和containsKey方法;HashTable有contains、containsKey和containsValue三个方法,其中contains和containsValue方法功能相同。
(3)key和value是否允许null值
Hashtable中,key和value都不允许出现null值。HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。
(4)数组初始化和扩容机制
HashTable在不指定容量的情况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。
Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。

HashMap和HashSet的区别

在这里插入图片描述

HashMap和TreeMap的区别

HashMap:数组方式存储key/value,线程非安全,允许null作为key和value,key不可以重复,value允许重复,不保证元素迭代顺序是按照插入时的顺序,key的hash值是先计算key的hashcode值,然后再进行计算,每次容量扩容会重新计算所以key的hash值,会消耗资源,要求key必须重写equals和hashcode方法

TreeMap:基于红黑二叉树的NavigableMap的实现,线程非安全,不允许null,key不可以重复,value允许重复,存入TreeMap的元素应当实现Comparable接口或者实现Comparator接口,会按照排序后的顺序迭代元素,两个相比较的key不得抛出classCastException。主要用于存入元素的时候对元素进行自动排序,迭代输出的时候就按排序顺序输出

TreeSet和HashSet区别

HashSet是采用hash表来实现的。其中的元素没有按顺序排列,add()、remove()以及contains()等方法都是复杂度为O(1)的方法。
TreeSet是采用树结构实现(红黑树算法)。元素是按顺序进行排列,但是add()、remove()以及contains()等方法都是复杂度为O(log (n))的方法。它还提供了一些方法来处理排序的set,如first(), last(), headSet(), tailSet()等等。

反射

什么是反射?

反射就是程序运行期间jvm会洞悉任意一个类的属性和方法,对任意一个对象都能够访问它的属性和方法。依靠此机制,可以动态的创建一个类的对象和调用对象的方法
优点:增加灵活性,可以在运行时动态获取对象
缺点:效率低,破坏封装,通过发射可以访问类的私有方法,不安全

哪里用到反射机制?

1.JDBC中,利用反射动态加载了数据库驱动程序。
2.Web服务器中利用反射调用了Sevlet的服务方法。
3.Eclispe等开发工具利用反射动态刨析对象的类型与结构,动态提示对象的属性和方法。
4.很多框架都用到反射机制,注入属性,调用方法,如Spring。

线程

线程和进程的区别?

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

守护线程是什么?

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

创建线程有哪几种方式?

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

线程有哪些状态?

NEW 尚未启动
RUNNABLE 正在执行中
BLOCKED 阻塞的(被同步锁或者IO锁阻塞)
WAITING 永久等待状态
TIMED_WAITING 等待指定的时间重新被唤醒的状态
TERMINATED 执行完成

sleep() 和 wait() 有什么区别?

类的不同:sleep() 来自 Thread,wait() 来自 Object。
释放锁:sleep() 不释放锁;wait() 释放锁。
用法不同:sleep() 时间到会自动恢复;wait() 可以使用 notify()/notifyAll()直接唤醒。

在 Java 程序中怎么保证多线程的运行安全?

方法一:使用安全类,比如 Java. util. concurrent 下的类。比如:ConCurrentHashMap
方法二:使用自动锁 synchronized。
方法三:使用手动锁 Lock。

线程池

什么是线程池?

线程池就是事先将线程放到一个容器中,当使用线程的时候,不用再去new出一个线程,直接从线程池取出来就可以了

为什么要使用线程池?

创建线程和销毁线程的花销是比较大的,这些时间有可能比处理业务的时间还要长。这样频繁的创建线程和销毁线程,再加上业务工作线程,消耗系统资源的时间,可能导致系统资源不足。(我们可以把创建和销毁的线程的过程去掉)

线程池有什么作用?

1、提高效率 创建好一定数量的线程放在池中,等需要使用的时候就从池中拿一个,这要比需要的时候创建一个线程对象要快的多。
2、方便管理 可以编写线程池管理代码对池中的线程同一进行管理,比如说启动时有该程序创建100个线程,每当有请求的时候,就分配一个线程去工作,如果刚好并发有101个请求,那多出的这一个请求可以排队等候,避免因无休止的创建线程导致系统崩溃。

线程池都有哪几种工作队列

1、ArrayBlockingQueue
一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
2、LinkedBlockingQueue
一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列
3、SynchronousQueue
一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
4、PriorityBlockingQueue
一个具有优先级的无限阻塞队列

悲观锁、乐观锁
什么是悲观锁,乐观锁

乐观锁和悲观锁是两种思想,用于解决并发场景下的数据竞争问题。
乐观锁:乐观锁在操作数据时非常乐观,认为别人不会同时修改数据。因此乐观锁不会上锁,只是在执行更新的时候判断一下在此期间别人是否修改了数据:如果别人修改了数据则放弃操作,否则执行操作。
悲观锁:悲观锁在操作数据时比较悲观,认为别人会同时修改数据。因此操作数据时直接把数据锁住,直到操作完成后才会释放锁;上锁期间其他人不能修改数据。

实现方式

悲观锁的实现方式是加锁,加锁既可以是对代码块加锁(如Java的synchronized关键字),也可以是对数据加锁(如MySQL中的排它锁)。
乐观锁的实现方式:
CAS(Compare and Swap)比较并交换,是一种乐观锁的实现,是用非阻塞算法来代替锁定,其中 java.util.concurrent 包下的 AtomicInteger 就是借助 CAS 来实现的。
但 CAS 也不是没有任何副作用,比如著名的 ABA 问题就是 CAS 引起的。

什么是死锁?

当线程 A 持有独占锁a,并尝试去获取独占锁 b 的同时,线程 B 持有独占锁 b,并尝试获取独占锁 a 的情况下,就会发生 AB 两个线程由于互相持有对方需要的锁,而发生的阻塞现象,我们称为死锁。
避免死锁最简单的方法就是阻塞循环等待条件,将系统中所有资源设置标志位、排序,规定所有的进程申请资源必须以一定的顺序(升序或者降序)做操作来避免死锁

synchronized 和 Lock 有什么区别?

54.synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。
synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁;而 lock 需要自己加锁和释放锁,如果使用不当没有 unLock()去释放锁就会造成死锁。
通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。

java垃圾回收

GC的主要任务

1.分配内存
2.确保被引用对象的内存不被错误的回收
3.回收不再被引用的对象的内存空间

垃圾回收机制的主要解决问题

1、哪些内存需要回收?如何判断?

垃圾收集器会对堆进行回收前,确定对象中哪些是“存活”,哪些是“死亡”(不可能再被任何途径使用的对象)

判断方法:
1、引用计数算法(不推荐)
每当一个地方引用它时,计数器+1;引用失效时,计数器-1;计数值=0——不可能再被引用。
查看运行结果,会发现并没有因为两个对象互相引用就没有回收,因此引用计数算法很难解决对象之间相互矛盾循环引用的问题
2、可达性分析算法(推荐)
当一个对象到GC Roots没有任何引用链相连,即不可达时,则证明此对象时不可用的。
举例:一颗树有很多丫枝,其中一个分支断了,跟树上没有任何联系,那就说明这个分支没有用了,就可以当垃圾回收去烧了。

2、什么时候回收?

1、会在cpu空闲的时候自动进行回收
2、在堆内存存储满了之后
3、主动调用System.gc()后尝试进行回收

3、如何回收,这就牵扯到垃圾收集算法和垃圾收集器

1)标记—清除算法

这是最基础的一种算法,分为两个步骤,第一个步骤就是标记,也就是标记处所有需要回收的对象,标记完成后就进行统一的回收掉哪些带有标记的对象。这种算法优点是简单,缺点是效率问题,还有一个最大的缺点是空间问题,标记清除之后会产生大量不连续的内存碎片,当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而造成内存空间浪费。

两个阶段:标记清除
不足:效率问题;空间问题(会产生大量不连续的内存碎片)

2)复制算法

复制将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况。只是这种算法的代价是将内存缩小为原来的一半。

不足:将内存缩小为了原来的一半

3)标记—整理算法

标记整理算法与标记清除算法很相似,但最显著的区别是:标记清除算法仅对不存活的对象进行处理,剩余存活对象不做任何处理,造成内存碎片;而标记整理算法不仅对不存活对象进行处理清除,还对剩余的存活对象进行整理,重新整理,因此其不会产生内存碎片。

两个阶段:标记清除
(让存活的对象都向一端移动

3)分代收集算法

分代收集算法是一种比较智能的算法,也是现在jvm使用最多的一种算法,他本身其实不是一个新的算法,而是他会在具体的场景自动选择以上三种算法进行垃圾对象回收。
那么现在的重点就是分代收集算法中说的自动根据具体场景进行选择。这个具体场景到底是什么场景。

场景其实指的是针对jvm的哪一个区域,1.7之前jvm把内存分为三个区域:新生代,老年代,永久代
JVM会将堆内存分为两个区域,一个新生代,一个老年代。
新生代:就是创建和使用完之后立马就要被回收的对象放在里面
老年代:把一些会长期存活的对象放在里面。
永久代:用于存放一些静态文件,如Java类、方法等
了解过场景之后再结合分代收集算法得出结论:
1、在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法。只需要付出少量存活对象的复制成本就可以完成收集。
2、老年代中因为对象存活率高、没有额外空间对他进行分配担保,就必须用标记-清除或者标记-整理。

垃圾回收总结:

在这里插入图片描述

JAVA8的新特性

Lambda 表达式 − Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。

方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。

默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。

新工具 − 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。

Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。

Date Time API − 加强对日期与时间的处理。

Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。

Nashorn, JavaScript 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。

String类的常用方法有哪些?

答:https://zhidao.baidu.com/question/232452768.html

String,StringBuffer,StringBuilder的区别。

第一种回答
答:1)如果操作少量的数据用String(查看源码得知,String类的声明是:public
final,改变它的值相当于创建一个新的字符串)

2)单线程下操作大量的数据用StringBuilder

3)多线程下操作大量的数据用StringBuffer

https://www.cnblogs.com/A_ming/archive/2010/04/13/1711395.html

第二种回答
(1)StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,
(2)只是StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不安全的。
(3)在单线程程序下,StringBuilder效率更快,因为它不需要加锁,不具备多线程安全而StringBuffer则每次都需要判断锁,效率相对更低

GET 和POST 的区别?

GET 请求的数据会附在URL 之后(就是把数据放置在 HTTP 协议头中),以?分割URL 和传输数据,参数之间以&相连,如:login.action?name=zhagnsan&password=123456。POST 把提交的数据则放置在是 HTTP 包的包体中。

session与cookie的差别

Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。
Session 是存储在 web 服务器端的一块信息。session 对象存储特定用户会话所需的属性及配置信息。当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。
Cookie 和session 的不同点:
(1)无论客户端做怎样的设置,session 都能够正常工作。当客户端禁用 cookie 时将无法使用 cookie。
(2)在存储的数据量方面:session 能够存储任意的java 对象,cookie 只能存储 String 类型的对象。
(3)cookie数据存放在客户的浏览器上,session数据放在服务器上

说一下 session 的工作原理?

session 的工作原理是客户端登录完成之后,服务器会创建对应的 session,session 创建完之后,会把 session 的 id 发送给客户端,客户端再存储到浏览器中。这样客户端每次访问服务器时,都会带着 sessionid,服务器拿到 sessionid 之后,在内存找到与之对应的 session 这样就可以正常工作了。

如果客户端禁止 cookie 能实现 session 还能用吗?

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

用户登录的功能服务器需要做什么,如何保持登录状态?

第一种,cookie和session的配合使用

实现原理:当用户请求页面,一般需要先登录,用户第一次输入用户名和密码之后,前台发送post请求,后台获取用户信息,通过查询数据库来验证用户信息是否正确,如果验证通过,则会开辟一块session空间来储存用户数据,并且同时生成一个cookie字符串,由后台返回给前台,前台接收后,会把这个cookie字符串储存到浏览器的cookie空间中,这个cookie就相当于一把钥匙,可以打开后台存储对应用户信息的锁,当用户下一次请求的时候,客户端便会自动携带这个cookie去请求服务器,服务器识别后,就会读取session中的用户信息,这样用户就可以直接访问,就不需要再输入用户名密码来验证身份了。
优缺点:
优点是:提升了用户体验,cookie和session的结合使用,比直接在客户端保存用户信息要相对安全;缺点是:当服务器向浏览器传送cookie的时候,很容易被劫持,并不是绝对的安全,还有一点就是,在大型的项目中,服务器往往不只一台,如果第一次请求,用户信息被保存在了服务器1的session空间中,但是第二次请求被分流到了服务器2,这样就获取不到用户信息了,依然要重新登录,所以又引出了另一种方法:token来实现。

第二种,使用token安全令牌

实现原理:当用户请求页面,输入用户信息,服务端经过验证后,会生成一个token安全令牌(随机字符串),并返回给客户端,当客户端发送下一次请求的时候,直接携带这个token,服务端识别后,就可以直接访问页面,不需要再次登录了
优点:token只是以字符串的形式存在,不要服务器再开辟空间,并且相对更安全,即使在传输的过程中被劫持,别人也并不能破解内容,并且减少了服务器压力,减少频繁的查询数据库。

泛型是什么?

就是一种不确定的输出类型,只有需要在使用这个类的时候才能够确定出来
泛型的好处:

  1. 省略了强转的代码。2. 可以把运行时的问题提前到编译时期。

静态属性与普通属性的区别是什么?

静态属性 程序一加载时 就初始化,只有一份
实例属性 需要实例化后 才加载

throw 和 throws 的区别?

throw:是真实抛出一个异常。
throws:是声明可能会抛出一个异常。

final、finally、finalize 有什么区别?

final

final关键字主要用在三个地方:变量、方法、类;
final修饰的变量是常量不能被修改;
final修饰的方法是私有(private)不可被调用;
final修饰的类不能被继承,类中的所有成员方法都被指定为final方法;

finally

finally:是 try{} catch{} finally{} 最后一部分,表示不论发生任何情况都会执行,finally 部分可以省略,但如果 finally 部分存在,则一定会执行 finally 里面的代码。

finalize

finalize: 是 Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法。

说一下堆栈的区别?

功能方面:堆是用来存放对象的,栈是用来执行程序的。
共享性:堆是线程共享的,栈是线程私有的。
空间大小:堆大小远远大于栈。

局部变量是存放在栈中,还是存放在堆栈中?

局部变量存放在栈中。
程序bai运行中有两个存储空间可用du,一个是栈,是zhi归属于进程本身的,另外一个是堆,所有dao进程共用的。
局部变量在声明周期为函数内部,其存储空间位于栈中。当进入函数时,会对根据局部变量需求,在栈上申请一段内存空间,供局部变量使用。当局部变量生命周期结束后,在栈上释放。
由于进程的栈空间是有限的,所以要避免申请占用空间过大的局部变量,以及避免函数嵌套层数过多。这些都可能引起栈空间不够导致程序崩溃。

框架

Spring

spring的15个经典面试题:https://www.cnblogs.com/yanggb/p/11004887.html

Spring是什么?

Spring 是一个开源框架,为简化企业级应用开发而生,目标是使得Java EE应用程序的开发更加简捷,

Spring有哪些优点?

轻量级:Spring在大小和透明性方面绝对属于轻量级的,基础版本的Spring框架大约只有2MB。
控制反转(IOC):Spring使用控制反转技术实现了松耦合。依赖被注入到对象,而不是创建或寻找依赖对象。
面向切面编程(AOP): Spring支持面向切面编程,同时把应用的业务逻辑与系统的服务分离开来。
容器:Spring包含并管理应用程序对象的配置及生命周期。
MVC框架:Spring的web框架是一个设计优良的web MVC框架,很好的取代了一些web框架。
事务管理:Spring对下至本地业务上至全局业务(JAT)提供了统一的事务管理接口。
异常处理:Spring提供一个方便的API将特定技术的异常(由JDBC, Hibernate, 或JDO抛出)转化为一致的、Unchecked异常。

AOP

AOP(面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性。
用于:事务处理、日志管理、权限控制

IOC

IOC(控制反转)是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由给Spring框架来管理。IOC在其他语言中也有应用,并非Spring特有。IOC容器是Spring用来实现IOC的载体,IOC容器实际上就是一个Map(key,value),Map中存放的是各种对象。
将对象之间的相互依赖关系交给IOC容器来管理,并由IOC容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。IOC容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。在实际项目中一个Service类可能由几百甚至上千个类作为它的底层,假如我们需要实例化这个Service,可能要每次都搞清楚这个Service所有底层类的构造函数,这可能会把人逼疯。如果利用IOC的话,你只需要配置好,然后在需要的地方引用就行了,大大增加了项目的可维护性且降低了开发难度。
也可理解为工厂模式+反射,不过工厂模式的对象生成是提前在工厂类中定死的,IOC更加灵活

什么是 ORM 框架?

ORM(Object Relation Mapping)对象关系映射,是把数据库中的关系数据映射成为程序中的对象。
使用 ORM 的优点:提高了开发效率降低了开发成本、开发更简单更对象化、可移植更强。

注解是怎么生效的?

Spring会扫描所有的类包,反射获取到类里面的注解,对注解进行逻辑判断,获取到进行分析,不同的注解有不同的逻辑

Spring事务的实现方式和实现原理

Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。
基于 @Transactional 的声明式事务管理

Spring事务管理的方式有几种?

1.编程式事务:在代码中硬编码(不推荐使用)。
2.声明式事务:在配置文件中配置(推荐使用),分为基于XML的声明式事务和基于注解的声明式事务。
声明式事务管理建立在AOP之上的。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

spring 中的 bean 是线程安全的吗?

spring 中的 bean 默认是单例模式,Spring容器本身并没有提供线程安全的策略,因此是否线程安全完全取决于Bean本身的特性

spring 事务实现方式有哪些?

1.基于 xml 配置文件的方式
2.注解方式(在类上添加 @Transaction 注解)

说出Spring 或者 Springmvc中常用的5个注解,并解释含义

在这里插入图片描述

log4j日志级别

DEBUG:输出调试信息;指出细粒度信息事件对调试应用程序是非常有帮助的。
INFO: 输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程。
WARN: 输出警告信息;表明会出现潜在错误的情形。
ERROR:输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行。
FATAL:输出致命错误;指出每个严重的错误事件将会导致应用程序的退出。
ALL level:打开所有日志记录开关;是最低等级的,用于打开所有日志记录。
OFF level:关闭所有日志记录开关;是最高等级的,用于关闭所有日志记录。

按照范围从小到大排序:OFF level > FATAL > ERROR > WARN > INFO > DEBUG > ALL
level;范围大的会包含范围小的,例如日志设置为INFO级别的话则FATAL、ERROR、WARN、INFO的日志开关都是打开的,而DEBUG的日志开关将是关闭的。

Log4j建议只使用四个级别,优先级从高到低分别是 ERROR、WARN、INFO、DEBUG。

SpringBoot

SpringBoot是什么?

简单版:
用来简化spring应用的初始搭建以及开发过程
使用特定的方式来进行配置(properties或yml文件)
main方法运行
嵌入的Tomcat 无需部署war文件
简化maven配置
自动配置spring添加对应功能starter自动化配置
spring boot来简化spring应用开发,约定大于配置,去繁从简,just run就能创建一个独立的,产品级别的应用

复杂版(改):
Spring Boot 是 Spring 开源组织下的子项目,是 Spring 组件一站式解决方案,主要是简化了使用 Spring 的难度,简省了繁重的配置,提供了各种启动器,开发者能快速上手。
1、独立运行
Spring Boot而且内嵌了各种servlet容器,Tomcat、Jetty等,现在不再需要打成war包部署到容器中,Spring Boot只要打成一个可执行的jar包就能独立运行,所有的依赖包都在一个jar包内。
2、简化配置
spring-boot-starter-web启动器自动依赖其他组件,简少了maven的配置。除此之外,还提供了各种启动器,开发者能快速上手。
3、自动配置
Spring Boot能根据当前类路径下的类、jar包来自动配置bean,如添加一个spring-boot-starter-web启动器就能拥有web的功能,无需其他配置。
4、无代码生成和XML配置
Spring Boot配置过程中无代码生成,也无需XML配置文件就能完成所有配置工作,这一切都是借助于条件注解完成的,这也是Spring4.x的核心功能之一。
5、应用监控
Spring Boot提供一系列端点可以监控服务及应用,做健康检测。

Spring Boot 的配置文件有哪几种格式?它们有什么区别?

.properties 和 .yml,它们的区别主要是书写格式不同。

springboot事务是怎么去控制的 / 实现管理事务的?

我们以前没有spring的时候,都是手动报错就rollback的,现在有了spring,底层实现是用了aop 创建代理对象,就可以产生事务了

Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?

启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
@ComponentScan:Spring组件包名扫描。

SpringBoot 常用注解

1.@RestController 和 @RequestMapping 注解
2.@SpringBootApplication
3.@ResponseBody @RequestParam
4.@AutoWired
5.@PathVariable

Spring Boot 注册bean的方法

@ComponentScan
@Bean
@Import

SpringBoot注入依赖及注解

常用注解
@Service用于标注业务层组件
@Controller用于标注控制层组件
@Repository用于标注数据库访问Dao组件
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注
@Autowired,自动注入,自动从spring的上下文找到合适的bean来注入
@RestController,Spring4之后新加入的注解,原来返回json需要@ResponseBody和@Controller配合,将调用的结果直接返回给调用者。
@Value:注入Spring boot application.properties配置的属性的值。
@RequestMapping:提供路由信息,负责URL到Controller中的具体函数的映射。@RequestMapping(“/path”)表示该控制器处理所有“/path”的UR L请求。RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。
@GetMapping是一个组合注解,是@RequestMapping(method = RequestMethod.GET)的缩写。该注解将HTTP Get 映射到 特定的处理方法上。
同理PostMapping也是一个组合注解,是@RequestMapping(method = RequestMethod.POST)的缩写。
@PathVariable:获取url中的数据。
@ComponentScan 组件扫描,发现和组装一些Bean。
@EnableAutoConfiguration自动配置。
@SpringBootApplication:申明让spring boot自动给程序进行必要的配置,这个配置等同于:@Configuration ,@EnableAutoConfiguration 和 @ComponentScan 三个配置。
@Data 自动生成setter、getter方法
@Import:用来导入其他配置类。
@ImportResource:用来加载xml配置文件。
@Bean:放在方法的上面,而不是类,意思是产生一个bean,并交给spring管理。
@Inject:等价于默认的@Autowired,只是没有required属性;

Spring Boot 自动配置原理是什么?

注解 @EnableAutoConfiguration, @Configuration, @ConditionalOnClass 就是自动配置的核心,首先它得是一个配置文件,其次根据类路径下是否有这个类去自动配置。

Springboot里面哪些注解属于单例模式?

@AutoWired

@Controller和@RestController的区别

springboot常用的starter有哪些?

1.spring-boot-starter-web (嵌入tomcat和web开发需要servlet与jsp支持)
2.spring-boot-starter-data-jpa (数据库支持)
3.spring-boot-starter-data-redis (redis数据库支持)
4.spring-boot-starter-data-solr (solr搜索应用框架支持)
5.mybatis-spring-boot-starter (第三方的mybatis集成starter)

Mybatis

Mybatis模糊查询用#和$什么区别

在这里插入图片描述

简述Mybatis提供的两级缓存,以及缓存的查找顺序

参考:https://juejin.cn/post/6926161053743218695

一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,底层实际上是在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

缓存的查找顺序:二级缓存 => 一级缓存 => 数据库

一级缓存是SqlSession级别的
一级缓存默认开启
一级缓存在底层update方法也就是删除 新增 更新 时候会清除一级缓存;
一级缓存在查询的时候会先从缓存中查询,没查到去查db,然后再放入本地一级缓存;
一级缓存之前会先从二级缓存查,没查到再去一级查,一级没查到再去db查;

SpringCloud

springCloud是什么?

SpringCloud是基于SpringBoot的一套实现微服务的框架。它提供了微服务开发所需的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等组件。最重要的是,跟SpringBoot框架一起使用的话,会让你开发微服务架构的云服务非常方便。

SpringCloud五大核心组件:
服务注册发现-Netflix Eureka
配置中心 - spring cloud config
负载均衡-Netflix Ribbon
断路器 - Netflix Hystrix
路由(网关) - Netflix Zuul

spring cloud 断路器的作用是什么?

在分布式架构中,断路器模式的作用也是类似的,当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个错误响应,而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。

spring cloud 的核心组件有哪些?

Eureka:服务注册于发现。
Feign:(负载均衡)一个轻量级Restful的HTTP服务客户端,Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务
Hystrix:提供线程池,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题。
Zuul:网关管理,由 Zuul 网关转发请求给对应的服务。

高并发

高并发解决方案案例

常用的高并发处理的思路与手段

从服务端视角看高并发

服务端处理请求需要耗费服务端的资源,比如能同时开启的进程数、能同时运行的线程数、网络连接数、cpu、I/O、内存等等,由于服务端资源是有限的,那么服务端能同时处理的请求也是有限的。高并发问题的本质就是:资源的有限性

高并发带来的问题
服务端的处理和响应会越来越慢,甚至会丢弃部分请求不予处理,更严重的会导致服务端崩溃。

高并发处理的基本思路
1)从客户端看

  • 尽量减少请求数量,比如:依靠客户端自身的缓存或处理能力
  • 尽量减少对服务端资源的不必要耗费,比如:重复使用某些资源,如连接池客户端处理的基本原则就是:能不访问服务端就不要访问

2)从服务端看

  • 增加资源供给,比如:更大的网络带宽,使用更高配置的服务器,使用高性能的Web服务器,使用高性能的数据库
  • 请求分流,比如:使用集群,分布式的系统架构
  • 应用优化,比如:使用更高效的编程语言,优化处理业务逻辑的算法,优化访问数据库的SQL

基本原则:分而治之,并提高单个请求的处理速度

高并发处理的基本手段

1)客户端发出请求层面,常见的手段有:

  • 尽量利用浏览器的缓存功能,减少访问服务端,比如:js、css、图片等
  • 可以考虑使用压缩传输的功能,减少网络流量,也会提高传输速度
  • 考虑使用异步请求,分批获取数据

2)前端接收客户端请求层面,常见的手段有:

  • 动静分离,部分静态资源可以直接从Nginx返回
  • 按请求的不同,分发到不同的后端进行处理,比如:负载均衡、业务拆分访问等
  • 前面再加上一层来做多个Nginx的负载均衡,比如:LVS、F5等
  • 还可以在更前面使用CDN服务
  • 还可以对动态内容进行缓存,尽量减少访问后端服务

3)Web服务器层面,常见的手段有:

  • 使用最新的JVM,并进行配置优化
  • 对Web服务器进行配置优化,比如:调整内存数量、线程数量等
  • 提供多个能提供相同服务的Web服务器,以实现负载均衡
  • 仔细规划Web服务器上部署的应用规模
  • 对Web服务器进行集群

4)Web应用层面,常见的手段有:

  • 动态内容静态化
  • Java开发优化
  • 优化处理业务逻辑的算法
  • 合理高效的利用缓存
  • 优化访问数据库的Sql,可以考虑利用存储过程等数据库的能力
  • 合理使用多线程,加快业务处理
  • 部分业务可以考虑内存数据库,或者是进行纯内存处理
  • 尽量避免远程调用、大量I/O等耗时的操作
  • 合理规划事务等较为耗资源的操作
  • 合理使用异步处理
  • 对部分业务考虑采用预处理或者预计算的方式,减少实时计算量
  • 内部系统间的业务尽量直接调用、直接处理,减少WebService、工作流等

5)数据库层面,常见的手段有:

  • 合理选择数据库的引擎,比如Mysql的InnoDB与MyISAM引擎
  • 进行配置优化
  • 可以考虑使用存储过程来处理复杂的数据逻辑
  • 数据库集群,进行读写分离
  • 合理设计数据库的表结构、索引等
  • 分库、分表,降低单库、单表的数据量

数据库

索引

比较好的面试题 https://www.cnblogs.com/williamjie/p/11187470.html

什么是索引?(为什么需要使用索引)

【简单】:
索引其实是一种数据结构,能够帮助我们快速的检索数据库中的数据

【复杂】:
MySQL官方对索引的定义为:索引(Index)是帮助 MySQL 高效获取数据的数据结构。
打个比方,如果合理的设计且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车。
索引分单列索引和组合索引。单列索引,即一个索引只包含单个列,一个表可以有多个单列索引,但这不是组合索引。组合索引,即一个索引包含多个列。
创建索引时,你需要确保该索引是应用在 SQL 查询语句的条件(一般作为 WHERE 子句的条件)。
实际上,索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录。
上面都在说使用索引的好处,但过多的使用索引将会造成滥用。因此索引也会有它的缺点:虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。
建立索引会占用磁盘空间的索引文件。

索引具体采用的哪种数据结构

常见的MySQL主要有两种结构:Hash索引和B+ Tree索引,我们使用的是InnoDB引擎,默认的是B+树

既然你提到InnoDB使用的B+ 树的索引模型,那么你知道为什么采用B+ 树吗?这和Hash索引比较起来有什么优缺点吗?

因为Hash索引底层是哈希表,哈希表是一种以key-value存储数据的结构,所以多个数据在存储关系上是完全没有任何顺序关系的,所以,对于区间查询是无法直接通过索引查询的,就需要全表扫描。所以,哈希索引只适用于等值查询的场景。而B+ 树是一种多路平衡查询树,所以他的节点是天然有序的(左子节点小于父节点、父节点小于右子节点),所以对于范围查询的时候不需要做全表扫描

优化

SQL语句优化

  1. 不要把 select 子句写成 select *
  2. 谨慎使用模糊查询
  3. 对 order by 排序的字段设置索引,可以大大加快数据库执行的速度
  4. 少用 is null 和 is not null
  5. 尽量少用 != 运算符
  6. 尽量少用 or 运算符,因为逻辑或运算符也会让数据库跳过索引
  7. 尽量少用 in 和 not in 运算符,原因和 or 运算符一样,都属于逻辑或关系
  8. 避免条件语句中的数据类型转换
  9. 在表达式左侧使用运算符和函数都会让索引失效

分库分表

1)分库分表,如何对数据库如何进行垂直拆分或水平拆分的,用什么中间件?
综上,现在其实建议考量的,就是 Sharding-jdbc 和 Mycat,这两个都可以去考虑使用。
Sharding-jdbc 这种 client 层方案的优点在于不用部署,运维成本低,不需要代理层的二次转发请求,性能很高,但是如果遇到升级啥的需要各个系统都重新升级版本再发布,各个系统都需要耦合 Sharding-jdbc 的依赖;
Mycat 这种 proxy 层方案的缺点在于需要部署,自己运维一套中间件,运维成本高,但是好处在于对于各个项目是透明的,如果遇到升级之类的都是自己中间件那里搞就行了。

2)分库分表之后,id 主键如何处理?、、
----snowflake 雪花算法 (id work)

3)从未分库分表动态切换到分库分表上?

a、停机迁移,写个公告凌晨升级(半夜) --> 写个工具从0点开始读写数据,4点伸个懒腰下班,爽

b、双写迁移方案

读写分离

前提:读并发大
就是写一个主库,但是主库挂多个从库,然后从多个从库来读,那不就可以支撑更高的读并发压力了吗?
​ 主库将变更写入 binlog 日志,然后从库连接到主库之后,从库有一个 IO 线程,将主库的 binlog 日志拷贝到自己本地,写入一个 relay 中继日志中。接着从库中有一个 SQL 线程会从中继日志读取 binlog,然后执行 binlog 日志中的内容,也就是在自己本地再次执行一遍 SQL,这样就可以保证自己跟主库的数据是一样的。

Mysql和Oracle有什么区别?

1.Oracle收费,Mysql免费

2.单引号的处理
Mysql里可以用双引号包起字符串,Oracle里只可以用单引号包起字符串。在插入和修改字符串前必须做单引号的替换:把所有出现的一个单引号替换成两个单引号。

3.自动增长的数据类型处理
Mysql是一个自动增长的数据类型,插入数据的时候,不需要管理,它自己会自动增长,Oracle不支持自动增长的数据类型,通过建立一个自动增长的序列号来完成自动增长。

4.事物提交方式
oracle默认不自动提交,需要用户手动提交。
Mysql默认是自动提交。不支持事物。
Mysql默认自动提交,也就是你提交一个query,他就直接执行,我们可以通过
set autocommit=0 禁止自动提交
set autocommit=1 开启自动提交

group by后面还可以加什么

group by 对应的列如果如果需要加条件,一般用having。

MySQL

MySQL的事务

事务的基本要素(ACID):
1、原子性(Atomicity):
事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位
2、一致性(Consistency):
事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
3、隔离性(Isolation):
同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
4、持久性(Durability):
事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。

事务的并发问题:

脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致
幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
小结:
不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

MySQL事务隔离级别:

四种:未提交读、已提交读、可重复读、可串行化,
默认是 可重复读

在这里插入图片描述

如何做 MySQL 的性能优化?

  1. 不要把 select 子句写成 select *
  2. 谨慎使用模糊查询
  3. 对 order by 排序的字段设置索引,可以大大加快数据库执行的速度
  4. 少用 is null 和 is not null
  5. 尽量少用 != 运算符
  6. 尽量少用 or 运算符,因为逻辑或运算符也会让数据库跳过索引
  7. 尽量少用 in 和 not in 运算符,原因和 or 运算符一样,都属于逻辑或关系
  8. 避免条件语句中的数据类型转换
  9. 在表达式左侧使用运算符和函数都会让索引失效

怎么确定有没有用到索引

使用方法,在select语句前加上explain就可以了:
如:
explain select surname,first_name from a,b where a.id=b.id
细节:https://www.cnblogs.com/the-fool/p/11113996.html

Redis

Redis是什么及特点

Redis是一个内存型缓存数据库
特点:
key/value型数据库
支持丰富的数据类型(String,List,Set,ZSet,Hash)
支持持久化,内存数据,持久化到硬盘中
是单进程,单线程,所以是线程安全
可实现分布式锁

Redis除了缓存还能做什么

比如:用户登录session,网页缓存,日志系统,搜索引擎,消息队列、持久化、发布订阅系统、计时器、计数器(浏览量!)

Redis 有哪些功能?

数据缓存功能
分布式锁的功能
支持数据持久化
支持事务
支持消息队列

一个字符串类型的值能存储最大容量是多少?

512M

最大能存多大?

官方说单例能处理key:2.5亿个

线程安全吗?

Redis是个单线程程序,所以它是线程安全的。

redis 存储结构

key-value

redis的基本数据类型

string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。”

怎么理解Redis事务?

事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?

持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。
RDB(Redis Database):指定的时间间隔能对你的数据进行快照存储。
AOF(Append Only File):每一个收到的写命令都通过write函数追加到文件中。

什么情况下要用到缓存,什么数据适合缓存,使用缓存需要注意什么问题?

热点数据,不变化的数据(如省市区,分类),登录用户的token也可以用缓存,需要注意缓存和数据库的一致性,缓存击穿,缓存穿透,雪崩问题

什么是缓存击穿?怎么解决?

相较于缓存穿透,缓存击穿的目的性更强,一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。这就是缓存被击穿,只是针对其中某个key的缓存不可用而导致击穿,但是其他的key依然可以使用缓存响应。
比如热搜排行上,一个热点新闻被同时大量访问就可能导致缓存击穿。

  1. 设置热点数据永远不过期
  2. 加互斥锁(分布式锁)
    在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。保证同时刻只有一个线程访问。这样对锁的要求就十分高。

什么是缓存穿透?怎么解决?

缓存穿透:
缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中(秒杀!),于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。洪水攻击。数据库也查不到就没有缓存,就会一直与数据库访问。

解决方案:

  1. 布隆过滤器
    对所有可能查询的参数以Hash的形式存储,以便快速确定是否存在这个值,在控制层先进行拦截校验,校验不通过直接打回,减轻了存储系统的压力。
  2. 缓存空对象
    一次请求若在缓存和数据库中都没找到,就在缓存中方一个空对象用于处理后续这个请求。

什么是缓存雪崩?怎么解决?

大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。

解决方案:

  1. redis高可用
    这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群
  2. 限流降级
    这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
  3. 数据预热
    数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

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

合理设置缓存的过期时间。
新增、更改、删除数据库操作时同步更新 Redis,可以使用事物机制来保证数据的一致性。

Redis悲观锁和乐观锁

悲观锁:
执行操作前假设当前的操作肯定(或有很大几率)会被打断(悲观)。基于这个假设,我们在做操作前就会把相关资源锁定,不允许自己执行期间有其他操作干扰。
Redis不支持悲观锁。Redis作为缓存服务器使用时,以操作为主,很少写操作,相应的操作被打断的几率较少。不采用悲观锁是为了防止降低性能。

乐观锁:
执行操作前假设当前操作不会被打断(乐观)。基于这个假设,我们在做操作前不会锁定资源,万一发生了其他操作的干扰,那么本次操作将被放弃。

Redis 怎么实现分布式锁?

Redis 分布式锁其实就是在系统里面占一个“坑”,其他程序也要占“坑”的时候,占用成功了就可以继续执行,失败了就只能放弃或稍后重试。
占坑一般使用 setnx(set if not exists)指令,只允许被一个程序占有,使用完调用 del 释放锁。

Redis 分布式锁有什么缺陷?

Redis 分布式锁不能解决超时的问题,分布式锁有一个超时时间,程序的执行如果超出了锁的超时时间就会出现问题。

Redis 如何做内存优化?

尽量使用 Redis 的散列表,把相关的信息放到散列表里面存储,而不是把每个字段单独存储,这样可以有效的减少内存使用。比如将 Web 系统的用户对象,应该放到散列表里面再整体存储到 Redis,而不是把用户的姓名、年龄、密码、邮箱等字段分别设置 key 进行存储。

redis是单线程的,为什么那么快

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

Redis 一级缓存与二级缓存(不知道答案对不对)

缓存为了减轻数据库访问量;
一级比二级多了一级
一级缓存请求内存,没有的话在请求数据库;
二级缓存请求内存,没有在请求二级缓存区,没有在请求数据库;
Hibernate 二级缓存需要添加配置文件
redis 自带二级缓存
因为数据库去进行IO操作(增删更新)都需要像(唱片)的刻度一样,动刻度,非常慢,
所以需要缓存减轻数据库访问量达到什么减轻数据库压力等等作用;

在看看别人的标准答案:
hibernate一级缓存和二级缓存的区别: https://blog.csdn.net/defonds/article/details/2308972
MyBatis缓存分为一级缓存和二级缓存:https://blog.csdn.net/u014756827/article/details/52754750

Nginx

比较好的文章(内含教程视频):https://blog.csdn.net/m0_49558851/article/details/107786372

请解释一下什么是Nginx?

Nginx—Ngine X,是一款免费的、自由的、开源的、高性能HTTP服务器和反向代理服务器;也是一个IMAP、POP3、SMTP代理服务器;Nginx以其高性能、稳定性、丰富的功能、简单的配置和低资源消耗而闻名。
也就是说Nginx本身就可以托管网站(类似于Tomcat一样),进行Http服务处理,也可以作为反向代理服务器 、负载均衡器和HTTP缓存。
Nginx 解决了服务器的C10K(就是在一秒之内连接客户端的数目为10k即1万)问题。它的设计不像传统的服务器那样使用线程处理请求,而是一个更加高级的机制—事件驱动机制,是一种异步事件驱动结构。

请列举Nginx和Apache 之间的不同点

在这里插入图片描述

请解释Nginx如何处理HTTP请求

Nginx 是一个高性能的 Web 服务器,能够同时处理大量的并发请求。它结合多进程机制和异步机制 ,异步机制使用的是异步非阻塞方式 ,接下来就给大家介绍一下 Nginx 的多线程机制和异步非阻塞机制 。

1、多进程机制
服务器每当收到一个客户端时,就有 服务器主进程 ( master process )生成一个 子进程( worker process )出来和客户端建立连接进行交互,直到连接断开,该子进程就结束了。
使用进程的好处是各个进程之间相互独立,不需要加锁,减少了使用锁对性能造成影响,同时降低编程的复杂度,降低开发成本。其次,采用独立的进程,可以让进程互相之间不会影响 ,如果一个进程发生异常退出时,其它进程正常工作, master 进程则很快启动新的 worker 进程,确保服务不会中断,从而将风险降到最低。
缺点是操作系统生成一个子进程需要进行 内存复制等操作,在资源和时间上会产生一定的开销。当有大量请求时,会导致系统性能下降 。

2、异步非阻塞机制

每个工作进程 使用 异步非阻塞方式 ,可以处理 多个客户端请求 。

当某个 工作进程 接收到客户端的请求以后,调用 IO 进行处理,如果不能立即得到结果,就去 处理其他请求 (即为 非阻塞 );而 客户端
在此期间也 无需等待响应 ,可以去处理其他事情(即为 异步 )。

当 IO 返回时,就会通知此 工作进程 ;该进程得到通知,暂时 挂起 当前处理的事务去 响应客户端请求 。

请列举Nginx的一些特性

跨平台:可以在大多数Unix like 系统编译运行。而且也有Windows的移植版本。

配置异常简单:非常的简单,易上手。

非阻塞、高并发连接:数据复制时,磁盘I/O的第一阶段是非阻塞的。官方测试能支持5万并发连接,实际生产中能跑2~3万并发连接数(得益于Nginx采用了最新的epoll事件处理模型(消息队列)。

Nginx代理和后端Web服务器间无需长连接;

Nginx接收用户请求是异步的,即先将用户请求全部接收下来,再一次性发送到后端Web服务器,极大减轻后端Web服务器的压力。

发送响应报文时,是边接收来自后端Web服务器的数据,边发送给客户端。

网络依赖性低,理论上只要能够ping通就可以实施负载均衡,而且可以有效区分内网、外网流量。

支持内置服务器检测。Nginx能够根据应用服务器处理页面返回的状态码、超时信息等检测服务器是否出现故障,并及时返回错误的请求重新提交到其它节点上。

此外还有内存消耗小、成本低廉(比F5硬件负载均衡器廉价太多)、节省带宽、稳定性高等特点。

简述反向代理和正向代理

正向代理:

对于目标服务器来讲,感受不到真实的客户端,与它通信的是代理客户端,如kexueguge的软件就是一个正向代理
在这里插入图片描述
举个正向代理的例子,我(客户端)没有绿码出不了门,但是朋友(代理)有,我(客户端)让朋友(代理)去超市买瓶水,而对于超市(服务器)来讲,他们感知不到我(客户端)的存在,这就是正向代理。

反向代理:

我们将请求发送到服务器,然后服务器对我们的请求进行转发,我们只需要和代理服务器进行通信就好
在这里插入图片描述
举个反向代理例子,我(客户端)让朋友(代理)去给我买瓶水,并没有说去哪里买,反正朋友(代理)买回来了,对于我(客户端)来讲,我(客户端)感知不到超市(服务器)的存在,这就是反向代理。

使用“反向代理服务器”的优点是什么?

反向代理服务器可以隐藏源服务器的存在和特征。它充当互联网云和web服务器之间的中间层。这对于安全方面来说是很好的,特别是当您使用web托管服务时。

nginx负载均衡的几种常用方式

1、轮询(默认)
2、weight 权重
3、ip_hash指令

解决nginx负载均衡的session共享问题

1、不使用session,换用cookie
2、session存在数据库(MySQL等)中
3、session存在memcache或者redis中
4、nginx中的ip_hash技术

一些配置

# upstream 节点群
upstream backend {
  server backend1.example.com    weight=5;
  server backend2.example.com:8080;
  server unix:/tmp/backend3;
 
  server backup1.example.com:8080  backup;
  server backup2.example.com:8080  backup;
}
 
 
server {
#转发
  location / {
  # 节点地址
    proxy_pass http://backend;
  }
}

安全性

攻击

注入攻击

如何避免 SQL 注入?

使用预处理 PreparedStatement。
使用正则表达式过滤掉字符中的特殊字符。

什么是 XSS 攻击,如何避免?

XSS 攻击:即跨站脚本攻击,它是 Web 程序中常见的漏洞。原理是攻击者往 Web 页面里插入恶意的脚本代码(css 代码、Javascript 代码等),当用户浏览该页面时,嵌入其中的脚本代码会被执行,从而达到恶意攻击用户的目的,如盗取用户 cookie、破坏页面结构、重定向到其他网站等。
预防 XSS 的核心是必须对输入的数据做过滤处理。

设计模式

说一下你熟悉的设计模式?

单例模式

构造参数私有化,外部不能调用,提供一个static的接口保证被创建一次

简单的懒汉式

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}

比较完整的版本(双重校验锁+不允许指令重排):
关键词:
锁:synchronized
指令重排:volatile

public class Lazy {
    private Lazy(){
        System.out.println(Thread.currentThread().getName() + "已被创建!");
    }
    
    //所以我们要加volatile关键字告诉jvm它是易变的 不要优化策略而进行指令重排
    private static volatile Lazy instance;

    public static Lazy getInstance(){
        if(instance == null) {
            synchronized (Lazy.class) {
                if (instance == null) {
                    instance = new Lazy();//这不是一个原子型操作
                   /**
                     * 1.分配内存空间
                     * 2.执行构造方法
                     * 3.引用变量指向内存空间
                     *     虚拟机jvm执行时,可能会产生指令重排的现象 类似 132 的顺序
                     *     这将导致并发场景下的另一线程想要获取实例时,
                     *     锁之前的判空就会认为不为空了
                     *     则会返回 指向未知内存的引用(因为实际上未执行构造方法)
                     */
                }
            }
        }
        return instance;
    }
}

详细:https://www.runoob.com/design-pattern/singleton-pattern.html

工厂模式

工厂设计模式,顾名思义,就是用来生产对象的,在java中,万物皆对象,这些对象都需要创建,如果创建的时候直接new该对象,就会对该对象耦合严重,假如我们要更换对象,所有new对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则,如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目的;所以说,工厂模式最大的优点就是:解耦

观察者模式

定义了对象之间的一对多的依赖,这样一来,当一个对象改变时,它的所有的依赖者都会收到通知并自动更新。

外观模式

提供一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层的接口,让子系统更容易使用。

模版方法模式

定义了一个算法的骨架,而将一些步骤延迟到子类中,模版方法使得子类可以在不改变算法结构的情况下,重新定义算法的步骤。
状态模式:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

tomcat

默认线程数

150

Shiro

谈谈Shiro的工作流程

别人的答案:

首先shiro工作流程中又三个重要的组件,分别是Subject,SecurityManager,以及Realm。
先来介绍Subject,即为主体也就相当于目前操作系统的用户直观来说就相当于目前登录的用户。这里Subject主要是用来检测用户的登录,登录完之后就主要将工作交给SecurityManager来完成
其次就是SecurityManager主要来检测目前的用户Subject的一系列的安全操作,比如说当前用户所具备的权限,以及该用户的角色是哪一层级的,在用户执行一系列操作的时候进行授权,避免越权的操作。
之后就是Realm,他主要就是负责与数据库交互,就比如说SecurityManager来检测用户的权限是,就需要Realm从数据库中取出该用户的权限以及角色信息,之后才能方便SecurityManager来进行授权的操作。

我自己练习的项目是这样的:

启动项目的时候,首先会 配置SecurityManager的生命周期处理器,然后注册自己写的授权和认证,配置shiro拦截器,并开启注解功能
登录的时候,先判断该用户是否生成过token,来进行保存或者更新token。
发送请求的时候,首先会拦截除option外所有的请求,进行token的非空验证,如果token是空的,则返回错误信息,如果不是空的,则验证token的有效性,如果无效,则返回错误信息,有效的话进行权限验证。先给用户添加角色和权限(授权)再进行验证,无权限返回对应异常,有权限进入对应接口

在这里插入图片描述

权限的注解

@RequiresPermissions
//例子:
@RequiresPermissions({"save"}) 

Linux

拷文件

跳节点

前端

前端跨域怎么实现

1.通过jsonp跨域
2.跨域资源共享(CORS)
3.nodejs中间件代理跨域
4.nginx反向代理中设置proxy_cookie_domain

vue2生命周期

实例创建前后

dom元素挂载前后

组件数据更新前后

组件卸载前后

vue3生命周期

setup函数

页面挂载前后

数据更新前后

卸载前后

页面报错函数

新增两个 废弃两个

  • 57
    点赞
  • 458
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值