亲自经历面试的一些java面试题(吐血整理!)

tech2022-12-05  98

面试题

基础题集合说一下list,set,map的区别。hashMap key是自己定义的类,有没复写过hashcode或者equals这些方法 ?线程安全问题ListArrayList 和 LinkedList 的区别是什么? SetHashSet的原理(怎么保证不重复)? MaphashMaphashMap的原理(怎么存储键值对的 /怎么put进去的) HashMap和HashSet的区别HashMap和TreeMap的区别 反射什么是反射?哪里用到反射机制? 线程线程和进程的区别?守护线程是什么?创建线程有哪几种方式?线程有哪些状态?sleep() 和 wait() 有什么区别?在 Java 程序中怎么保证多线程的运行安全?线程池什么是线程池?为什么要使用线程池?线程池有什么作用?线程池都有哪几种工作队列 锁悲观锁、乐观锁什么是悲观锁,乐观锁实现方式 什么是死锁?synchronized 和 Lock 有什么区别? java垃圾回收GC的主要任务垃圾回收机制的主要解决问题1、哪些内存需要回收?如何判断?2、什么时候回收?3、如何回收,这就牵扯到垃圾收集算法和垃圾收集器 垃圾回收总结: JAVA8的新特性String类的常用方法有哪些?String,StringBuffer,StringBuilder的区别。session与cookie的差别说一下 session 的工作原理?如果客户端禁止 cookie 能实现 session 还能用吗?用户登录的功能服务器需要做什么,如何保持登录状态?泛型是什么?静态属性与普通属性的区别是什么?throw 和 throws 的区别?final、finally、finalize 有什么区别?finalfinallyfinalize 说一下堆栈的区别?局部变量是存放在栈中,还是存放在堆栈中? 框架SpringSpring是什么?Spring好在哪里(提供了什么)?AOPIOC什么是 ORM 框架?注解是怎么生效的?Spring事务的实现方式和实现原理Spring事务管理的方式有几种?spring 中的 bean 是线程安全的吗?spring 事务实现方式有哪些?log4j日志级别 SpringBootSpringBoot是什么?springboot事务是怎么去控制的 / 实现管理事务的?Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?SpringBoot 常用注解Spring Boot 注册bean的方法SpringBoot注入依赖及注解Spring Boot 自动配置原理是什么?Springboot里面哪些注解属于单例模式?@Controller和@RestController的区别springboot常用的starter有哪些? MybatisMybatis模糊查询用#和$什么区别 SpringCloudspringCloud是什么?spring cloud 断路器的作用是什么?spring cloud 的核心组件有哪些? 高并发高并发解决方案案例 数据库索引什么是索引?(为什么需要使用索引) 优化SQL语句优化分库分表读写分离 Mysql和Oracle有什么区别?group by后面还可以加什么MySQLmysql的默认隔离级别如何做 MySQL 的性能优化?怎么确定有没有用到索引 RedisRedis是什么Redis的特点Redis除了缓存还能做什么最大能存多大?线程安全吗?redis 存储结构redis的基本数据类型什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?Redis 有哪些功能?什么情况下要用到缓存,什么数据适合缓存,使用缓存需要注意什么问题?什么是缓存击穿?怎么解决?什么是缓存穿透?怎么解决?什么是缓存雪崩?怎么解决?怎么保证缓存和数据库数据的一致性?Redis 怎么实现分布式锁?Redis 分布式锁有什么缺陷?Redis 如何做内存优化?Redis 一级缓存与二级缓存 Nginx请解释一下什么是Nginx?请列举Nginx和Apache 之间的不同点请解释Nginx如何处理HTTP请求请列举Nginx的一些特性简述反向代理和正向代理正向代理:反向代理: 使用“反向代理服务器”的优点是什么?nginx负载均衡的几种常用方式解决nginx负载均衡的session共享问题 安全性攻击注入攻击如何避免 SQL 注入?什么是 XSS 攻击,如何避免? 设计模式说一下你熟悉的设计模式?单例模式工厂模式观察者模式外观模式模版方法模式 tomcat默认线程数 Linux拷文件跳节点前端前端跨域怎么实现

基础题

集合

说一下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这些方法 ?

线程安全问题

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

List

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

ArrayList 和 LinkedList 的区别是什么?

数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。 随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。 增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。 综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。

Set

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

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

HashSet 是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许重复的值。

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和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。主要用于存入元素的时候对元素进行自动排序,迭代输出的时候就按排序顺序输出

反射

什么是反射?

反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。

哪里用到反射机制?

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

session与cookie的差别

存储位置不同:session 存储在服务器端;cookie 存储在浏览器端。 安全性不同:cookie 安全性一般,在浏览器存储,可以被伪造和修改。 容量和个数限制:cookie 有容量限制,每个站点下的 cookie 也有个数限制。 存储的多样性:session 可以存储在 Redis 中、数据库中、应用程序中;而 cookie 只能存储在浏览器中。

说一下 session 的工作原理?

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

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

可以用,session 只是依赖 cookie 存储 sessionid,如果 cookie 被禁用了,可以使用 url 中添加 sessionid 的方式保证 session 能正常使用。

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

验证用户名和密码,通过生成token保存在服务端session中 每次请求从session中取值 存在说明已登录 不存在未登录

泛型是什么?

就是一种不确定的输出类型,只有等到运行后才知道

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

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

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是一种轻量级框架,旨在提高开发人员的开发效率以及系统的可维护性。 我们一般说的Spring框架就是Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发。这些模块是核心容器、数据访问/集成、Web、AOP(面向切面编程)、工具、消息和测试模块。比如Core Container中的Core组件是Spring所有组件的核心,Beans组件和Context组件是实现IOC和DI的基础,AOP组件用来实现面向切面编程。

Spring好在哪里(提供了什么)?

1.spring 提供 ioc 技术,容器会帮你管理依赖的对象,从而不需要自己创建和管理依赖对象了,更轻松的实现了程序的解耦。 2.spring 提供了事务支持,使得事务操作变的更加方便。 3.spring 提供了面向切片编程,这样可以更方便的处理某一类的问题。 4.更方便的框架集成,spring 可以很方便的集成其他框架,比如 MyBatis、hibernate 等。

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实现的。

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

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

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

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

spring 事务实现方式有哪些?

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

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就能创建一个独立的,产品级别的应用

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模糊查询用#和$什么区别

#可以防止SQL注入的风险(语句的拼接);但KaTeX parse error: Expected 'EOF', got '#' at position 23: …注入,大多数情况下还是经常使用#̲,一般能用#的就别用;但有些情况下必须使用 $,例:MyBatis排序时使用order by 动态参数时需要注意,用 $而不是#

SpringCloud

springCloud是什么?

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

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

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

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

spring cloud 的核心组件有哪些?

Eureka:服务注册于发现。 Feign:基于动态代理机制,根据注解和选择的机器,拼接请求 url 地址,发起请求。 Ribbon:实现负载均衡,从一个服务的多台机器中选择一台。 Hystrix:提供线程池,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题。 Zuul:网关管理,由 Zuul 网关转发请求给对应的服务。

高并发

高并发解决方案案例

流量优化:防盗链处理 前端优化:减少HTTP请求,合并css或js,添加异步请求,启用浏览器缓存和文件压缩,CDN加速,建立独立图片服务器, 服务端优化:页面静态化,并发处理,队列处理    数据库优化:数据库缓存,分库分表,分区操作,读写分离,负载均衡 web服务器优化:负载均衡,nginx反向代理,7,4层LVS软件

数据库

索引

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

MySQL官方对索引的定义为:索引(Index)是帮助 MySQL 高效获取数据的数据结构。 也就是说:索引就像书的目录一样可以非常快速的定位到书的页码。 如果向mysql发出一条sql语句请求,查询的字段没有创建索引的话,可能会导致全表扫描,这样查询效率非常低

优化

SQL语句优化

合理的建立索引; 避免整张表查询(select ); 用连接查询代替子查询; 少用in、not int、on等关键词; 少用模糊查询; 分页的时候最好前端分页查询,innodb的count()比较慢需要全表扫描; 字段类型永远越小越好,建议:布尔/枚举:tinyint,日期与时间戳:timestamp或 int,char/text/blob: 尽量用符合实际长度的varchar(n),小数及货币:移位转为int 或 decimal,IP地址:int。

分库分表

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.sql语句的扩展和灵活性 Mysql对sql语句有很多非常实用而方便的扩展,比如limit功能,insert可以一次插入多行数据,select某些管理数据可以不加from。 Oracle在这方面感觉更加稳重传统一些。

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

group by后面还可以加什么

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

MySQL

mysql的默认隔离级别

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

如何做 MySQL 的性能优化?

为搜索字段创建索引。 避免使用 select *,列出需要查询的字段。 垂直分割分表。 选择正确的存储引擎。 Redis

怎么确定有没有用到索引

使用方法,在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是一个内存型数据库 缓存 消息中间件等

Redis的特点

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

Redis除了缓存还能做什么

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

最大能存多大?

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

线程安全吗?

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

redis 存储结构

key-value

redis的基本数据类型

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

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

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

Redis 有哪些功能?

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

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

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

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

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

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

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

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

解决方案:

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

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

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

解决方案:

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

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

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

Redis 怎么实现分布式锁?

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

Redis 分布式锁有什么缺陷?

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

Redis 如何做内存优化?

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

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技术

安全性

攻击

注入攻击

如何避免 SQL 注入?

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

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

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

设计模式

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

单例模式

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

比较完整的版本(双重校验锁+不允许指令重排): 关键词: 锁: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

工厂模式

(简单工厂、抽象工厂):解耦代码。

观察者模式

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

外观模式

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

模版方法模式

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

tomcat

默认线程数

150

Linux

拷文件

跳节点

前端

前端跨域怎么实现

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

最新回复(0)