JavaEE面试宝典

JavaEE面试宝典

1. SpringCloud微服务

1.1. 注册中心(Eureka,Zookeeper,Nacos)

注册中心有两大核心,分为provider(服务提供者)和consumer(服务消费者),也被称为Web和Service,将provider和consumer注册到注册中心的客户端中,调用时根据模块名称去客户端获取,ip和端口号来进行调用。Eureka保证的是可用性,Zookeeper保证的是一致性,详情请搜索CAP理论

1.1.1. Eureke

Eureka是目前国内最流行的注册中心组件,虽然已经停更但热度不减,依然是SpringCloud官方推荐的注册中心。支持集群,实现方式是互相注册即可。

1.1.1.1. Eureka-模块调用

Eureka采用crp远程调用(crp是zookeeper的分布式连接方式,国内已经习惯这么叫),底层协议是HTTP协议。

1.1.1.2. 心跳及自我保护机制

在应用启动后,节点们将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)

Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,Eureka Server 会将这些实例保护起来,让这些实例不会过期,但是在保护期内如果服务刚好这个服务提供者非正常下线了,此时服务消费者就会拿到一个无效的服务实例,此时会调用失败,对于这个问题需要服务消费者端要有一些容错机制,如重试,断路器等。

1.1.2. Zookeeper

它是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。

Dubbo(阿里,类似于SpringCloud)建议使用Zookeeper作为服务的注册中心

1.1.3. Nacos**(建议学习或了解)**

Nacos是阿里推出的注册中心,并集成了,配置中心Config和消息总线Bus的功能,很有潜力,值得大家自行学习。提供了Web页面,极大的简便了学习成本。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L2zas5Az-1600746909872)(file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml7636\wps1.jpg)]

1.2. 负载均衡-Ribbon

负载均衡是用于解决一台机器(一个进程)无法解决所有请求而产生的一种算法。像nginx可以使用负载均衡分配流量,ribbon为客户端提供负载均衡,dubbo服务调用里的负载均衡等等,很多地方都使用到了负载均衡。

负载均衡有好几种实现策略,常见的有:

  1. 轮询 (RoundRobin)-(默认)

  2. 随机 (Random)

  3. 一致性哈希 (ConsistentHash)

  4. 哈希 (Hash)

  5. 加权(Weighted)

1.3. 声明式服务调用-Feign

  1. Feign可以把Rest的请求进行隐藏,伪装成类似SpringMVC的Controller一样。你不用再自己拼接url,拼接参数等等操作,一切都交给Feign去做。

  2. Feign底层使用了Ribbon,所以默认支持负载均衡。

  3. OpenFeign是springcloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

1.4. 服务网关(Zuul,Zuul2,GetWay)

服务网关 = 路由转发 + 过滤器

1、路由转发:接收一切外界请求,转发到后端的微服务上去

2、过滤器:在服务网关中可以完成一系列的横切功能,例如权限校验、限流以及监控等

1.4.1. Zuul

1、路由–所有请求通过zuul访问consumer

2、容错–客户端通过zuul无法调用consumer,使用zuul对consumer进行降级

3、限流–使用令牌桶算法实现zuul对consumer的限流

1.4.2. Zuul2

Zuul2和Zuul一样都是由Netflix公司开发的服务网关,但是由于公司的决策问题,导致开发Zuul的核心人员变更,开发推迟,所以目前处于新版本测试阶段。

1.4.2. GetWay**(建议学习或了解)**

GetWay是由SpringCloud官方推出的服务网关组件,由于Zuul2的推迟,SpringCloud官方决定自己开发服务网关组件,至此GetWay横空出世,目前国外很常用,但是在国内基本都还是再用Zuul。GetWay和Zuul2一样都实现了,异步非阻塞模型。

1.5. 服务降级(Hystrix,Sentinel)

1.5.1. Hystrix

1、降级

​ 服务提供者无法调用服务消费者时,使用Hystrix对服务消费者进行降级,返回拖底数据

2、熔断

​ 当失败率、线程池和信号量达到阀值时自动触发降级,熔断触发的降级会在指定时间后测试服务是否恢复。(默认5秒后的下一次请求,会测试服务是否恢复)

1.5.2. Sentinel**(建议学习或了解)**

Sentinel是由阿里推出的新一代熔断降级组件,不像Hystrix一样使用时需要大量配置。

Sentinel是一个单独的组件,可以独立出来,直接页面化的细粒度的统一配置,提供了一套Web页面,实现对流控,速率,降级,熔断等操作。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9DZM8JcX-1600746909878)(file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml7636\wps2.jpg)]

1.6. 配置中心Config和消息总线Bus

Config配置中心: 用来集中管理配置文件

Bus消息总线: 用来实现不需要重启即可更新配置文件

工作流程:

  1. 首先将配置文件发送到远程的GitHub上

  2. 在Config客户端上配置GitHub地址

  3. 使用postman发送post请求,通知Config客户端拉取配置文件

  4. 配置文件下载后,发送消息到MQ队列中

  5. Bus监听MQ队列,收到消息后

  6. Bus发送MQ消息,通知相应的模块

  7. 模块收到消息后,去Config客户端获取配置文件,进行更新

2. 多线程

多线程在项目中很常见,比如高效的读取一个大型的txt文件,首先将txt文件分片,之后使用多线程读取每个分片,再组合到一起,这样来说效率最高。

2.1. 多线程实现方式

  1. 继承Thread类:

重写run方法即可,缺点是Java是单继承,多实现的,这样的话该类就无法继承其他类了,一般不推荐使用。

  1. 实现Runnable接口:

解决Thread类单继承问题,可以实现多线程,缺点是没有返回值,无法接收结果只能为void。

  1. 实现Callable接口:

Java5提供了Future接口来代表Callable接口里call()方法的返回值,并为Future接口提供了一个实现类FutureTask。

  1. 从线程池中获取:

使用Executors创建线程和调用线程,可以有返回值,大规模使用多线程时,建议使用线程池,使用后需要关闭线程。

2.2. 线程状态

新建状态:

使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

就绪状态:

当线程对象调用了 start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待 JVM 里线程调度器的调度。

运行状态:

如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

阻塞状态:

如果一个线程执行了 sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。

死亡状态:

一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

3. 同步锁

3.1. Synchronized和Lock(ReentrantLock)的区别

Synchronized是java中的修饰符,可以修饰类、方法、代码块,不需要手动释放锁,发生异常会自动释放锁,不会造成死锁,操作简单。

Lock只能给代码块加锁,需要手动释放锁,如果没有调用unLock方法释放锁,会造成死锁,而且提供了一些操作方法,方便的获取锁信息。

4. MySQL数据库

4.1. InnoDB和MyISAM的区别

InnoDB:

  1. 适合增删改查

  2. 必须有主键

  3. 支持事务

  4. 聚簇索引

  5. 最小锁粒度为行级锁

  6. MySQL-5.5版本后改为默认存储引擎

MyISAM:

  1. 适合查询

  2. 非聚簇索引

  3. 系统崩溃后恢复慢

  4. 最小锁粒度为表级锁

4.2. SQL语句执行顺序

from-on-join-where-group by-having-select-distinct-order by-limit

查表-过滤-连接-过滤-分组-过滤-指定列-去重-排序-分页

4.3. B+树结构—附学习网站链接

https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

二叉树:

二叉树是最基础的树状结构,根据第一个值判断,小的放左边,大的放右边

红黑树:

红黑树是在二叉树的基础上进行,动平衡调节,不会导致一边链性过长

B树:

B树是在红黑树的基础上,可以在节点上进行横向扩展,并且每个节点上都可以挂载key-value形式的数据,可以减少查询的IO次数

B+树:

B+树是由MySQL开发,独享的一种数据结构,在B树的基础上,砍掉了,B树上的key-value数据,只保留key,并且只有在跟节点才有key-value的数据,key为主键,value为根据存储结构为InnoDB或MyISAM来决定,value是磁盘地址还是数据

5. JVM与GC

5.1. JVM内存模型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XNK7Kuig-1600746909881)(file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml7636\wps3.png)]

JVM:https://www.bilibili.com/video/BV11J411N7Tg?from=search&seid=11648229642537222949

5.1.1. 程序计数器(线程私有)

记录程序命令顺序,记录当前线程所执行的字节码行号,每一条线程都有一个独立的程序计数器,这类内存也称为“线程私有”的内存。

5.1.2. JAVA虚拟机栈(线程私有)(栈)

存放的是声明的变量、对象。每个方法在执行的时候也会创建一个栈帧,存储了局部变量,操作数,动态链接,方法返回地址。每个方法从调用到执行完毕,对应一个栈帧在虚拟机栈中的入栈和出栈。通常所说的栈,一般是指在虚拟机栈中的局部变量部分。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HSbh8nsT-1600746909885)(file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml7636\wps4.png)]

当前栈帧:表示当前正在执行的方法,存放当前方法的变量,对象,操作命令等一系列相关的数据和操作行为。

5.1.2.1. 局部变量表

存放编译期可知的各种基本数据类型、对象引用类型和returnAddress类型(指向一条字节码指令的地址:函数返回地址)。
long、double占用两个局部变量控件Slot。
局部变量表所需的内存空间在编译期确定,当进入一个方法时,方法在栈帧中所需要分配的局部变量控件是完全确定的,不可动态改变大小。

5.1.2.2. 操作数栈

后进先出LIFO,最大深度由编译期确定。栈帧刚建立使,操作数栈为空,执行方法操作时,操作数栈用于存放JVM从局部变量表复制的常量或者变量,提供提取,及结果入栈,也用于存放调用方法需要的参数及接受方法返回的结果。
操作数栈可以存放一个jvm中定义的任意数据类型的值。
在任意时刻,操作数栈都一个固定的栈深度,基本类型除了long、double占用两个深度,其它占用一个深度

5.1.2.3. 动态链接

每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。Class文件的常量池中存在有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。这些符号引用,一部分会在类加载阶段或第一次使用的时候转化为直接引用(如final、static域等),称为静态解析,另一部分将在每一次的运行期间转化为直接引用,这部分称为动态连接。

5.1.2.4. 方法出口

当一个方法被执行后,有两种方式退出该方法:执行引擎遇到了任意一个方法返回的字节码指令或遇到了异常,并且该异常没有在方法体内得到处理。无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行。方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。一般来说,方法正常退出时,调用者的PC计数器的值就可以作为返回地址,栈帧中很可能保存了这个计数器值,而方法异常退出时,返回地址是要通过异常处理器来确定的,栈帧中一般不会保存这部分信息。
方法退出的过程实际上等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,如果有返回值,则把它压入调用者栈帧的操作数栈中,调整PC计数器的值以指向方法调用指令后面的一条指令。

5.1.3. 本地方法栈(了解)

主要使用计算机的硬件调用,如:打印机、键盘、鼠标等硬件设备,目前已经被弃用,由系统执行硬件命令,并非直接操作。

5.1.4. 堆(线程共享)

被所有线程共享的一块内存区域,在虚拟机启动的时候创建,用于存放对象实例。对可以按照可扩展来实现(通过-Xmx 和-Xms 来控制),也是GC回收的主要方向。

堆内存一般分为年轻代、老年代、永久代,JDK1.8之后使用元空间代替了永久代,因为Oracle官方更换了根据JVM公约开发的另一个JVM。

5.1.5. 方法区(线程共享)

被所有方法线程共享的一块内存区域。
用于存储已经被虚拟机加载的类信息,常量,静态变量等。
这个区域的内存回收目标主要针对常量池的回收和堆类型的卸载。

5.2. GC垃圾回收器

GC:https://www.bilibili.com/video/BV154411r7GP?p=7

5.2.1. 引用计数法

使用计数器记录每个对象的使用情况,GC时回收使用次数为0的对象,当使用次数达到15时将数据放到老年代存储。

特点:需要维护计数器,使用计数器也会消耗一定的资源,JVM一般不采用此方式。

5.2.2. 复制算法

在内存开辟两个空间,先将数据存放到一边,当GC时将存活的数据复制到另一边内存上并将对象年龄+1,如果达到15时,就将对象存放到老年代,对象复制之后再将内存清空。

特点:需要双倍的内存空间。

年轻代中采用的此算法,因为复制算法适合存活率低的年轻代

5.2.3. 标记清除

分为两步

第一步标记: 从根集合中开始扫描,对存活的对象进行标记

第二步清除: 扫描整个内存空间,回收未被标记的对象

特点:两次扫描耗时严重,会产生内存碎片,不需要额外空间,但是该算法会暂停整个应用,不然会出现两次扫描中,调用的对象出现误判

5.2.4. 标记压缩

分为两步

第一步标记:和标记清除一样,先标记再清除

第二步压缩:再次扫描,并往一段滑动存活对象

特点:没有内存碎片,需要移动对象的成本。

内存碎片缺点:会不规则的占用整个内存,比如当需要创建数组时,因为数组需要连续的使用空间所以,如果有内存碎片的话会很麻烦。

优化:为了减少移动成本,可以设计当GC一定次数时再进行压缩操作。

5.2.5. 最优算法

标记清除*

分为两步

第一步标记: 从根集合中开始扫描,对存活的对象进行标记

第二步清除: 扫描整个内存空间,回收未被标记的对象

特点:两次扫描耗时严重,会产生内存碎片,不需要额外空间,但是该算法会暂停整个应用,不然会出现两次扫描中,调用的对象出现误判

5.2.4. 标记压缩

分为两步

第一步标记:和标记清除一样,先标记再清除

第二步压缩:再次扫描,并往一段滑动存活对象

特点:没有内存碎片,需要移动对象的成本。

内存碎片缺点:会不规则的占用整个内存,比如当需要创建数组时,因为数组需要连续的使用空间所以,如果有内存碎片的话会很麻烦。

优化:为了减少移动成本,可以设计当GC一定次数时再进行压缩操作。

5.2.5. 最优算法

没有最优算法,只有最适合的算法,根据内存效率、内存整齐度、内存利用率等因素选择合适的算法即最优算法。

猜你喜欢

转载自blog.csdn.net/zhang_yuanbai/article/details/108728459