Java常见面试题

12 篇文章 0 订阅
7 篇文章 0 订阅

JavaSE

1、JAVA 8个数据类型

 byteshortintlongfloatdoublecharboolean

2、序列化作用、什么是序列化、什么是反序列化

- 序列化:把对象转换为字节序列的过程
- 反序列化:把字节序列恢复为对象的过程                                                                          

1. 方便传输,速度快,安全,常用于不同进程之间的对象传输
2. 方便存储,不管是存储成文件还是数据库,都行,存储为文件,要用可以直接反序列拿到对象

3、Java常用编码规范 和开发规范(说5条)?

- 抽象类命名使用Abstract或Base开头;
- 异常类命名使用Exception结尾;
- 测试类命名以它要测试的类的名称开始,以    Test结尾
- 常量命名全部大写,单词间用下划线隔开
- 类名遵守驼峰命名法

4、Java代码优化(至少说3条)

1. 减少对变量的重复计算-->for循环的优化
2. 底层使用可变数组的数据结构尽量指定长度
3. 字符串拼接使用StringBuilder或者StringBuffer,不需要改变的使用String
4. 在数值运算时,遇到偶数乘、除偶数倍时,尽量使用位移运算
5. 类、方法、变量尽量指定final修饰
6、returm 不要空,使用空集合
7、经常删除、添加的数据使用LinkList

5、Java创建对象的几种方式

- 通过new创建对象
- 反射创建对象
- clone创建对象
- 序列化创建对象

6、深克隆和浅克隆的区别

浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

总之深浅克隆都会在堆中新分配一块区域,区别在于对象属性引用的对象是否需要进行克隆(递归性的)。

7、Java的反射的优缺点和应用场景

- 优点: 运行期类型的判断,动态加载类,提高代码灵活度。
- 缺点:
    1:性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的 java 代码要慢很多。
    2:安全问题,让我们可以动态操作改变类的属性同时也增加了类的安全隐患。

应用场景
- 使用 JDBC 连接数据库时使用 Class.forName()通过反射加载数据库的驱动程序;
- Spring 框架的 IOC(动态加载管理 Bean)创建对象以及 AOP(动态代理)功能都和反射有联系;
- 动态配置实例的属性;

8、String和StringBuffer和StringBuilder的区别

结构是否可变线程安全效率拼接
String字符串常量不可变两个不同的空间
StringBuffer字符串变量可变安全字符串后面直接追加
StringBuilder字符串变量可变不安全字符串后面直接追加

9、String为什么是final修饰的

1. 只有当字符串是不可变的,**字符串池**才有可能实现。
2. 如果字符串是可变的,那么会引起很严重的安全问题。
3. 因为字符串是不可变的,所以是**多线程安全**的,同+
4. 一个字符串实例可以被多个线程共享。
5. 类加载器要用到字符串,不可变性提供了安全性,以便正确的类被加载。
6. 因为字符串是不可变的,所以在它创建的时候hashcode就被缓存了,不需要重新计算。

10、过滤器、拦截器、监听器的区别、加载顺序和应用场景?

- 区别
    过滤器(Filter):过滤器实际上就是对web资源进行拦截,做一些处理后再交给下一个过滤器或servlet处理
    拦截器(Interceptor):依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。拦截指定的用户请求,并进行相应的预处理与后处理
    监听器(Listener):监听器用于监听Web应用中某些对象的生命周期,并作出相应的响应处理。当监听范围的对象的状态发生变化的时候,服务器自动调用监听器对象中的方法。

- 执行顺序
    过滤器 -> 拦截器 -> aop -> 业务逻辑 ->监听器响应 --> 拦截器的post方法 -> filter的destroy 

- 应用场景
    拦截器:拦截未登录、审计日志等;
    过滤器:设置字符编码、URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等;
    监听器:统计在线人数、清除过期session。

列表

1、JAVA中集合和数组的区别

1. 数组声明了它容纳的元素的类型,而集合不声明。
2. 数组一旦固定的大小就无法改变容量了,而集合是可以动态扩展容量
3. 数组存放的类型只能是一种(基本类型/引用类型),集合存放的类型可以不是一种(不加泛型时添加的类型是Object)。
4. 数组是java语言中内置的数据类型,是线性排列的,执行效率或者类型检查都是最快的

Collectio和Collections的区别

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

ArrayList和LinkedList 和Vector的区别

类别ArrayListVectorLinkedList
优点查询快查询快增删快
缺点增删慢增删慢查询慢
继承类AbstractListAbstractListAbstractSequentialList
实现接口List, RandomAccess,Cloneable, SerializableList, RandomAccess, Cloneable, SerializableList, Deque, Cloneable, Serializable
线程安全不安全被synchronized修饰,线程安全不安全
默认容量1010
最大容量Integer.MAX_VALUE - 8Integer.MAX_VALUE - 81 << 25
扩增1.5倍,不可设增长因子2倍,可设增长因子\
数据结构数组数组双向链表
使用场景频繁查找元素(单线程)频繁查找元素(多线程)频繁增删元素(单线程)

list/set/map的区别

类别listsetmap
元素顺序有序无序无序
元素重复可以不可以键唯一,value可以重复
线程不安全不安全不安全
collection接口继承继承并列
操作增删慢,查询快增删快,查询慢

HashSet和TreeSet和LinkedHashSet区别

类别HashSetTreeSetLinkedHashSet
数据结构hash表树形链表
元素顺序无序有序有序,hash值排
null值存在一个不可以,但是可以重写Comparator接口,实现元素允许为null值一个
时间复杂度1n1

HashMap和Hashtable的比较

类别HashMapHashtable
父类AbstractMapDictionary
Iteratorfail-fast迭代器1.8前:Enumeration
线程不安全安全:Synchronize、modCount++
null值可以不可以
方法/多elments() 和contains() 两个方法
容量扩增16、2:计算效率11、2n+1:哈希冲突减少

HashMap和ConcurrentHashMap区别

类别HashMapConcurrentHashMap
底层结构1.8前:列表+链表 1.8:列表+链表+红黑树(链表长度到8)1.7:数组+链表 1.8:数组+链表+红黑树
线程不安全安全:分段锁、CAS+Synchronized
扩增默认初始值16、扩容因子:0.75(时间与空间一个平衡值),扩容一倍段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容ConcurrentHashMap
null值可以不可以

HashMap 底层实现原理、常用参数

常用参数:
默认初始化容量、最大容量、默认负载因子、树形化的最小容量

底层原理:
HashMap存储的是key-value的键值对,允许key为null,也允许value为null。HashMap实际上是一个元素为链表的数组。当添加一个元素时,先计算元素key的hash值,会根据key的hash值来确定数组的索引(确认放在哪个桶里),如果遇到索引相同的key(如果一个key的hashCode是7,一个key的hashCode是3)那么他们就会被分到一个桶中(hash冲突),HashMap会将同一个桶中的数据以链表的形式存储,但是如果发生hash冲突的概率比较高,就会导致同一个桶中的链表长度过长,遍历效率降低,所以在JDK1.8中如果链表长度到达阀值(默认是8),链表会转换成红黑树,这样会减少查询时间。

减少hash冲突
 hash值冲突是发生在put()时,hash值是通过hash(key.hashCode())来获取的,当put的元素越来越多时,难免或出现不同的key产生相同的hash值问题,也即是hash冲突,当拿到一个hash值,通过indexFor(hash, table.length)获取数组下标,先查询是否存在该hash值,若不存在,则直接以Entry<V,V>的方式存放在数组中,若存在,则再对比key是否相同,若hash值和key都相同,则替换value,若hash值相同,key不相同,则形成一个单链表,将hash值相同,key不同的元素以Entry<V,V>的方式存放在链表中,这样就解决了hash冲突,这种方法叫做分离链表法,与之类似的方法还有一种叫做 开放定址法,开放定址法师采用线性探测(从相同hash值开始,继续寻找下一个可用的槽位)hashMap是数组,长度虽然可以扩大,但用线性探测法去查询槽位查不到时怎么办?因此hashMap采用了分离链表法。

HashMap put过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d2GNU8qW-1659408738746)(HashMap-put.png)]

HashMap和LinkedHashMap的区别?

- LinkedHashMap 可以保证 HashMap 集合有序。存入的顺序和取出的顺序一致。
- HashMap 不保证顺序,即为无序的, 具有很快的访问速度。HashMap 最多只允许一条记录的键为 Null, 不支持线程的同步。

ArrayList/LinkedList /HashMap初始大小,add后是多少?(jdk1.8)

类型ArrayListLinkedListHashMap
默认10016
扩容1.50.75因子、2倍
最大Integer.MAX_VALUE - 81 << 30

多线程

1、线程的实现方式有几种?

1. 继承 Thread 类
2. 实现 Runnable 接口方式
3. 通过Callable和Future创建线程
4. 线程池创建

2、什么是线程?线程和进程的区别?

- 进程:在系统中能够独⽴运⾏并作为资源分配的基本单位,表示运⾏中的程序。系统运⾏⼀个程序就是⼀个进程从创建、运⾏到消亡的过程。

- 线程:是⼀个⽐进程更⼩的执⾏单位,能够完成进程中的⼀个功能,也被称为轻量级进程。⼀个进程在其执⾏的过程中可以产⽣多个线程。

- 线程与进程不同的是:
	同类的多个线程共享进程的堆和⽅法区资源,但每个线程有⾃⼰的程序 计数器、虚拟机栈和本地⽅法栈,所以系统在产⽣⼀个线程,或是在各个线程之间作切换⼯作 时,负担要⽐进程⼩得多

3、描述CPU和多线程的关系

	单位时间内,一个CPU只能处理一个线程,如果想要在一单位时间内处理超过一个线程是不可能的,除非是有两个CPU的实体单元。多核心技术将多个一样的CPU放置于一个封装内(或直接将两个CPU做成一个芯片),英特尔的HT技术在CPU内部仅复制必要的资源、让CPU模拟成两个线程,也就是一个实体核心,两个逻辑线程,在一单位时间内处理两个线程的工作,模拟实体双核心、双线程运作。

4、什么是线程安全/线程不安全?

- 线程安全 
	线程安全就是多线程访问时,采⽤了加锁机制,当⼀个线程访问该类的某个数据时,进⾏保护,其他线程不能进⾏访问,直到该线程读取完,其他线程才可使⽤。不会出现数据不⼀致或者数据污染。Vector 是⽤同步⽅法来实现线程安全的, ⽽和它相似ArrayList不是线程安全的。 

- 线程不安全 
	线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据,线程安全问题都是由全局变量及静态变量引起的。 若每个线程中对全局变量、静态变量只有读操作,⽽⽆写操作,⼀般来说,这个全局变量是线程安全的;若有多个线程同时执⾏写操作,⼀般都需要考虑线程同步,否则的话就可能影响线程安全。

- 线程安全问题的解决思路:
    1. 尽量不使⽤共享变量,将不必要的共享变量变成局部变量来使⽤。
    2. 使⽤synchronized关键字同步代码块,或者使⽤jdk包中提供的Lock为操作进⾏加锁。
    3. 使⽤ThreadLocal为每⼀个线程建⽴⼀个变量的副本,各个线程间独⽴操作,互不影响。

5、描述下线程的生命周期?(画图)

image-20210927162413289

6、wait、sleep、join 、yield的区别

- sleep 方法和 wait 方法都可以用来放弃 CPU 一定的时间,不同点在于如果线程持有某个对象的监视器,
	sleep 方法不会放弃这个对象的监视器
	wait方法会放弃这个对象的监视器
- join⽤于让当前执⾏线程等待join线程执⾏结束。其实现原理是不停检查join线程是否存活,如果join线程存活则让当前线程永远wait
- yield方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。 它是一个静态方法而且只保证当前线程放弃 CPU 占用而不能保证使其它线程一定 能占用 CPU,执行yield()的线程有可能在进入到暂停状态后马上又被执行                         

7、Synchronized和Lock的区别

- 相同点:Lock 能完成 synchronized 所实现的锁功能
- 不同点:Lock 有比 synchronized 更精确的线程语义和更好的性能。synchronized 会自动释放锁,而 Lock 一定要求程序员手工释放,并且必须在finally 从句中释放

8、ThreadLocal、Volatile、Synchronized 的作用和区别

- ThreadLocal:不是为了解决多线程访问共享变量,而是为每个线程创建一个单独的变量副本,提供了保持对象的方法和避免参数传递的复杂性
- volatile:主要是用来在多线程中同步变量,volatile一般用于声明简单类型变量,使得这些变量具有原子性
- synchronized:关键字保证了数据读写一致和可见性等问题。但是他是一种阻塞的线程控制方法,在关键字使用期间,所有其他线程不能使用此变量。

9、同步方法和同步块,哪个更好?

同步块,这意味着同步块之外的代码是异步执行的,这比同步整个方法更提升代码的效率。同步的范围越小越好

10、什么是死锁?如何避免死锁?

- 死锁就是两个线程相互等待对方释放对象锁。
- 避免死锁: 
    1. 避免⼀个线程同时获取多个锁 
    2. 避免⼀个线程在锁内同时占⽤多个资源,尽量保证每个锁只占⽤⼀个资源
    3. 尝试使⽤定时锁,使⽤ lock.tryLock(timeout) 来代替使⽤内部锁机制。
    4. 对于数据库锁,加锁和解锁必须在⼀个数据库连接⾥,否则会出现解锁失败的情况。

11、常见的线程池叫什么,线程池的作用是什么,有哪些参数

- 常见线程
    1. newCachedThreadPool可缓存线程池,如果线程池⻓度超过处理需要,可灵活回收空闲线程,若⽆可 回收,则新建线程。 
    2. newFixedThreadPool 定⻓线程池,可控制线程最⼤并发数,超出的线程会在队列中等待。 
    3. newScheduledThreadPool 定时线程池,⽀持定时及周期性任务执⾏。 
    4. newSingleThreadExecutor 单线程化的线程池,它只会⽤唯⼀的⼯作线程来执⾏任务,保证所有任务 按照指定顺序(FIFO, LIFO, 优先级)执⾏。
- 作用
    1. 减少了创建和销毁线程的次数,每个⼯作线程都可以被重复利⽤,可执⾏多个任务
    2. 可以根据系统的承受能⼒,调整线程池中⼯作线线程的数⽬,防⽌因为消耗过多的内存使服务器崩溃,减少在创建和销毁线程上所花的时间以及系统资源的开销
- 参数
    1. corePoolSize:核心线程数量
    2. maximumPoolSize:最大线程数量
    3. keepAliveTime:非核心线程闲置时的存活时间
    4. BlockingQueue:阻塞队列,存储等待执行的任务;
    5. ThreadFactory:线程工厂,用来创建线程
    6. RejectedExecutionHandler:队列已满,而且任务量大于最大线程数量的异常处理策略。

可选题

1、描述你所理解的锁升级、锁降级
	锁的升级、降级就是 JVM 优化` synchronized `运行的机制,当 JVM 检测 到不同的竞争状况时,会自动切换到适合的锁实现,这种切换就是锁的升级、 降级。

	在JDK 1.6后,Jvm为了提高锁的获取与释放效率对`synchronized`进行了优化,引入了 偏向锁 和 轻量级锁 ,从此以后锁的状态就有了四种(无锁、偏向锁、轻量级锁、重量级锁),并且四种状态会随着竞争的情况逐渐升级且是不可逆的过程,即不可降级,也就是说只能进行锁升级(从低级别到高级别),不能锁降级(高级别到低级别),意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。
2、描述公平锁与非公平锁的区别和优缺点
- 公平锁是指多个线程同时尝试获取同⼀把锁时,获取锁的顺序按照线程达到的顺序
- 非公平锁是多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。
    synchronized是⾮公平锁,⽽ReentrantLock的默认实现是⾮公平锁,但是也可以设置为公平锁
    - Synchronized 
        synchronized是java内置的关键字,它提供了⼀种独占的加锁⽅式。synchronized的获取和释放锁由JVM实现,⽤ 户不需要显示的释放锁,⾮常⽅便。然⽽synchronized也有⼀定的局限性:
        1. 当线程尝试获取锁的时候,如果获取不到锁会⼀直阻塞。
        2. 如果获取锁的线程进⼊休眠或者阻塞,除⾮当前线程异常,否则其他线程尝试获取锁必须⼀直等待

    - ReentrantLock
        1. ReentrantLock它是JDK 1.5之后提供的API层⾯的互斥锁,需要lock()和unlock()⽅法配合try/finally语句块来完成。
        2. 等待可中断避免,出现死锁的情况(如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获 取了锁定,就返回true,如果等待超时,返回false
        3. 公平锁与⾮公平锁多个线程等待同⼀个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁⾮公平锁,ReentrantLock默认的构造函数是创建的⾮公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好

- 公平锁
       优点:所有的线程都能得到资源,不会饿死在队列中。
       缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大。
- 非公平锁
       优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必唤醒所有线程,会减少唤起线程的数量。
       缺点:可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,导致饿死。
3、描述轻量级锁、重量级锁的区别
轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。
4、描述自旋锁和互斥锁的区别和使用场景
- 自旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线 程改变时才能进入临界区。未获取自旋锁的线程会一直尝试加锁知道成功获取自旋锁,期间不允许其他线程运行
    - 问题:自旋锁被持有时间太长,其他尝试获取自旋锁的线程会一直处于轮询自旋锁的状态,非常浪费CPU时间,这个时候让该线程睡眠会是更好的选择。
    - 使用场景:
        1. 线程等待锁的时间较短
        2. 加锁的代码被频繁访问,竞争不激烈
        3. CPU资源不紧张
        4. 多核处理器

- 互斥锁指代相互排斥,它是最基本的同步方式。互斥锁用于保护临界区,以保证任何时刻只有一个线程在执行其中的代码(假设互斥锁由多个线程共享),或者任何时刻只有一个进程在执行其中的代码。未获取互斥锁的线程会休眠,使得其他线程可以马上运行。但是线程的休眠和唤醒是相当昂贵的操作,需要大量的CPU指令,会花费很多时间。
  - 问题:如何只是锁住很短的一段时间,用来使线程休眠和唤醒的时间会比该线程睡眠的时间还要长,比自旋锁轮询的时间还长
  - 使用场景:
        1. 线程等待锁的时间较长
        2. 单核处理器
        3. 临界区有IO等耗时操作
        4. 临界区操作复杂或者有大量循环
        5. 临界区竞争非常激烈

JVM

1、Object类下有哪些方法(至少说4个)

- clone()用来另存一个当前存在的对象
- getClass()用于获得运行时的类型
- equals()用来比较两个对象的内容是否相等
- toString()方法返回该对象的字符串表示
- hashCode()方法用来返回其所在对象的物理地址

2、JVM 的主要组成部分及其作用

1. 类加载器(Class Loader):加载类文件到内存,但只管加载,只要符合文件结构就加载。
2. 执行引擎(Execution Engine):也叫解释器,负责解释命令,交由操作系统执行。
3. 本地库接口(Native Interface):本地接口的作用是融合不同的语言为java所用。
4. 运行时数据区(Runtime Data Area):就是我们常说的JVM的内存。

3、JVM内存模型划分

1. 程序计数器(线程私有):每个线程都有一个独立的程序计数器,计数器所记录的是虚拟机字节码指令当前的地址
2. 虚拟机栈(线程私有):虚拟机栈描述的是Java方法执行的内存模型。
3. 本地方法栈(线程私有):和虚拟机栈类似,但主要为虚拟机使用到的Native方法服务。
4. Java堆(线程共享):在虚拟机创建时启动,用于存放对象的实例。
5. 方法区(线程共享):用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。

4、描述深克隆和浅克隆区别

- 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于引用类型属性,仍指向原对象的内存地址。
- 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原对象的内存地址。

5、JAVA堆栈的区别

- 堆:堆的物理地址分配是不连续的,性能慢;分配的内存在运行期确认,大小不固定;堆存放对象的实例和数组;堆对于整个应用程序都是共享可见的。
- 栈:栈的物理地址分配是连续的,性能快;分配的内存大小在编译期确认,大小固定;栈存放局部变量、操作数栈、返回结果等;栈只对于线程是可见的。

6、JAVA对象的创建方式有哪些

- 反射创建对象 newInstance
- 反序列化创建对象	
- new 关键字
- clone创建对象 

7、描述新生代、老年代、持久代区别

- 新生代:所有新生成的对象首先都是放在新生代的,新生代的目标就是尽可能快速的收集掉那些生命周期短的对象。
- 老年代:在新生代中经历了多次垃圾回收后仍然存活的对象会被放到老年代中,存放的是一些生命周期较长的对象。
- 持久代:用于存放静态文件,如今Java类、方法等。

8、描述强引用、软引用、弱引用、虚引用区别

- 强引用:如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足虚拟机宁愿抛出错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
- 软引用:如果一个对象只具有软引用且内存空间足够,垃圾回收器不会回收它,内存空间不足才会回收这个对象的内存。
- 弱引用:如果一个对象只具有弱引用,垃圾回收器一旦发现这个对象,不管当前内存空间是否足够都会回收它的内存。
- 虚引用:如果一个对象只具有虚引用,它就和没有任何引用一样,在任何时候都可能被垃圾回收,用来返回一个通知。

9、如何判断一个对象应该被回收?

1. 引用计数法:给对象增加一个计数器,当引用它时,计数器就加一,当引用失效时,计数器就减一;JVM由于循环引用会导致引用计数法失效而并没有采用这种方式来判断对象是否应该被回收。
    循环引用:A类中一个属性引用了B类对象,B类中一个属性引用了A类对象。
2. 可达性分析法:通过一系列称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称之为"引用链",当一个对象到GC Roots没有任何的引用链相连时,证明此对象应该被回收。

10、描述类的生命周期

一个Java类从开始到结束整个生命周期会经历7个阶段:加载、验证、准备、解析、初始化、使用和卸载。其中验证、准备、解析三个部分又统称为连接。

11、Maven Scope作用域范围?

- compile, 缺省值,适用于所有阶段,即编译、测试、运行、打包。
- provided,类似compile,期望JDK、容器或使用者会提供这个依赖,在编译和测试时使用,在容器启动时如果包含了这个依赖,不会冲突,而是把provided作用域的exclude动作。如servlet-api-2.3.jar。
- runtime, 在运行时使用,如JDBC驱动,适用运行和测试阶段。 如plexus-utils-1.1.jar
- test, 只在测试时使用,用于编译和运行测试代码。不会随项目发布。如Junit-3.8.1.jar
- system, 类似provided,需要显式提供包含依赖的jar,Maven不会在Repository中查找它。
  1. Maven生命周期常用指令有哪些?

- CleanLifecycle 在进行真正的构建之前进行一些清理工作。
- DefaultLifecycle 构建的核心部分,编译,测试,打包,部署等等。
- SiteLifecycle 生成项目报告,站点,发布站点。
命令功能
mvn –version显示版本信息
mvn clean清理项目生产的临时文件,target下
mvn package项目打包工具,会在模块下的target目录生成jar或war等文件
mvn install将打包的jar/war文件复制到你的本地仓库中,供其他模块使用
mvn deploy将打包的文件发布到远程参考,提供其他人员进行下载依赖
mvn tomcat:run在tomcat容器中运行web应用
mvn test测试命令

加密

1、描述Linux常用命令

查看文件内容:cat	name
修改文件:	vi	name
查看文件夹下文件:ll		ls
关机:poweroff
重启:reboot

2、什么是加密?作用

加密技术是最常用的安全保密手段,利用技术手段把重要的数据变为乱码(加密)传送,到达目的地后再用相同或不同的手段还原(解密)

作用:可通过适当的钥加密技术和管理机制来保证网络的信息通信安全。

3、加密的种类、(对称加密和非对称加密的区别)

- 可逆加密算法
- 不可逆加密算法(哈希HASH)
- 国密算法

1、对称加密算法相比非对称加密算法来说,加解密的效率要高得多。但是缺陷在于对于秘钥的管理上,以及在非安全信道中通讯时,密钥交换的安全性不能保障
2、非对称加密的缺点是加解密速度要远远慢于对称加密,在某些极端情况下,甚至能比非对称加密慢上1000倍。

4、什么是盐值加密?

	加盐加密是一种对系统登录口令的加密方式,它实现的方式是将每一个口令同一个叫做”盐“(salt)的n位随机数相关联。无论何时只要口令改变,随机数就改变。随机数以未加密的方式存放在口令文件中,这样每个人都可以读。不再只保存加密过的口令,而是先将口令和随机数连接起来然后一同加密,加密后的结果放在口令文件中。

5、描述对称加密/非对称加密/不可逆 常用加密算法?

  • 可逆加密算法

    对称加密

    对称加密算法又称传统加密算法,指加密和解密使用相同密钥的加密算法。
    对称加密算法的优点在于加解密的高速度和使用长密钥时的难破解性
    
    加密过程:明文->密钥加密->密文,
    解密过程:密文->密钥解密->明文。
    
    优缺点:
    算法公开,计算量小,加密速度快,加密效率高
    双方使用相同的钥匙,安全性得不到保证
    
    注意事项:
    密钥的保密工作非常重要
    密钥要求定期更换
    

    非对称加密

    非对称加密算法又称现代加密算法。
    指加密和解密使用不同密钥的加密算法,也称为公私钥加密。假设两个用户要加密交换数据,双方交换公钥,使用时一方用对方的公钥加密,另一方即可用自己的私钥解密
    
    非对称加密是计算机通信安全的基石,保证了加密数据不会被破解。
    非对称加密算法需要两个密钥:公开密钥(publickey) 和私有密(privatekey)
    
    公开密钥和私有密钥是一对
    如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密。
    如果用私有密钥对数据进行加密,只有用对应的公开密钥才能解密。
    
    特点:
    算法强度复杂,安全性依赖于算法与密钥
    加密解密速度慢
    与对称加密算法的对比:
    
    对称加密只有一种密钥,并且是非公开的,如果要解密就得让对方知道密钥
    非对称加密有两种密钥,其中一个是公开的
    
    RSA应用场景:
    由于RSA算法的加密解密速度要比对称算法速度慢很多,在实际应用中,通常采取
    数据本身的加密和解密使用对称加密算法(AES)。用RSA算法加密并传输对称算法所需的密钥。
    
    常见的非对称加密算法:RSA、ECC(移动设备用)、Diffie-Hellman、El Gamal、DSA(数字签名用)
    
  • 不可逆加密算法(哈希HASH)

    不可逆加密算法的特征是加密过程中不需要使用密钥,输入明文后由系统直接经过加密算法处理成密文,这种加密后的数据是无法被解密的
    
    MD5,HMAC,SHA1、SHA-224、SHA-256、SHA-384,和SHA-512,其中SHA-224、SHA-256、SHA-384,和SHA-512我们可以统称为SHA2加密算法,SHA加密算法的安全性要比MD5更高,而SHA2加密算法比SHA1的要高。
    由于这些加密都是不可逆的,因此比较常用的场景就是用户密码加密,其验证过程就是通过比较两个加密后的字符串是否一样来确认身份的
    

6、int和integer的区别?

int是java提供的8种原始数据类型之一,Java为每个原始类型提供了封装类,Integer是java为int提供的封装类。Integer 是一个类,是int的扩展,定义了很多的转换方法。另外,Integer提供了多个与整数相关的操作方法,Integer中还定义了表示整数的最大值和最小值的常量。

7、this和super的区别?

1. this表示当前对象引用,调用本类(包括继承)的属性、方法、本类构造方法
2. super表示父类对象引用,调用父类的属性、方法、构造方法
3. this和super不能同时使用,因为两个都必须在首行

8、类的成员和执行顺序?

成员变量、方法、构造器、初始化块、内部类(包括接口、枚举)

1. 父类的静态初始块、静态变量
2. 子类的静态初始块、静态变量
3. 父类的非静态初始化块
4. 父类的构造方法
5. 子类的非静态初始化块
6. 子类的构造方法

9、描述最熟悉的设计模式(除了单例/工厂)

工厂模式

实体类entity :Dept

public class Dept {
    private Integer id;
    private Integer deptno;
    private String dname;
    private String location;

    public Dept() {
    }

    public Dept(Integer id, Integer deptno, String dname, String location) {
        this.id = id;
        this.deptno = deptno;
        this.dname = dname;
        this.location = location;
    }
    //toString	get	set

接口类Dao : DeptDao

public interface DeptDao {
    void insert(Dept dept);	//添加

    void del(int id);	//删除

    void update(Dept dept);	//更新

    List<Dept> findAll();	//查询所有

    Dept findById(int id);	//根据id查询
}

接口实现类DaoImpl : DeptDaoImpl

public class DeptDaoImpl implements DeptDao {
    @Override
    public void insert(Dept dept) {
        Connection con = null;
        PreparedStatement ps = null;
        try {
            con = DruidPoolUtil.getConnection();
            String sql = "insert into dept values(?,?,?,?)";
            ps = con.prepareStatement(sql);
            ps.setInt(1, dept.getId());
            ps.setInt(2, dept.getDeptno());
            ps.setString(3, dept.getDname());
            ps.setString(4, dept.getLocation());
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DruidPoolUtil.close(ps, con);
        }
    }

    @Override
    public void del(int id) {
        Connection con = null;
        PreparedStatement ps = null;
        try {
            con = DruidPoolUtil.getConnection();
            String sql = "delete from dept where id=?";
            ps = con.prepareStatement(sql);
            ps.setInt(1, id);
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DruidPoolUtil.close(ps, con);
        }
    }

    @Override
    public void update(Dept dept) {
        Connection con = null;
        PreparedStatement ps = null;
        try {
            con = DruidPoolUtil.getConnection();
            String sql = "update dept set deptno=?,dname=?,location=? where id=?";
            ps = con.prepareStatement(sql);
            ps.setInt(1, dept.getDeptno());
            ps.setString(2, dept.getDname());
            ps.setString(3, dept.getLocation());
            ps.setInt(4, dept.getId());
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DruidPoolUtil.close(ps, con);
        }
    }

    @Override
    public List<Dept> findAll() {
        List<Dept> list = new ArrayList<>();
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet resultSet = null;
        try {
            con = DruidPoolUtil.getConnection();
            String sql = "select * from  dept";
            ps = con.prepareStatement(sql);
            resultSet = ps.executeQuery();
            Dept dept = null;
            while (resultSet.next()) {
                dept = new Dept();
                dept.setId(resultSet.getInt("id"));
                dept.setDeptno(resultSet.getInt("deptno"));
                dept.setDname(resultSet.getString("dname"));
                dept.setLocation(resultSet.getString("location"));
                list.add(dept);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DruidPoolUtil.close(resultSet, ps, con);
        }
        return list;
    }

    @Override
    public Dept findById(int id) {
        Dept dept = new Dept();
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            con = DruidPoolUtil.getConnection();
            String sql = "select * from dept where id=?";
            ps = con.prepareStatement(sql);
            ps.setInt(1, id);
            rs = ps.executeQuery();
            if (rs.next()) {
                dept.setId(rs.getInt("id"));
                dept.setDeptno(rs.getInt("deptno"));
                dept.setDname(rs.getString("dname"));
                dept.setLocation(rs.getString("location"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DruidPoolUtil.close(rs, ps, con);
        }
        return dept;
    }
}

工厂类 Factory : DeptFactory

public class DeptFactory {
    /**
     * 返回符合type接口要求的对象
     *
     * @param type 接口类型
     * @return
     */
    public static <T> T getInstance(String type) {
        T obj = null;
        try {
            String className = PropertiesUtil.getVal(type);
            obj = (T) Class.forName(className).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }

业务类 Service : DeptService

public class DeptService {
    static DeptDao deptDao;

    static {
        try {
            deptDao = DeptFactory.getInstance("DeptDao");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        insert();
        del();
        update();
        findAll();
        findById();
    }

    public static void insert() {

        Dept dept = new Dept(5, 5, "运维", "北京");
        deptDao.insert(dept);
    }

    public static void del() {
        deptDao.del(5);
    }

    public static void update() {
        Dept dept = new Dept(12, 40, "后勤部", "广州");
        deptDao.update(dept);
    }

    public static void findAll() {
        List<Dept> all = deptDao.findAll();
        System.out.println(all);
    }

    public static void findById() {
        System.out.println(deptDao.findById(12));
    }
}

配置文件 Properties.properties

#接口类型=实现类的完整路径
DeptDao=com.qf.dao.impl.DeptDaoImpl
单例模式
定义

整个程序只有一个实例。这个类负责创建自己的对象,同时确保只有一个对象被创建。

一般用作在工具类的实现或创建对象需要消耗资源 连接池

懒汉模式

线程不安全

​ 当线程A拿到锁执行程序,执行到判断singleton==null,cpu不给它机会继续执行,让线程B执行,线程B会创建singleton对象,之后线程A拿到执行的机会,线程A不会重新判断,会直接创建对象,造成两个singleton对象的产生

public class LazyMan {
    private LazyMan() {
        System.out.println(Thread.currentThread().getName());
    }

    private static LazyMan lazyMan;

    /**
     * 测试多线程下是否安全
     * @return
     */
    public static LazyMan getInstance() {
        if (lazyMan == null) {
            lazyMan = new LazyMan();
        }
        return lazyMan;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                LazyMan.getInstance();
            }).start();
        }
    }
}

DCL懒汉单例

线程安全

/**
 * 双重检测锁模式的懒汉单例 DCL懒汉单例
 */
public class LazyMan {
    private LazyMan() {
        System.out.println(Thread.currentThread().getName());
    }

    /**
     * 测试多线程下是否安全
     *
     * @return
     */
    private volatile static LazyMan lazyMan;

    public static LazyMan getInstance() {
        if (lazyMan == null) {
            synchronized (LazyMan.class) {
                if (lazyMan == null) {
                    lazyMan = new LazyMan();   
                     //不是原子性操作 所有加上volatile
                     //分配内存空间、执行构造方法,初始化对象、对象指向空间
                     //A占用空间,B还未完成构造
                }
            }
        }
        return lazyMan;
    }
}
饿汉模式

线程安全,因为初始化线程只有一个,而且被锁起来,但是容易产生垃圾,因为不管是否使用,一开始就会初始化

/**
 * 饿汉单例模式
 * 资源浪费:无论是否使用,都会创建
 */
public class Hungry {
    private Hungry() {
    }

    private final static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance() {
        return HUNGRY;
    }
}
双重锁模式

线程安全,使用了volatile和synchronized双重锁机制,安全并且多线程下可以保持高性能

    private volatile static Singleton singleton;

    private Singleton() {
    }

    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

反射破坏单例

  public static void main(String[] args) throws Exception {
        LazyMan instance = LazyMan.getInstance();
        Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);
        constructor.setAccessible(true);
        LazyMan lazyMan1 = constructor.newInstance();
        System.out.println(instance);
        System.out.println(lazyMan1);
    }

三重检测

public class LazyMan {
    private LazyMan() {
        synchronized (LazyMan.class){
            if (lazyMan!=null){
                throw  new RuntimeException("反射一边站去");
            }
        }
    }
    private volatile static LazyMan lazyMan;

    public static LazyMan getInstance() {
        if (lazyMan == null) {
            synchronized (LazyMan.class) {
                if (lazyMan == null) {
                    lazyMan = new LazyMan();    //不是原子性操作
                }
            }
        }
        return lazyMan;
    }

    //反射破坏单例
    public static void main(String[] args) throws Exception {
        LazyMan instance = LazyMan.getInstance();
        Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);
        constructor.setAccessible(true);
        LazyMan lazyMan1 = constructor.newInstance();
        System.out.println(instance);
        System.out.println(lazyMan1);
    }
}

破坏

    private LazyMan() {
        synchronized (LazyMan.class) {
            if (ducai == false) {
                ducai = true;
            }
        }
    }
静态内部类单例模式

​ 静态内部类比双重检查锁定和在getInstance()方法上加同步都要好,实现了线程安全又避免了同步带来的性能影响

    private Singleton() {
    }

    public static Singleton getInstance() {
        return Inner.singleton;
    }

    private static class Inner {
        //创建实例
        private static final Singleton singleton = new Singleton();
    }
枚举

​ 这种方式不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,不过由于1.5中才加入enum特性,很少看见有人这么写过。

 public enum EnumSingle {
    INSTNCE;

    public EnumSingle getInstnce() {
        return INSTNCE;
    }
}

class Test {
    public static void main(String[] args) {
        EnumSingle enumSingle1 = EnumSingle.INSTNCE;
        EnumSingle enumSingle2 = EnumSingle.INSTNCE;
        System.out.println(enumSingle1);
        System.out.println(enumSingle2);
    }
}

报错:无默认的空构造方法

public enum EnumSingle {
    INSTNCE;

    public EnumSingle getInstnce() {
        return INSTNCE;
    }
}

class Test {
    public static void main(String[] args) throws Exception {
        EnumSingle enumSingle1 = EnumSingle.INSTNCE;
        Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(null);
        constructor.setAccessible(true);
        EnumSingle instance = constructor.newInstance();

        System.out.println(enumSingle1);
        System.out.println(instance);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pvd4Sxfr-1659408738748)(image-20211108083707030.png)]

反编译class文件

package single;


public final class EnumSingle extends Enum
{

	public static final EnumSingle INSTNCE;
	private static final EnumSingle $VALUES[];

	public static EnumSingle[] values()
	{
		return (EnumSingle[])$VALUES.clone();
	}

	public static EnumSingle valueOf(String name)
	{
		return (EnumSingle)Enum.valueOf(single/EnumSingle, name);
	}

	private EnumSingle(String s, int i)
	{
		super(s, i);
	}

	public EnumSingle getInstnce()
	{
		return INSTNCE;
	}

	static 
	{
		INSTNCE = new EnumSingle("INSTNCE", 0);
		$VALUES = (new EnumSingle[] {
			INSTNCE
		});
	}
}

使用带有参数的构造方法

class Test {
    public static void main(String[] args) throws Exception {
        EnumSingle enumSingle1 = EnumSingle.INSTNCE;
        Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);
        constructor.setAccessible(true);
        EnumSingle instance = constructor.newInstance();

        System.out.println(enumSingle1);
        System.out.println(instance);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V17Itv4P-1659408738749)(image-20211108083912604.png)]

总结
问题
  1. 如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。
  2. 如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。单例与序列化的那些事儿
修复
private static Class getClass(String classname) throws ClassNotFoundException {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    if(classLoader == null){
        classLoader = Singleton.class.getClassLoader();
        return (classLoader.loadClass(classname));
    }
}
public class Singleton implements java.io.Serializable {
    public static Singleton INSTANCE = new Singleton();
    protected Singleton() {}
    private Object readResolve() {
        return INSTANCE;
    }
}
代理模式
好处
  • 可以使真实角色的操作更加存粹,不去关注公共业务
  • 将公共业务交给代理角色,实现业务的分工
  • 公共业务扩展时,方便集中管理
缺点

一个真实角色就会产生一个代理角色,代码量增加,开发效率低

实现
真实角色
public class Host implements Rent {
    public void rent() {
        System.out.println("房东出租房子");
    }
}
公共业务
public interface Rent {
    void rent();
}
代理
public class Proxy implements Rent {
    private Host host;

    public Proxy() {
    }

    public Proxy(Host host) {
        this.host = host;
    }

    public void rent() {
        host.rent();
    }

    //代理自有的行为
    public void hetong() {
        System.out.println("签合同");
    }
}
运行
public class Client {
    public static void main(String[] args) {
        //直接找房子
        Host host = new Host();
        host.rent();

        //代理
        Proxy proxy = new Proxy(host);
        proxy.rent();
        proxy.hetong();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ll0TKKo1-1659408738749)(image-20211225172757744.png)]

mysql初级

1、描述主键、外键、候选主键、超键是什么

- 超键(super key): 在关系中能唯一标识元组的属性集
- 候选键(candidate key): 不含有多余属性的超键称为候选键。也就是在候选键中,若再删除属性,就不是键了!
- 主键(primary key): 用户选作元组标识的一个候选程序主键
- 外键(foreign key):如果关系模式R中属性K是其它模式的主键,那么k在模式R中称为外键。

2、数据库设计的三大范式

1. 第一范式(确保每列保持原子性) 
2. 第二范式(确保表中的每列都和主键相关) 
3. 第三范式(确保每列都和主键列直接相关,而不是间接相关)

3、drop,delete与truncate的区别

1. delete和truncate都是只能删除表的内容而不能删除表的结构,而drop则是删除表的结构和内容(表将不复存在);
2. delete可以删除整个表的数据也可以有选择性地删除某一条或多条数据,而truncate则是一次性删除整个表的数据;
3. 使用delete删除的表数据并没有真正被删掉,数据占用的表空间还存在,日后有需要还可以恢复;
4. 使用truncate删除的表数据会连同其占用的表空间一起直接被删掉,无法恢复。

4、SQL UNION 和 UNION ALL 区别

union会自动压缩多个结果集合中的重复结果,而union all则将所有的结果全部显示出来,不管是不是重复。

5、exists、in、any、all区别

- exists是表示子查询是否返回结果,而不管返回的具体内容
- in表示值是否存在子查询结果集中
- any是表示子查询结果中任意一个
- all表示子查询结果中的所有

6、sql语句的执行顺序

1. from 子句组装来自不同数据源的数据
2. where 子句基于指定的条件对记录行进行筛选
3. group by 子句将数据划分为多个分组
4. 使用聚集函数进行计算
5. 使用 having 子句筛选分组
6. 计算所有的表达式
7. select 的字段筛选
8. 使用 order by 对结果集进行排序。

7、count(*)和count(1)和count(id)区别

设定count(a),其中a为变量,可以为各种值,下面根据a的不同值,得出不同的count(a)的结果

1. 当a = null时,count(a)的值为0
2. 当a != null 且不是表的列名的时候,count(a)为该表的行
3. 当a是表的列名时,count(a)为该表中a列的值不等于null的行的总数,它和2)中的差值就是该表中a列值为null的行数

8、SQL语句优化(不少于6条)

1. 尽量避免在字段开头模糊查询,会导致数据库引擎放弃索引进行全表扫描
2. order by 条件要与where中条件一致,否则order by不会利用索引进行排序
3. 避免出现select *
4. 多表关联查询时,小表在前,大表在后。
5. 使用表的别名
6. 对于复杂的查询,可以使用中间临时表 暂存数据;

9、SQL常用函数有哪些?

- 数学函数
- 字符串函数
- 日期和时间函数
- 条件判断函数
- 系统信息函数
- 加密函数
- 格式化函数等

10、左连接 右连接 内连接的区别

- 左连接返回包括左表中的所有记录和右表中连接字段相等的记录;
- 右连接返回包括右表中的所有记录和左表中连接字段相等的记录;
- 内连接只返回两个表中连接字段相等的行;
- 全外连接返回左右表中所有的记录和左右表中连接字段相等的记录

11、Mysql三种常见引擎的区别

MySQL常见的三种存储引擎为InnoDB、MyISAM和MEMORY。其区别体现在事务安全、存储限制、空间使用、内存使用、插入数据的速度和对外键的支持。

1. 事务安全:InnoDB支持事务安全,MyISAM和MEMORY两个不支持。
2. 存储限制:InnoDB有64TB的存储限制,MyISAM和MEMORY要是具体情况而定。
3. 空间使用:InnoDB对空间使用程度较高,MyISAM和MEMORY对空间使用程度较低。
4. 内存使用:InnoDB和MEMORY对内存使用程度较高,MyISAM对内存使用程度较低。
5. 插入数据的速度:InnoDB插入数据的速度较低,MyISAM和MEMORY插入数据的速度较高。
6. 对外键的支持:InnoDB对外键支持情况较好,MyISAM和MEMORY两个不支持外键。

mysql高级

1、mysql explain是什么?有什么作用?描述下type

EXPLAIN :模拟Mysql优化器是如何执行SQL查询语句的,分析查询语句或表结构的性能瓶颈。

type:type是查询的访问类型。是较为重要的一个指标,结果值从最好到最坏依次是: system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL ,一般来说,得保证查询至少达到range级别,最好能达到ref。

2、mysql中有哪几种锁?表锁、行锁、页锁区别?

MySQL大致可归纳为以下3种锁:

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

3、悲观锁 for update 乐观锁(version) 的区别

- 悲观锁:悲观锁是基于数据库层面的锁, 就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block阻塞。
- 乐观锁:就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。

锁:
JDK锁: lock  synchroinized...vo...
数据库锁:悲观锁(for update)

乐观锁: 开发人员通过业务逻辑来研发的一个锁机制。(设计模式:前人经验的总结)。
表名称: account帐户表。
表设计: id,cardid,balance,version
        1   10001  120      1
  每次对该表进行改都要+1   
A和B对同一个帐号 同时转帐100元。
 update account set balance=balance-100  where cardid=1001;
 
A:
  select * from  account where cardid=1001; //version=1
 update account set balance=balance-100,version=version+1  where and version<=1;
B:
  select * from  account where cardid=1001; //version=1
 update account set balance=balance-100,version=version+1  where and version<=1;

4、什么是索引?索引的种类有哪些,描述索引的优缺点?

在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。

索引分为:普通索引、唯一索引、聚集索引、主键索引、全文索引几种。

- 优点
    1. 提高数据的搜索速度
    2. 加快表与表之间的连接速度
    3. 提高检索效率
- 缺点
    1. 需要花费时间去建立和维护索引
    2. 在创建索引的时候会占用存储空间
    3. 修改表中的数据时索引需要进行动态的维护

5、设计索引的原则?(不少于4条)

1. 选择唯一性索引
2. 为经常需要排序、分组和联合操作的字段建立索引
3. 为常作为查询条件的字段建立索引
4. 限制索引的数目
5. 尽量使用数据量少的索引
6. 数据量小的表最好不要使用索引

6、什么情况下索引会失效?(不少于4条)

1. 复合索引未用左列字段;
2. like以%开头;idea的maven在pluging出现两个clean2
3. 需要类型转换;
4. where中索引列有运算;
5. where中索引列使用了函数;

7、Btree和Hash区别

- BTree:BTree索引是最常用的mysql数据库索引算法,因为它不仅可以被用在=,>,>=,<,<=和between这些比较操作符上,而且还可以用于like操作符,只要它的查询条件是一个不以通配符开头的常量
- Hash:Hash索引只能用于对等比较,例如=,<=>(相当于=)操作符。由于是一次定位数据,不像BTree索引需要从根节点到枝节点,最后才能访问到页节点这样多次IO访问,所以检索效率远高于BTree索引

8、数据库优化方案?

1. 优化索引,sql语句,分析慢查询
2. 设计表的时候严格按照数据库设计规范来设计数据库
3. 使用缓存,把经常访问并且不需要经常变化的数据放在缓存中,能够节约磁盘IO
4. 数据量很大的时候分库分表      

9、什么是存储过程?有什么优缺点?

储过程是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来调用存储过程。

优点
1. 存储过程是预编译过的,执行效率高。
2. 存储过程的代码直接存放于数据库中,通过存储过程名直接调用,减少网络通讯。
3. 安全性高,执行存储过程需要有一定权限的用户。
4. 存储过程可以重复使用,减少数据库开发人员的工作量。

缺点
1. 调试麻烦,但是用 PL/SQL Developer 调试可以弥补这个缺点。
2. 移植性差。
3. 重新编译问题,如果带有引用关系的对象发生改变时,包将需要重新编译(可设置成运行时自动编译)。
4. 在一个程序系统中大量的使用存储过程,随着用户需求的增加会导致数据结构的变化,维护该系统很难很麻烦、而且代价很大。

10、什么是视图,视图有什么作用?

- 视图也被称作虚表,即虚拟的表,是一组数据的逻辑表示,其本质是对应于一条SELECT语句,结果集被赋予一个名字,即视图名字。
- 视图本身并不包含任何数据,它只包含映射到基表的一个查询语句,当基表数据发生变化,视图数据也随之变化。

1. 可以定制用户数据,聚焦特定的数据。 
2. 可以简化数据操作。 
3. 基表中的数据有一定的安全性 
4. 可以合并分离的数据,创建分区视图 

11、模拟面试

问题1: 如何提高SQL查询性能?
问题2:SQL语句优化方案?
问题3:描述一下你在工作中是如何进行数据库设计、或者SQL优化?
面试官: 假设现在一张表内有1亿条数据,而这个表的数据又在不断的增加,问如何提高查询效率?
   答:我的公司用的是MYSQL。在设计表的时候(因为MYSQL官方开发规范 单个表数据约“300”)。在设计表的时候不会让单个表数据量超过 范围。
     回答2:Oracle数据库。
   Oracle和MYSQL区别?
     答:Oracle:甲骨文公司收费且不开源。称为大型数据库。功能强大。权限管理很精细。
         MYSQL:开源且免费。中小型数据库。
        应用场景:Oracle:国企。(移动、联通、电信、银行【金融】电网)
        MYSQL:个人企业。互联网公司。
   
   答:MYSQL(开发规范。单表不超过100万)。而现在有1亿条数据,怎么提高查询效率?
      
   1、通过explain查看SQL的查询性能等相关数据指标。
   
   2、对关键查询条件进行索引。
    尽量用列名代替*  表有5个字段。 设计需求变更:增加了个字段。
      select * from 表名;   select id,name,age,remark,status from表名 
   3、SQL语句优化
   
   问:如果添加了索引,查询的效率还是低怎么办?
      答:表设计优化。  
        1、遵循三大范式进行表设计(建议和参考)
        2、列的个数进行优化
        3、字段的类型进行优化
        4、分表设计
          分表的理论依据是:
             冷热数据拆分:  经常被查询的数据(热数据表)。不经常被查询的数据(冷数据表)
             按照业务逻辑分表(日期、地区)。
          用户表:id,姓名,年龄,身份证号,生日,性别,身高,体重,爱好,血型...星座..
              user(热数据表)  user_info(一对一外键关联)
          
          按照业务逻辑分表(日期、地区)。
            中国移动:地区。
            日期:
            日志表。
            表名:log_year_month   log_2021_10  log_2021_11 log_2021_12
          
          表进行(地区/日期)该如何进行查询?
          还有分布式ID?主键要保证唯一。
            答:  union/union all
            答:   mybatis: #和$的区别?
             select * from log_${year}_${month}  union
             select * from log_${year}_${month} ;
        "dfdsaf" or 1=1
   select * from user where uname='admin' and pass="dfdsaf" or 1=1

    继续问:$有SQL注入的风险? 你该怎么办?
      答:前端进行校验。(安全)。


   表的查询效率还慢怎么办?
     答:缓存+非关系型数据(就是为了解决大数据和高并发产生的问题)。
     
     数据库优化方案:
       1、硬件进行优化 (增加内存、硬盘、CPU)
       2、安装优化。
    
   存储过程:
     为什么要使用存储过程?
       答:一定表的SQL查询是多表关联。(六七八九十张表关联)。
     
     存储过程:
        优点:预编译、减少网络传输
        缺点:可维护性差。
     create produce_名称(in  out)
     存储过程就是一个函数。(类似JAVA的方法)。 

JAVA调用SQL查询过程:

1、在JAVA端发送一个SQL语句:    select * from t1 left join t2 on.. leftjon t3...t8
2、数据库得到SQL语句(普通字符串)
3、【检查】和【解析】字符串(解析成数据库对象)
4、运行对象 (需要MYSQL大量的算法完成)
5、MYSQL数据存放在硬盘上(还需要序列化和反序列化过程)IO
6、将结果返回给JAVA端

存储过程应用场景(最常见)分页:

真分页: select * from 表名 limit offset,rows     (还有深分页问题)
假分页: h-ui(假分页)

MyBatis

1、什么是MyBatis?

Mybatis是的持久层框架,它对JDBC操作数据库的过程进行封装,使开发者只需要关注sql本身

2、#{}和${}的区别是什么? 应用场景

#{} : 根据参数的类型进行处理,#{} 传参在进行SQL预编译时,防止 SQL注入。
${} : 将参数取出不做任何处理,直接放入语句中。
mybatis 中优先使用 #{};当需要动态传入 表名或列名时,再考虑使用 ${} 。

3、在mapper中如何传递多个参数,都有哪几种

1. #{param1}
2. @Param("pageIndex") Integer pageIndex
3. JavaBean
4. Map

4、说出MyBatis动态标签(至少5个)

- if
- choose
- where
- forEach
- trim

5、parameterType和resultType的区别

- resultType:主要针对于从数据库中提取相应的数据出来。
- parameterType:主要针对于将信息存入到数据库中,如:insert 增加数据到数据库,update更新数据等

6、在用MyBatis插入时,如何得到数据库自增的主键值?

1. useGeneratedKeys:使用主键回填,默认false
2. last_insert_id:最后一条记录的id值

7、MyBatis批量删除/添加时,代码实现

在 Mapper 接口中传入 list 集合,在 mapper.xml 的 `<delete>/<insert>`  标签中使用 `<foreach>` 遍历 list 集合,循环多次删除数据,其实就是在` delete / insert `标签中加上 foreach 标签

8、MyBatis动态SQL执行流程

1. 读取MyBatis的配置文件
2. 加载SQL映射文件
3. 构造SqlSessionFactory会话工厂
4. 创建SqlSession会话对象
5. Executor执行器
6. 创建MappedStatement对象
7. 输入参数映射
8. 输出结果映射

原理:根据表达式的值 完成逻辑判断并动态拼接 sql 的功能。

9、JDBC和Mybatis性能效率哪个块?为什么

JDBC快,因为 mybatis 需要去映射,而映射是在内存完成的,所以效率比 jdbc 慢。

10、MyBatis框架中用到了哪些设计模式?(3个)

- 单例模式:例如 ErrorContext 和 LogFactory。
- 工厂模式:例如 SqlSessionFactory、ObjectFactory。
- 代理模式:Mybatis 实现的核心,例如 MapperProxy、ConnectionLogger。
- Builder 模式 :例如 SqlSessionFactoryBuilder、XMLConfigBuilder。

11、MyBatis 一级缓存和二级缓存的区别

- 一级缓存:每一个SqlSession对象单独分配内存,多个SqlSession内存不共享,该缓存无需手动开启,直接使用
- 二级缓存:mybatis默认不开启,需要手动开启,它是mapper级别的缓存;同一个namespace下的所有操作语句,都影响着同一个Cache,即   二级缓存被多个SqlSession共享,是一个全局的变量。

Redis

1、Redis是什么?优点和缺点?

- Redis开源,遵守BSD协议、支持网络、基于内存、分布式、可选持久性的键值对存储数据库,是一个数据结构服务器,支持不同类型的值。虽然在传统的key-value中存储关联的字符串键和字符串值,但在redis中,值不仅限于简单的字符串,还可以保存更复杂的数据结构。
- 优势:性能极高,数据是存储在内存中;丰富的数据类型,支持多种数据类型操作;所有操作都是原子性的;支持通知,持久化,key过期等特性;使用自己实现的分离器,代码量很短,读写效率非常高。
- 缺点:耗内存,占用内存过高;在线扩容难,尤其在集群场景里,当存储容量达到上限后,在线扩容会非常困难。 

2、NOSQL是什么,出现的目的和意义是什么?

NoSQL泛指非关系型的数据库,它可以作为关系型数据库的良好补充。
NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题

3、Redis有哪些应用场景?(举例即可)

可以用作数据库、缓存、秒杀、计数器、排行榜、热点数据(经常会被查询,但是不经常被修改或者删除的数据)、分布式锁、分布式ID、和消息中间件等相关场景里。

4、Redis持久化RDB和AOF区别?

RDB是redis的默认持久化机制,在指定的时间间隔内,执行了指定次数的写操作,则会将内存中的数据写入到磁盘中。即在指定目录下生成一个dump.rdb文件。Redis 重启会通过加载dump.rdb文件恢复数据。
AOF是Redis默认不开启。它的出现是为了弥补RDB的数据的不一致性,所以它采用日志的形式来记录每个写操作,并追加到文件中。Redis 重启后会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

5、Redis持久化触发条件有哪些?

1、服务器正常关闭时  ./bin/redis-cli shutdown
2、key满足一定条件,会进行快照 
 vim redis.conf搜索save
       :/save
      save 900 1     //每900秒(15分钟)至少1个key发生变化,产生快照
      save 300 10   //每300秒(5分钟)至少10个key发生变化,产生快照
      save 60 10000   //每60秒(1分钟)至少10000个key发生变化,产生快照

6、Redis内存维护策略?

当内存不足时,Redis会根据配置的缓存策略淘汰部分Keys,以保证写入成功。当无淘汰策略时或没有找到适合淘汰的Key时,Redis直接返回out of memory错误。

volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
volatile-lfu:从已设置过期的数据集中挑选一段时间内使用次数最少的数据淘汰
volatile-random:从已设置过期时间的数据集中随机选择数据淘汰
volatile-ttl:从已设置过期时间的数据集中挑选最近将要过期的数据淘汰

allkeys-lru:从数据集中挑选最近最少使用的数据淘汰
allkeys-lfu:从数据集中挑选一段时间内使用次数最少的数据淘汰
allkeys-random:从数据集中随机选择数据淘汰
no-enviction(驱逐):禁止驱逐数据,不采用任何淘汰策略。针对写操作,返回错误信息(默认即为此配置)

7、String类型常用命令(5个)、应用场景(2个)

赋值语法:SET KEY_NAME   VALUE
取值语法:GET KEY_NAME
删值语法:DEL KEY_Name 
批量写:MSET k1 v1 k2 v2 
批量读:MGET k1 k2 k3
自增:INCR KEY_Name 
自减:DECR KEY_NAME 
INCRBY KEY_Name :增量值 Incrby 命令将 key 中储存的数字加上指定的增量值 
应用场景:
1、用于保存单个字符串或JSON字符串数据
2、可以把一个图片文件的内容作为字符串来存储
3、计数器(常规key-value缓存应用。常规计数: 微博数, 粉丝数)

8、Hash类型常用命令(5个)、应用场景(2个)

赋值语法: HSET KEY  FIELD VALUE :为指定的KEY,设定FILD/VALUE  
取值语法: HGET KEY FIELD 
删除语法: HDEL KEY field1[field2]   :删除一个或多个HASH表字段 
其它语法:
HINCRBY key field increment :为哈希表 key 中的指定字段的整数值加上增量 increment 。
HINCRBYFLOAT key field increment  :为哈希表 key 中的指定字段的浮点数值加上增量 increment 。
HEXISTS key field  :查看哈希表 key 中,指定的字段是否存在
应用场景:常用于存储一个对象 / 分布式Session

9、List类型常用命令(5个)、应用场景(2个)

赋值语法: 
LPUSH key value1 [value2]  :将一个或多个值插入到列表头部(从左侧添加)
RPUSH key value1 [value2]  :将一个或多个值插入到列表头部(从右侧添加)
取值语法: 
LLEN key      :获取列表长度
LINDEX key index   :通过索引获取列表中的元素
LRANGE key start stop  :获取列表指定范围内的元素
删除语法:
LPOP key  移除并获取列表的第一个元素(从左侧删除)
RPOP key  移除并获取列表的最后一个元素(从右侧删除)
修改语法:
LSET key index value  :通过索引设置列表元素的值
LINSERT key BEFORE|AFTER world value :在列表的元素前或者后插入元素 
应用场景:
1、对数据量大的集合数据删减:列表数据显示、关注列表、粉丝列表、留言评价等…分页、热点新闻(Top5)等
2、任务队列(通常用来实现一个消息队列,而且可以确保先后顺序)

10、Set类型常用命令(5个)、应用场景(2个)

赋值语法: 
SADD key member1 [member2] :向集合添加一个或多个成员
取值语法: 
SCARD key :获取集合的成员数 
SMEMBERS key  :返回集合中的所有成员 
SISMEMBER key member  :判断member元素是否是集合 key 的成员
SRANDMEMBER key [count] :返回集合中count个随机数
删除语法:  
SREM key member1 [member2] :移除集合中一个或多个成员
SPOP key [count]  :移除并返回集合中的count个随机元素
SMOVE source destination member :将 member 元素从 source 集合移动到 destination 集合
差集语法: SDIFF(+STORE destination) key1  [key2] :返回给定所有集合的差集(存储在 destination 集合中)
交集语法: SINTER(+STORE destination) key1 [key2] :返回给定所有集合的交集(存储在 destination 集合中)
并集语法: SUNION(+STORE destination) key1 [key2] :返回所有给定集合的并集(存储在 destination 集合中)
应用场景:对两个集合间的数据进行交集、并集、差集运算

11、ZSet类型常用命令(5个)、应用场景

 赋值语法: 
 ZADD key score1 member1 [score2 member2] :向有序集合添加一个或多个成员,或者更新已存在成员的分数  
 取值语法: 
 ZCARD key  :获取有序集合的成员数
 ZCOUNT key min max :计算在有序集合中指定区间分数的成员数
 ZRANK key member :返回有序集合中指定成员的索引
 ZRANGE key start stop [WITHSCORES] :通过索引区间返回有序集合成指定区间内的成员(低到高)
 ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] :通过分数返回有序集合指定区间内的成员
 ZREVRANGE key start stop [WITHSCORES] :返回有序集中指定区间内的成员,通过索引,分数从高到底
 ZREVRANGEBYSCORE key max min [WITHSCORES] :返回有序集中指定分数区间内的成员,分数从高到低排序
 删除语法: 
 DEL key   :
 移除集合 
 ZREM key member [member ...] :移除有序集合中的一个或多个成员
 ZREMRANGEBYRANK key start stop :移除有序集合中给定的排名区间的所有成员(第一名是0)(低到高排序)
 ZREMRANGEBYSCORE key min max :移除有序集合中给定的分数区间的所有成员
 ZINCRBY  key increment member  	:增加memeber元素的分数increment,返回值是更改后的分数
 应用场景:排行榜(销量排名,积分排名等)

12、缓存穿透是什么,解决方案、原因

 缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
 解决办法: 
 1、不管数据实际上存不存在都把这个键存到缓存中(有效期设置的短一些,比如一分钟到三分钟),然后值设置为一个特定值,业务中如果获取到的结果是这个特定值,则报错返回。
 2、使用 redis 的布隆过滤器(Bloom Filter),将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
 发生原因:1.外部用户的恶意攻击
 		2.不合理的缓存失效策略
 避免方法:
 分析用户行为,对URL地址进行算法加密。

13、缓存雪崩是什么,解决方案、原因

 缓存雪崩:缓存大量失效的时候,引发大量查询数据库。
 解决办法:
 1.用锁/分布式锁或者队列串行访问
 2.缓存失效时间均匀分布
 发生原因:缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。
 避免方法:分析用户行为,尽量让失效时间点均匀分布。用加锁或者队列的方式保证缓存的单线程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。

Nginx

1、高可用、高并发、高性能概述(3遍)

- 高并发:通常是指通过设计保证系统能够同时并行处理很多请求。
- 高可用:通常来描述一个系统经过专门的设计,从而减少停工时间,而保持其服务的高度可用性。
- 高性能:是指服务响应时间快,(CPU/处理器/内存)特别是在高并发下响应时间不会急剧增加。

2、什么是Nginx

Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件代理服务器,在BSD-like 协议下发行。其特点是占有内存少,并发能力强

3、Nginx应用场景

静态配置文件处理,动静分离 、反向代理、负载均衡

4、正向代理和反向代理区别

- 正向代理是在客户端的。比如需要访问某些国外网站,我们可能需要购买vpn。并且vpn是在我们的用户浏览器端设置的(并不是在远端的服务器设置)。浏览器先访问vpn地址,vpn地址转发请求,并最后将请求结果原路返回来。
- 反向代理是作用在服务器端的,是一个虚拟ip。对于用户的一个请求,会转发到多个后端处理器中的一台来处理该具体请求

5、长连接和短连接区别

- 短连接:连接->传输数据->关闭连接,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。
- 长链接:连接->传输数据->保持连接 -> 传输数据-> ...........->直到一方关闭连接,多是客户端关闭连接。长连接指建立SOCKET连接后   不管是否使用都保持连接,但安全性较差。

6、HTTP和HTTPS的区别

1. https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2. http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3. http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4. http的连接是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

7、什么是集群

将一个应用程序,部署到多台服务器上面,然后在这些服务器通过负载均衡服务器来择优选择哪一台服务器去执行(保证高可用)

8、什么是负载均衡

将服务器接收到的请求按照规则分发的过程,称为负载均衡

9、Nginx负载均衡的规则

1. 轮询(默认) oo
2. 指定轮询几率   
3. ip_hash    
4. url_hash(第三方)  
5. fair(第三方)

10、tomcat标准目录 理解

文件或者目录名用途
bin/包含了Tomcat相关的可执行文件
conf/包含了Tomcat的相关配置文件
lib/包含了Tomcat运行时需要的相关jar包
logs/包含了Tomcat运行时相关日志输出文件
temp/Tomcat产生的一些临时文件(不用关心)
webapps/包含了要进行发布的项目
work/用来保存Tomcat运行过程中的相关文件

11、描述TCP和UDP区别

TCPUDP
是否连接面向连接面向非连接
传输可靠性可靠不可靠
应用场合少量数据传输大量数据
速度

12、描述tcp三次握手和四次挥手的全过程

  • 三次握手

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uiLdbUeH-1659408738750)(20180717202520531.png)]

    1. 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)
    2. 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
    3. 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手
    
  • 四次挥手

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IRuHzbuZ-1659408738750)(20180717204202563.png)]

    1. 在客户端,服务端之间的连接建立好之后。客户端会主动向服务端发送一段带有结束码(Fin)的询问信息(大致就像问服务端,你数据发送完了吗,我要关闭连接了)给服务端这是第一次挥手;
    2. 服务端就会回应客户端一个信息(我还没有传完了,不能关闭连接),带有确认码(ack)这是第二次挥手;
    3. 当服务器传完数据之后,就会向客户端发送一个带有结束码(Fin)的消息(我数据传输完了,可以关闭连接),这是第三次挥手;
    4. 客户端收到服务端的消息之后,会返回服务一个带有确认码(ack)的消息(好的,我知道了,那就关闭连接),这是第四次挥手。这之后,整个连接就关闭了。
    
    1)客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
    2)服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
    3)客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
    4)服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
    5)客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
    6)服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
    

Spring

1、详细描述什么是Spring?

Spring主要负责技术的整合,该框架提供了IOC和AOP的机制,基于这些特性,可以降低组件之间的耦合度, 便于系统组件的维护,扩展和替换。 主要利用spring容器管理我们程序中的controller、service、dao组件,通过容器的IOC和AOP机制,降低 组件之间的联系,利用IOC特性降低service和dao之间的关联,利用aop进行事务等共通部分的处理。

2、什么是Spring IOC?

Inverse of Controller:控制反转
- 控制:对象的创建、初始化、销毁和对象之间关系的指定
- 反转:将控制的逻辑交给第三方框架或者容器负责,当两个组件之间的关系发生改变时,只需要修改框架或者容器的配置

3、什么是Spring AOP?描述其应用场景(最少3个)

OOP(Object-Oriented Programming)面向对象编程,允许开发者定义纵向的关系,但并适用于定义横向的关系,导致了大量代码的重复,而不利于各个模块的重用。

AOP(Aspect-Oriented Programming),一般称为面向切面编程,以oop为基础,主要关注的是方面,方面 组件主要用来封装通用的逻辑,可以以低耦合的方式切入到某一批目标对象中。

- Authentication 权限
- Caching 缓存
- Context passing 内容传递
- Error handling 错误处理
- Lazy loading 懒加载
- Debugging  调试
- logging, tracing, profiling and monitoring 记录跟踪 优化 校准
- Performance optimization 性能优化
- Persistence  持久化
- Resource pooling 资源池
- Synchronization 同步
- Transactions 事务

4、AOP的通知有几个,分别是什么

1. 前置通知(Before):方面组件在目标组件之前执行
2. 后置通知(After):方面组件在目标组件之后执行,目标组件方法没有抛出异常才会执行方面组件
3. 最终通知(After-returning ):方面组件在目标组件之后执行,目标组件不管是否发生异常都会执行方面组件
4. 异常通知(After-throwing):方面组件在目标组件抛出异常之后执行
5. 环绕通知(Around):方面组件在目标组件之前和之后执行

5、描述Spring Bean的作用域?

Spring框架支持以下五种bean的作用域:

- singleton : 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,bean作用域范围的默认值。
- prototype:每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()。

以下作用域仅在基于web的Spring ApplicationContext情形下有效:

- request:每次HTTP请求都会创建一个新的Bean,该作用域仅适用于web的Spring WebApplicationContext环境。
- session:同一个HTTP Session共享一个Bean,不同Session使用不同的Bean。该作用域仅适用于web的Spring WebApplicationContext环境。
- application:限定一个Bean的作用域为`ServletContext`的生命周期。该作用域仅适用于web的Spring WebApplicationContext环境。

6、Spring Bean 注入有几种方式?

- setter注入
- 构造方法注入
- 自动装配

7、SpringBean 生命周期?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KCUiwmUR-1659408738751)(image-20211229100005617.png)]

1. 实例化一个 Bean,也就是我们通常说的 new
2. 按照 Spring 上下文对实例化的 Bean 进行配置,也就是 IOC 注入
3. 如果这个Bean实现了BeanNameAware接口,会调用它实现的setBeanName(String beanld)方法,此处传递的是Spring配置文件中Bean的ID
4. 如果这个Bean实现了BeanFactoryAware接口,会调用它实现的setBeanFactory0,传递的是Spring工厂本身(可以用这个方法获取到其他Bean)
5. 如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文,该方式同样可以实现步骤4,但比4更好,以为ApplicationContext是BeanFactory的子接口,有更多的实现方法
6. 如果这个Bcan关联了BcanPostProccssor接口,将会调用postProcessBeforelnitialization(Object obj,Strings)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用After方法,也可用于内存或缓存技术
7. 如果这个Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法
8. 如果这个Bean关联了BeanPostProcessor接口,将会调用postAfterInitialization(Object obj,Strings)方法注意:以上工作完成以后就可以用这个Bean了,那这个Bean是一个single的,所以一般情况下我们调用同一个ID的Bean会是在内容地址相同的实例
9. 当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBeon接口,会调用其实现的destroy方法
10. 最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法

8、Spring框架中 用到了哪些设计模式?(不低于3个)

- 工厂设计模式 : Spring使用工厂模式通过 `BeanFactory`、`ApplicationContext` 创建 bean 对象。
- 代理设计模式 : Spring AOP 功能的实现。
- 单例设计模式 : Spring 中的 Bean 默认都是单例的。
- 模板方法模式 : Spring 中 `jdbcTemplate`、`hibernateTemplate` 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
- 包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
- 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
- 适配器模式: :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配`Controller`。

9、描述Spring事务传播行为? 传播行为 REQUIRED和REQUIRES_NEW区别

- REQUIRED(常用):如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
- MANDATORY:强制的 使用当前的事务,如果当前没有事务,就抛出异常。
- REQUIRES_NEW:需要JTA事务管理器的支持,新建事务,如果当前存在事务,把当前事务挂起。
- NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- NESTED:嵌套 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类 似的操作

- 在同一个方法中,因为大多数情况是一系列业务要保证要么都成功要么都失败的,所以各个业务方法使用默认的REQUIRED方式。
- 如果一个特殊的业务方法,和其他业务不关联,给它的方法设置REQUIRES_NEW,这样就能保证其他业务有异常时也不会被回滚。

10、描述Spring事务的隔离级别?

1. int ISOLATION_DEFAULT = -1; 使用数据库默认的隔离级别
2. int ISOLATION_READ_UNCOMMITTED = 1;读未提交,A事务可以读到B事务未提交的数据
3. int ISOLATION_READ_COMMITTED = 2;读提交,A事务可以读到B事务已提交的数据
4. int ISOLATION_REPEATABLE_READ = 4;可重复读,A事务读不到B事务已提交的数据
5. int ISOLATION_SERIALIZABLE = 8;串行化

11、SpringMVC中的Bean是线程安全的吗? 说出你的解决方案?

Spring容器中的Bean本身不具备线程安全的特性,Spring 的 bean 作用域(scope)类型默认是单例的,所有线程都共享一个单例实例Bean,因此是存在资源的竞争

解决方案:将bean的作用域定义为原型(prototype)

12、简述SpringMVC的执行流程?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tD3jVu5O-1659408738751)(image-20211213125046499.png)]

13、@RequestMapping注解作用

将请求和处理请求的控制器方法关联起来,建立映射关系

14、@RequestBody 和@ResponseBody的区别

- @RequestBody:作用在形参列表上,用于将前台发送过来固定格式的数据【xml 格式或者 json等】封装为对应的 JavaBean 对象,封装时使用到的一个对象是系统默认配置的 HttpMessageConverter进行解析,然后封装到形参上
- @ResponseBody:该方法的返回结果直接写入 HTTP response body 中,一般在异步获取数据时使用

15、@RequestParam和@PathViriable的区别

1. 用法上的不同, PathVariable只能用于接收url路径上的参数,而RequestParam只能接收请求带params 的
2. 内部参数不同 ,PathVariable有value,name,required这三个参数,而RequestParam除此之外还多一个参数defaultValue
3. PathVariable一般用于get和delete请求,RequestParam一般用于post请求。

16、@Resource和 @Autowired/ @Qualifier的区别

- @Autowired 根据类型注入
- @Resource 默认根据名字注入,其次按照类型搜索
- @Autowired @Qualifier 根据Qualifier 的value注入实现类

17、@Controller, @Service, @Repository,@Component作用

- @Controller 用于标注控制层,负责注册一个bean 到spring 上下文中
- @Service用于标注服务层,主要用来进行业务的逻辑处理,是类级别的注解,用于声明Service类
- @Repository用于标注数据访问层,也可以说用于标注数据访问组件,即DAO组件
- @Component注解也就是“Controller注解”、“Service注解”和“Repository注解”的通用注解,可以和它们起到相同的作用

18、@Transactional注解作用

@Transactional注解可以作用于接口、接口方法、类以及类方法上 

1. 当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性 
2. 当作用在方法级别时会覆盖类级别的定义 
3. 当作用在接口和接口方法时则只有在使用基于接口的代理时它才会生效,也就是JDK动态代理,而不是Cglib代理
4. 当在 protected、private 或者默认可见性的方法上使用 @Transactional 注解时是不会生效的,也不会抛出任何异常

19、@Configuration注解作用

1. 告诉spring这是一个配置类,相当于spring的xml配置文件
2. 被@Configuration 注解的类,会被cglib代理进行增强
3. @Configuration类允许通过调用同一类中的其他@Bean方法来定义bean之间的依赖关系,保证@Bean的对象作用域受到控制,避免多例

20、AOP的常用注解

@Aspect:把当前类声明为切面类。
@Before:把当前方法看成是前置通知。
@AfterReturning:把当前方法看成是后置通知。
@AfterThrowing:把当前方法看成是异常通知。
@After:把当前方法看成是始终通知。
@Around:把当前方法看成是环绕通知。
@Pointcut:指定切入点表达式

21、描述HTTP常用状态码(4和5各3个)

- 400:服务器不理解请求的语法
- 404:服务器找不到请求的网页
- 403:服务器拒绝请求
- 500:服务器遇到错误,无法完成请求。
- 502:服务器作为网关或代理,从上游服务器收到无效响应
- 503:服务器目前无法使用

22、描述 @ControllerAdvice @ExceptionHandler注解作用

- ExceptionHandler, 方法注解, 作用于 Controller 级别.,为一个 Controler 定义一个异常处理器
- ControllerAdvice, 类注解, 作用于 整个 Spring 工程. 定义了一个全局的异常处理器

Spring Boot

1、描述Spring @Configuration @Bean @ComponentScan @PropertySource @Value注解作用

- @Configuration:用于表明当前类是一个配置类。他的作用和bean,xml一样
- @Bean:用于把当前方法的返回值作为bean对象存入Spring的IOC容器中
- @ComponentScan:用于Spring 在创建容器时要扫描的包
- @PropertySource:用于指定properties文件的位置
- @Value:用于将常量、配置文件中的值、其他bean的属性值注入到变量中,作为变量的初始值

2、Spring Boot、Spring MVC 和 Spring 有什么区别

Spring MVC和Spring Boot都属于Spring,Spring MVC 是基于Spring的一个 MVC 框架,而Spring Boot 是基于Spring的一套快速开发整合包。spring主要特性是IOC,AOP;

3、什么是SpringBoot,优点和缺点?

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的创建、运行、调试、部署等。使用Spring Boot可以做到专注于Spring应用的开发,而无需过多关注XML的配置

- 优点:对主流开发框架的无配置集成,项目可独立运行,无须外部依赖Servlet容器。提供运行时的应用监控。极大地提高了开发、部署效率。与云计算的天然集成。
- 缺点:版本迭代速度快,模块改动很大,报错时很难定位。

4、什么是yml? 语法格式(说3点)

YML是一个可读性高,用来表达数据序列化的格式,和XML是一类。这种语言以数据做为中心,而不是以标记语言为重点
语法格式:1.大小写敏感2.使用缩进表示层级关系3.缩进时不允许使用Tab键,只允许使用空格。

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

- @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
- @EnableAutoConfiguration:打开自动配置的功能,
- @ComponentScan:Spring组件扫描。

6、描述Spring-boot-maven-plugin插件作用

spring-boot-maven-plugin插件是将spring boot的应用程序打包成fat jar的插件,借助其将所有应用启动运行所需要的jar都包含进来,从逻辑上将具备了独立运行的条件。

7、SpringBoot读取配置文件的方式(几种),分别是什么

3种,分别是:@Environment注解@Value注解@ConfigurationProperties注解

8、SpringBoot加载配置文件的顺序?如果配置文件有相同的配置属性,该如何处理?

加载顺序:properties>xml>yml
如果配置文件有相同属性,先读取到的生效
后读取的会加载,但不会覆盖前面已读取的配置

9、SpringBoot如何自定义异常?描述相关注解

使用@ControllerAdvice、@RestControllerAdvice捕获运行时异常
重写ErrorController,手动抛出自定义ErrorPageException异常,方便404、403等被统一处理。

SpringCloud

1、什么是微服务架构(微服务架构优缺点)

微服务架构就是将单体的应用程序分成多个应用程序,这多个应用程序就成为微服务,每个微服务运行在自己的进程中,并使用轻量级的机制通信。这些服务围绕业务能力来划分,并通过自动化部署机制来独立部署。这些服务可以使用不同的编程语言,不同数据库,以保证最低限度的集中式管理

2、描述SpringCloud是什么?

- SpringCloud是分布式微服务治理解决方案。提供了一系列框架技术的有序集合。
- 利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、智能路由、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。
- Spring Cloud是集大成者,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

3、Spring/SpringMVCSpringBoot和SpringCloud的区别

- Spring是一个一站式的轻量级的Java开发框架,它的核心是IOC控制反转(用于管理对象的生命周期和对象间的关系)和AOP面向切面编程(将主线业务与一些功能性代码分离)。

- Spring MVC是Spring的一个模块,是一个基于Servlet的MVC框架,主要解决WEB开发的问题。通过Dispatcher Servlet, ModelAndView 和 View Resolver,开发web应用变得很容易。解决的问题领域是网站应用程序或者服务开发——URL路由、Session、模板引擎、静态Web资源等等

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者

- SpringBoot可以离开SpringCloud独立使用开发项目, SpringCloud基于SpringBoot ,不能脱离SpringBoot。专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架
- SpringCloud是全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等集成服务

4、SpringCloud有哪些常用组件

- Spring Cloud Eureka:服务注册与发现
- Spring Cloud Ribbon:客户端负载均衡
- Spring Cloud Feign:声明性的Web服务客户端
- Spring Cloud Hystrix:断路器
- Spring Cloud Zuul:服务网关
- Spring Cloud Config:分布式统一配置管理

5、为什么使用注册中心?注册中心应具备哪些功能?

如果你在写代码调用一个有REST API或Thrift API的服务,你的代码需要知道一个服务实例的网络地址(IP地址和端口)。运行在物理硬件上的传统应用中,服务实例的网络地址是相对静态的,你的代码可以从一个很少更新的配置文件中读取网络地址。
在一个现代的,基于云的微服务应用中,
服务实例的网络地址是动态分配的。而且,由于自动扩展,失败和更新,服务实例的配置也经常变化。此时,需要服务注册与发现的组件来解决动态IP的变化等问题。

注册中心应具备以下功能:
1. 服务注册表
    服务注册表是注册中心的核心,它用来记录各个微服务的信息,例如微服务的名称、IP、端口等。服务注册表提供查询API和管理API,查询API用于查询可用的微服务实例,管理API用于服务的注册与注销。
2. 服务注册与发现
    服务注册是指微服务在启动时,将自己的信息注册到注册中心的过程。服务发现是指查询可用的微服务列表及网络地址的机制。
3. 服务检查
    注册中心使用一定的机制定时检测已注册的服务,如发现某实例长时间无法访问,就会从服务注册表移除该实例。

6、注册中心选择CAP简单描述一下?

CAP理论作为分布式系统的基础理论,它描述的是一个分布式系统在以下三个特性中:
- 一致性:在分布式系统完成某写操作后任何读操作,都应该获取到该写操作写入的那个最新的值。相当于要求分布式系统中的各节点时时刻刻保持数据的一致性。
- 可用性: 一直可以正常的做读写操作。简单而言就是客户端一直可以正常访问并得到系统的正常响应。用户角度来看就是不会出现系统操作失败或者访问超时等问题。
- 分区容错性:指的分布式系统中的某个节点或者网络分区出现了故障的时候,整个系统仍然能对外提供满足一致性和可用性的服务。也就是说部分故障不影响整体使用。

7、服务与服务间通信的解决方案有哪些?

1、RestTemplate
2、Ribbon
3、Feign
4、Dobbo

8、什么是Ribbon,Nginx和Ribbon的区别?

- Ribbon是Netflix发布的开源项目,主要功能是提供客户端的服务间调用和服务的负载均衡。
- Ribbon客户端组件提供一系列完善的配置项,如连接超时,重试等。简单的说,就是在配置文件中列出后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随即连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。(有点类似Nginx)

Nginx是反向代理同时可以实现负载均衡,nginx拦截客户端请求采用负载均衡策略根据upstream配置进行转发,相当于请求通过nginx服务器进行转发。
Ribbon是客户端负载均衡,从注册中心读取目标服务器信息,然后客户端采用轮询策略对服务直接访问,全程在客户端操作。(默认是轮循操作)

9、什么Feign? Ribbon和Feign区别?Feign常用注解

- Feign 是一个声明web服务客户端,这使得编写web服务客户端更容易
- Feign可帮助我们更加便捷,优雅的调用HTTP API。 
    在SpringCloud中,使用Feign非常简单——创建一个接口,并在接口上添加一些注解,代码就完 成了。 Feign支持多种注解,例如Feign自带的注解或者JAX-RS注解等。
    SpringCloud对Feign进行了增强,使Feign支持了SpringMVC注解,并整合了Ribbon和Eureka, 从而让Feign的使用更加方便。 
- Spring Cloud 集成 Ribbon 和 Eureka 提供的负载均衡的HTTP客户端Feign。**(Feign默认集成了Ribbon,并和Eureka结合)**。默认实现了负载均衡的效果

- 调用方式同:Ribbon需要我们自己构建Http请求,模拟Http请求然后通过RestTemplate发给其他服务,步骤相当繁琐
- 而Feign则是在Ribbon的基础上进行了一次改进,采用接口的形式,将我们需要调用的服务方法定义成抽象方法保存在本地就可以了,不需要自己构建Http请求了,直接调用接口就行了,不过要注意,调用方法要和本地抽象方法的签名完全一致

@EnableFeignClient 启动类注解 开启对Feign Client扫描加载处理
@FeignClient(name = "DUDU-USER",path = "dudu-user") 用于通知Feign组件对该接口进行代理

10、描述FeignClient注解常用参数name path?

name/value属性: 这两个的作用是一样的,指定的是调用服务的微服务名称
url : 指定调用服务的全路径,经常用于本地测试
如果同时指定name和url属性: 则以url属性为准,name属性指定的值便当做客户端的名称

11、什么是Hystrix? 降级和熔断的区别?解决的问题

- 在分布式系统,我们一定会依赖各种服务,那么这些个服务一定会出现失败的情况,就会导致雪崩,
- Hystrix是由Netflix开源的一个针对分布式系统容错处理的开源组件。能够提供断路,降级,监控等多种服务。它具有服务降级,服务熔断,服务隔离,监控等一些防止雪崩的技术

熔断这一概念来源于电子工程中的断路器(Circuit Breaker)
在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这种牺牲局部,保全整体的措施就叫做熔断
降级,就是当某个服务熔断之后,服务器将不再被4r用,此时客户端可以自己准备一个本地的 fallback回调,返回一个缺省值。

12、描述SpringCloud说5个常用注解,并描述@LoadBalanced @HystrixCommond @FeignClient/@Enable***

@LoadBalanced:Feign支持Ribbon的负载均衡
@FeignClient注解:Feign客户端提供负载均衡的热插拔注解,通过该注解可以动态代理创建Feign客户端
	fallback 如果接口服务不能访问或延迟,则运行对应其接口实现类
@HystrixCommand,代表当前方法启用Hystrix处理服务雪崩效应 fallbackMethod - 代表当调用的application
@EnableEurekaServer  启动EurekaServer 表明自己是一个服务端
@EnableDiscoveryClient  //启动主类声明该服务被注册中心发现
@EnableEurekaClient //表明自己是一个Eureka Client

架构

1、单体应用架构的产生哪些问题

- 代码不具备可维护性
- 容错性差

2、描述分布式、集群、微服务概念

- 分布式:按功能拆分将一个大的应用架构按功能拆分成多个互不相干的小应用。每个应用都是独立的WEB应用程序
- 集群:同一个业务(应用程序) 部署在不同的服务器上,对外提供相同的功能
- 微服务:单体应用拆分成互不相干的多个小应用。每一个小的应用称为微服务

3、分布式微服务架构的优点、缺点

- 优点:当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。

- 缺点:当服务越来越多,服务和服务之间调用非常混乱
	   分布式ID、分布式事务、 分布式锁、  分布Session 、分布式日志 

4、描述JVM内存模型

- 程序计数器(线程私有):每个线程都有一个独立的程序计数器,计数器所记录的是虚拟机字节码指令当前的地址
- 虚拟机栈(线程私有):虚拟机栈描述的是Java方法执行的内存模型
- 本地方法栈(线程私有):和虚拟机栈类似,但主要为虚拟机使用到的Native方法服务
- Java堆(线程共享):在虚拟机创建时启动,用于存放对象的实例
- 方法区(线程共享):用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据

5、描述JVM内存优化常用参数

-Xms2g:初始化推大小为 2g;
-Xmx2g:堆最大内存为 2g;
-XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4;
-XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2;
–XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器组合;
-XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器组合;
-XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器组合;
-XX:+PrintGC:开启打印 gc 信息;
-XX:+PrintGCDetails:打印 gc 详细信息。

n 和 Eureka 提供的负载均衡的HTTP客户端Feign。(Feign默认集成了Ribbon,并和Eureka结合)。默认实现了负载均衡的效果

  • 调用方式同:Ribbon需要我们自己构建Http请求,模拟Http请求然后通过RestTemplate发给其他服务,步骤相当繁琐
  • 而Feign则是在Ribbon的基础上进行了一次改进,采用接口的形式,将我们需要调用的服务方法定义成抽象方法保存在本地就可以了,不需要自己构建Http请求了,直接调用接口就行了,不过要注意,调用方法要和本地抽象方法的签名完全一致

@EnableFeignClient 启动类注解 开启对Feign Client扫描加载处理
@FeignClient(name = “DUDU-USER”,path = “dudu-user”) 用于通知Feign组件对该接口进行代理


### 10、描述FeignClient注解常用参数name path?

name/value属性: 这两个的作用是一样的,指定的是调用服务的微服务名称
url : 指定调用服务的全路径,经常用于本地测试
如果同时指定name和url属性: 则以url属性为准,name属性指定的值便当做客户端的名称


### 11、什么是Hystrix? 降级和熔断的区别?解决的问题

  • 在分布式系统,我们一定会依赖各种服务,那么这些个服务一定会出现失败的情况,就会导致雪崩,
  • Hystrix是由Netflix开源的一个针对分布式系统容错处理的开源组件。能够提供断路,降级,监控等多种服务。它具有服务降级,服务熔断,服务隔离,监控等一些防止雪崩的技术

熔断这一概念来源于电子工程中的断路器(Circuit Breaker)
在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这种牺牲局部,保全整体的措施就叫做熔断
降级,就是当某个服务熔断之后,服务器将不再被4r用,此时客户端可以自己准备一个本地的 fallback回调,返回一个缺省值。


### 12、描述SpringCloud说5个常用注解,并描述@LoadBalanced @HystrixCommond @FeignClient/@Enable***

@LoadBalanced:Feign支持Ribbon的负载均衡
@FeignClient注解:Feign客户端提供负载均衡的热插拔注解,通过该注解可以动态代理创建Feign客户端
fallback 如果接口服务不能访问或延迟,则运行对应其接口实现类
@HystrixCommand,代表当前方法启用Hystrix处理服务雪崩效应 fallbackMethod - 代表当调用的application
@EnableEurekaServer 启动EurekaServer 表明自己是一个服务端
@EnableDiscoveryClient //启动主类声明该服务被注册中心发现
@EnableEurekaClient //表明自己是一个Eureka Client


## 架构

### 1、单体应用架构的产生哪些问题

  • 代码不具备可维护性
  • 容错性差

### 2、描述分布式、集群、微服务概念

  • 分布式:按功能拆分将一个大的应用架构按功能拆分成多个互不相干的小应用。每个应用都是独立的WEB应用程序
  • 集群:同一个业务(应用程序) 部署在不同的服务器上,对外提供相同的功能
  • 微服务:单体应用拆分成互不相干的多个小应用。每一个小的应用称为微服务

### 3、分布式微服务架构的优点、缺点

  • 优点:当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。

  • 缺点:当服务越来越多,服务和服务之间调用非常混乱
    分布式ID、分布式事务、 分布式锁、 分布Session 、分布式日志


### 4、描述JVM内存模型

  • 程序计数器(线程私有):每个线程都有一个独立的程序计数器,计数器所记录的是虚拟机字节码指令当前的地址
  • 虚拟机栈(线程私有):虚拟机栈描述的是Java方法执行的内存模型
  • 本地方法栈(线程私有):和虚拟机栈类似,但主要为虚拟机使用到的Native方法服务
  • Java堆(线程共享):在虚拟机创建时启动,用于存放对象的实例
  • 方法区(线程共享):用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据

### 5、描述JVM内存优化常用参数

-Xms2g:初始化推大小为 2g;
-Xmx2g:堆最大内存为 2g;
-XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4;
-XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2;
–XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器组合;
-XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器组合;
-XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器组合;
-XX:+PrintGC:开启打印 gc 信息;
-XX:+PrintGCDetails:打印 gc 详细信息。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值