java面试题

文章只是自己准备面试,为了便于记忆整理的知识点,可能会有的地方有所缺漏或者理解有误,请大家多多包涵和指正!!!
文章只是自己准备面试,为了便于记忆整理的知识点,可能会有的地方有所缺漏或者理解有误,请大家多多包涵和指正!!!
文章只是自己准备面试,为了便于记忆整理的知识点,可能会有的地方有所缺漏或者理解有误,请大家多多包涵和指正!!!

集合面试题

1.HashMap、HashTable、ConcurrentHashMap、LinkedHashMap的数据结构。

1.HashMap

是并发不安全的容器,数据结构由jdk1.7 ==> 1.8从数组+链表变为了数组+链表或者红黑树。
这一点优化,解决了,jdk1.7时候高并发下hashmap扩容会造成死循环问题。1.8还提高了查询效率。

1.1jdk1.7数据结构 数组+链表。

整体是一个数组,数组初始大小为16(默认容量),当有不同的元素的indexFor(hash(key))相同是,就会形成链表,链表中的每个节点存储的时value。
扩容的加载因子时0.75。例:所以默认阈值为16*0.75=12。

put()流程:

1.根据key,计算出hash值。
2.调用 indexFor(hash % (length-1))方法计算当前对象应该存储在哪个hash桶中。
3.判断所有entry的数量,是否达到扩容阈值(12)。达到了则进行数组扩容,扩容到原来的两倍。
4.把key、value、hash封装成一个entry,根据hash到数组上查询是否存在元素,若无则放上去;
有的话,说明已经是链表,那么遍历链表,判断是否存在key相同,若存在kry相同,则替换掉value;
若不存在,则把entry中next指向链表第一个元素(头插法)。

扩容机制:

1.只有在put的时候才会扩容,扩容是将数组的容量*2。
2.讲所有的entry放入到新的扩容数组中。

扩容造成死循环原理:

在扩容的时候,两个线程分别new出了hash表,一个线程先完成链表扩容,另一个链表会转置entry位置,就会形成环形链表,这样当get不存在元素时候,就会无限循环,导致cpu百分之百。

1.2jdk1.8数据结构 数组+链表(红黑树)

链表转红黑树的条件:当前链表的长度大于8,且整个hash桶的数组容量大于64时。
hashmap的key和value都可以为null。

流程分析:

当我们无参new HashMap();时候,不会马上去创建数组。而是在第一次put的时候会创建长度为16的数组,数组的扩容机制和1.7一样。
put机制和jdk1.7一样,只不过采用的是尾插法(防止死循环),和转红黑树阈值判断。
二叉树转为链表的阈值是:桶中的元素小于6时。

为什么HashMap的容量是2的倍数呢

1.第一个原因是为了方便哈希取余。
2.第二个方面是在扩容时,利用扩容后的大小也是2的倍数,将已经产生hash碰撞的元素完美的转移到新的table中去。

2.HashTable

HashTable是一个并发安全的Map容器,put、get方法上都加上了synchronized,所以效率也比较低。
同样Collections.synchronizedMap(map)也是并发安全容器,底层原理是所有的方法加上了排斥锁。

3.ConcurrentHashMap

是JUC下的一个并发安全类,且效率高,在我们选择map并发安全容器时,所以往往倾向于它。

jdk1.7==>jdk1.8优化:

锁: 由分段锁(Segment继承自ReentrantLock)升级为 CAS+synchronized实现。
数据结构层面: 将Segment(分段锁,只有写的时候加锁,读的时候不加锁)

变为了Node(HashMap中的定义很相似,但是有一些差别它对value和next属性设置了volatile同步锁),减小了锁粒度,使每个Node独立,由原来默认的并发度16变成了每个Node都独立,提高了并发度。
数据结构和hashmap一样1.8改造成了数组+链表+红黑树,但是为了并发安全,hash桶添加几个内部类。

为什么hashTable和ConcurrentHashMap的key和value不能为null呢?

安全快速失败机制。

4.LinkedHashMap

HashMap和双向链表合二为一即是LinkedHashMap。
linkedHashMap,相对于hashMap的基础上额外维护了一个双向链表,来记录元素的插入顺序。
相对于hashMap的Entry组成,有增加了before和after信息。

2.ArrayList和LinkedList和Vector

1.ArrayList

ArrayList的底层数据结构是动态数组,get,add(不考虑扩容的情况下),时间复杂度都是O(1),这也是它最大的优势。
arrayList<>(),初始容量0,add()则容量为10。加载因子是1。
当发生扩容的时候,调用的是Arrays.copyOf()方法里面的system.arraycopy();
优势:查询快

2.LinkedList

内部是使用双向链表来实现的。
因为查询需要遍历,所以时间复杂度是O(n)。
优势:插入快
在我们日常使用中,应该优先选择使用ArrayList,因为他更轻量级,不需要在每个元素上维护上一个和下一个元素的地址,时间复杂度低。

3.Vector

vector是已经被弃用的一个并发安全的集合,现在常用Collections.synchronizedList(new ArrayList<>())、juc的CopyOnWriteArrayList,List<?> list = new CopyOnWriteArrayList<>();代替。

3.HashSet和TreeSet

1.HashSet

hashset本质就是hashmap的key,可以参考hashMap的是实现。

2.TreeSet

TreeSet<?>对某类对象进行排序,这个?class必须实现Comparable接口,复写compareTo()方法,
或者直接构造内部类 在集合中定义排序 实现Comparator接口 覆盖compare()方法。
如果是String比较,则默认比较的是字母排序。
并发安全的set,使用collections工具类的synchronizedSet()。

多线程面试题

1.wait和sleep的区别

sleep是Thread的静态方法,wait是Object的方法。
调用sleep方法不会释放锁,也不需要占用锁。wait会释放锁,但调用它的前提是当前线程占有锁(即代码要在synchronized中)。
它们都可以被interrupted方法中断。

2.线程的状态

NEW新建 => RUNNABLE准备就绪 => BLOCKED阻塞 =>WAITING等待(不见不散) =>TIMED_WAITING等待(过时不候)=>
TERMINATED(终结)。

3.synchronized和ReentrantLock区别

便利性:lock是一个接口,而synchronized是java的关键字,很明显Synchronized的使用比较方便简洁,并且由编译器去保证锁的加锁和释放,而ReenTrantLock需要手工声明来加锁和释放锁,为了避免忘记手工释放锁造成死锁,所以最好在finally中声明释放锁。
锁的细粒度和灵活度:
① 底层实现上来说,synchronized 是JVM层面的锁,是Java关键字,通过monitor对象来完成(monitorenter与monitorexit),对象只有在同步块或同步方法中才能调用wait/notify方法,ReentrantLock 是从jdk1.5以来(java.util.concurrent.locks.Lock)提供的API层面的锁。
synchronized 的实现涉及到锁的升级,具体为无锁、偏向锁、自旋锁、向OS申请重量级锁,ReentrantLock实现则是通过利用CAS(CompareAndSwap)自旋机制保证线程操作的原子性和volatile保证数据可见性以实现锁的功能。
ReentrantLock可以更细分锁的颗粒度。
https://zhuanlan.zhihu.com/p/126085068
还可以从锁的是什么?是否为公平锁来讲。

4.synchronized锁的是什么

1.如果是普通方法上,则锁的是对象。
2.如果是静态方法上都锁的时候这个类的class字节码文件。
3.如果是方法快,则锁的是,参数这个对象。

5.线程之间如何实现通信

加标志位,用lock实现每个唤醒需要新建出自己的condition,然后指定去唤醒特定线程的condition(await()、signal())。

6.定义线程池的参数和常用线程池

public ThreadPoolExecutor(int corePoolSize, // 常驻线程数量(核心)
int maximumPoolSize, // 最大线程数量
long keepAliveTime, // 线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue workQueue, // 阻塞队列
ThreadFactory threadFactory, // 线程工厂:用于创建线程
RejectedExecutionHandler handler) // 拒绝策略
1.SingleThreadPool,单线程的线程池,使用无界队列LinkedBlockingQueue作为工作队列。
2.FixedThreadPool 重用固定线程数的线程池,使用无界队列LinkedBlockingQueue作为工作队列。
3.CacheThreadPool 不确定线程多少的负载线程池。CachedThreadPool使用没有容量的SynchronousQueue作为线程池的工作队列,但CachedThreadPool的maximumPool是无界的
4.ScheduledThreadPool 它主要用来在给定的延迟之后运行任务,或者定期执行任务。使用DelayQueue作为任务队列。

7.synchronized锁升级的过程

在jdk1.6之前synchronized是一个重量级锁,编译成字节码的时候会包含monitorenter、 monitorexit指令。在jdk1.6之后就优化了synchronized的是现实,偏向锁=>轻量级锁(cas)=>重量级锁。
参考:锁膨胀过程分析

8.ReentrantReadWriteLock和锁降级过程

读写锁,既可以获取读锁,也可以获取写锁
需要明确的是:写锁是独占锁,所谓独占即为独自占有,别的线程既不能获取到该锁的写锁,也不能获取到对应的读锁。
读锁是共享锁,所谓共享即是所有线程都可以共同持有该读锁。
锁降级指的是写锁降级为读锁的过程:
如果只使用写锁,那么释放写锁之后,其他线程就会获取到写锁或读锁,使用锁降级可以在释放写锁前获取读锁,这样其他的线程就只能获取读锁,对这个数据进行读取,但是不能获取写锁进行修改,只有当前线程释放了读锁之后才可以进行修改。

9.CompletableFuture使用

异步回调:CompletableFuture
没有返回值的 .runAsync .get
有返回值的 .supplyAsync .get .whenComplete

10.voliate

作用:

1.保证线程间的可见性

2.防止指令重排序

原理:被volatile修饰的编写修改时,不会把变量读取到CPU cache中再修改,而只直接在内存中操作,保证了可见性。

有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置)

:什么是指令重排序:是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理。

IO面试题

1.流

应用程序和外部设备之间的数据传递,常见的外部设备包括文件、管道、网络连接。
特点 1.输入和输出顺序不变(先进先出)2.流中顺序存储 3.一个流只能是读或者写,不能读写兼备。
分类 方向:输入流、输出流。 处理数据单位:字节流和字符流。功能:节点流和处理流。
流的区分
为什么有字节流了,还要推出字符流呢?
在UTF-8编码中,一个中文字符是3个字节,如果一次读写一个字符对应的字节数就不会有问题,一旦将一个字符对应的字节分裂开来,就会出现乱码了。(字符由多个字节组成,防止出现乱码)
所以,字节流一般用来处理图像、视频、音频、PPT、Word等类型的文件。字符流一般用于处理纯文本类型的文件。
字节流和字符流的区别:
字节流可以处理一切文件,字符流只能处理文本。
字节流没有缓冲区,所以缓冲字节流效率特别高,而字符流自带缓冲区。
节点流和处理流
节点流:直接处理文件的流,例如FileInputStream
处理流:对已存在的节点流进行了封装,最终的数据处理还是由节点流完成的例如BufferedInputStream(缓冲字节流)
缓冲流的原理:
因为磁盘和程序的直接交互是很慢的,所以在内存中申请了一块缓冲区,等缓冲填满了,一次性交互。这样,在总数据量不变的情况下,通过提高每次交互的数据量,减少了交互次数。

InputStream与OutputStream是两个抽象类,是字节流的基类,所有具体的字节流实现类都是分别继承了这两个类。
与字节流类似,字符流也有两个抽象基类,分别是Reader和Writer。其他的字符流实现类都是继承了这两个类。
new FileReader(File file)等同于new InputStreamReader(new FileInputStream(file, true),“UTF-8”)

可参考IO复习
参考

2.io和nio

NIO和BIO的比较
BIO和NIO的处理数据方式,BIO是以流stream的方式,NIO是以块buffer的方式,块的方式比流的方式快很多,
BIO是阻塞的,NIO是非阻塞的。
BIO基于字节流和字符流来处理数据,而NIO是基于管道和缓冲区,数据总是从通道读到缓冲区,再冲缓冲区,读到管道。(类似与流,但是又不同,因为缓冲区既可以写也可以读,但是字节输入流可以写,输出流才可以读)
NIO的选择器可以监听多个管道事件,所以一个线程可以处理多个客户端通道
NIO一个线程对应一个选择器(多路复用器),一个选择器对应多个通道,一个通道对应一个缓冲区,一个缓冲区对应一个客户端连接。
可参考io和nio

Spring面试题

1.阐述Spring中对IOC和AOP的理解。

IOC是控制反转是一种思想,就是把bean的创建和管理交由给Spring框架来做的。

IOC容器是Spring用来实现IOC的载体,IOC容器实际上就是一个Map(key, value),Map中存放的是bean的实例,bean实例是通过反射的方式得到的。

对象之间的相互依赖关系也交给IOC容器来管理,并由IOC容器完成对象的依赖注入。

这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。不然每次都需要开发人员去new一个实例,且还要去填充对象属性太麻烦了,极大的提高了开发效率。

AOP即面向切面编程。能够将那些与业务无关,但是业务模块必须要实现的统一功能封装起来。(例如事务处理、日志管理、权限控制等),这样可以减少系统的重复代码,降低模块间的耦合度,增加系统的可扩展性和可维护性。

Spring的AOP是基于动态代理的,如果要代理的对象实现了某个接口,那么Spring AOP就会使用JDK动态代理去创建代理对象;而对于没有实现接口的对象,就无法使用JDK动态代理,转而使用cglib动态代理生成一个被代理对象的子类来作为代理。

2.Spring中IOC的底层实现原理。

createBeanFactory()–>getBean()–>doCreateBean()–>populateBean()–>initializingBean()

1.首先通过createBeanFactory创建一个bean工厂。

2.然后就开始创建对象,因为默认是单例的,所以先通过getBean()到ioc容器来获取。

3.如果没有获取到,则通过doCreateBean()来创建bean,实现的方式是反射。

4.创建好bean,再通过populateBean()来实现属性的注入。

5.最后通过initializingBean() 来实现其它的初始化操作。

3.Spring中AOP的底层实现原理。

1.首先aop是ioc的一个拓展功能,是通过BeanPostProcessor的后置方法来实现的。

2.通过动态代理的方法来创建代理对象,如果要代理的对象实现了某个接口,那么Spring AOP就会使用JDK动态代理去创建代理对象;而对于没有实现接口的对象,就无法使用JDK动态代理,转而使用cglib动态代理生成一个被代理对象的子类来作为代理。

3.1JDK动态代理和cglib动态代理区别。

EatProxy implements InvocationHandler,通过EatProxy获取代理对象
cglib是通过字节码生成代理对象

4. Spring中bean的生命周期。

img

实例化 --> 属性注入 --> 实现aware接口 --> 前置BeanPostProcessor -->初始化 --> 后置 --> 销毁

BeanPostProcessor 就是来修改bean的

1.bean的实例化,是通过反射的方式实现的。

2.bean的属性注入,是通过populateBean()实现的。

3.调用aware接口的相关方法(实现BeanName、BeanFactory、ApplictionContext对象的属性设置)

4.调用BeanPostProcessor的前置处理

5.调用initMethond方法,判断是否实现InitializingBean接口,如果有的话就实现afterPropertiesSet方法。

6.调用BeanPostProcessor的后置处理,aop就是这块实现的

7.获取完整的bean对象,可以通过getBean来获取。

8.当容器关闭的时候,就会调用销毁流程,调用DisposableBean的destory()。

5. Spring是如何解决循环依赖问题的。

一级缓存singletonObjects 用于存储单例模式下创建的Bean实例(已经创建完毕)

二级缓存earlySingletonObjects 用于存储单例模式下创建的Bean实例(该Bean被提前暴露的引用,该Bean还在创建中)

三级缓存singletonFactories 三级缓存是为了解决动态代理的问题,通过ObjectFactory对象来存储单例模式下提前暴露的Bean实例的引用(正在创建中)

1.循环依赖问题只在单例模式下可以解决,本质上解决方法就是把实例化和初始化的属性赋值分开。

2.两个Bean相互依赖的时候,提前暴露未完全初始化的Bean放入到二级缓存中,getBean的时候会有参数判断是否完全初始化bean,然后递归注入到另一个Bean的属性上。

3.初始化完成则放入到一级缓存。

4.三级缓存是为了解决动态代理的问题,通过ObjectFactory对象来存储单例模式下提前暴露的Bean实例的引用(正在创建中)

5.1Spring 三级缓存解决bean循环依赖,为何用三级缓存而非二级

如果仅仅是解决循环依赖问题,使用二级缓存就可以了,但是如果对象实现了AOP,那么注入到其他bean的时候,并不是最终的代理对象,而是原始的。这时就需要通过三级缓存的ObjectFactory才能提前产生最终的需要代理的对象。

什么时候将Bean的引用提前暴露给第三级缓存的ObjectFactory持有?时机就是在第一步实例化之后,第二步依赖注入之前,完成此操作。

6.Bean Factory 和Factory Bean的区别。

相同点:都是来创建bean对象的。

1.BeanFactory是spring中IOC容器的定义规范,我们熟知的applicationContext就是集成了它,但是这样定义bean的生命周期太复杂了。

2.FactoryBean在IOC容器的基础上给Bean的实现加上了一个简单工厂模式,想简单创建一个对象就实现FactoryBean就可以了(isSingleton;getObjectType;getObject(怎么来创建);)

7. Spring中运用到的设计模式。

1.工厂模式 BeanFactory

2.单例模式 默认所有的Bean都是单例的

3.代理模式 aop动态代理

4.策略模式 实例化对象的时候

5.适配器模式 mvc中对象hanlderAdapters

8.Spring的事务是如何实现的,和事务的失效场景。

底层就是Aop来实现的,分别为声明式事务和编程式事务(几乎不用)

1.首先要生成代理对象,但是事务不是直接通过通知来实现的,而是通过TransactionInterceptor来间接实现的,然后调用invoke来实现具体的逻辑

2.解析各个方法上面的属性

3.当需要开启事务的时候,获取数据库连接,关闭自动提交

4.执行业务逻辑

5.如果执行失败了则事务rollback,如果成功则commit。

8.1Spring事务的失效场景。

1.方法的修饰符不为public

2.数据库不支持事务

3.抛出的异常不是spring支持的异常,比如说Exception(但是可以rollback指定异常),spring支持RuntimeException。

4.类没有被spring管理

5.被内部catch住没有向上抛出异常

6.对象调用本身;因为事务的原理是aop,通过代理对象实现的,自己调用自己无代理对象产生。

7.多线程中,不支持。因为每个线程会建立新的数据库连接。

9. 谈一下Spring中的事务传播。

事务传播特性有7种。

用的比较多的是Required(默认值)、 Require_new 、nested 、 NEVERSUPPORTSNOT_SUPPORTEDMANDATORY

1.Required( 如果当前已经存在事务,那么加入该事务,如果不存在事务,创建一个事务,这是默认的传播属性值。)

2.Require_new(创建一个新的事务,如果新事务回滚了,不影响之前的事务,外部事务有问题,也不会回滚)

3.nested(创建一个新的事务,会记录一个SavePoint ,如果内部这个事务出现问题了,则回滚到这个点,不影响外部事务提交,如果外部事务有问题则全都回滚)

10.Spring中的bean的作用域有哪些

1.singleton:唯一bean实例,Spring中的bean默认都是单例的。

2.prototype:每次请求都会创建一个新的bean实例。

3.request:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。

4.session:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效。

5.global-session:全局session作用域,仅仅在基于Portlet的Web应用中才有意义,Spring5中已经没有了。Portlet是能够生成语义代码(例如HTML)片段的小型Java Web插件。它们基于Portlet容器,可以像Servlet一样处理HTTP请求。但是与Servlet不同,每个Portlet都有不同的会话。

11.SpringMVC 工作原理

1.客户端发送请求到 DispatcherServlet
2.DispatcherServlet 查询 handlerMapping 找到handler返回给DispatcherServlet,处理请求的 Controller
3.DispatcherServlet在通过handlerAdapter来执行这个hander,请求到Controller 调用业务逻辑后,返回 ModelAndView
4.DispatcherServlet 查询 ModelAndView,通过视图解析器找到指定视图
5.视图将结果返回到客户端

12.bean的创建方式

1:调用构造器创建Bean(无参和有参构造方法)

2:调用静态工厂方法创建Bean(场景:把一些三方的类交由spring来管理)

3:调用实例工厂方法创建Bean(场景:整合Hibernate,把创建工程类的实例由spring管理)

Mybatis面试题

1.通常一个mapper.xml文件,都会对应一个Dao接口,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?

dao如何和xml文件关联
Mapper 接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Mapper接口生成代理对象proxy,代理对象会拦截接口方法,根据类的全限定名+方法名,唯一定位到一个MapperStatement并调用执行器执行所代表的sql,然后将sql执行结果返回。

Mapper接口里的方法,是不能重载的,因为是使用 全限名+方法名 的保存和寻找策略。

Dao接口即Mapper接口。接口的全限名,就是映射文件中的namespace的值;接口的方法名,就是映射文件中Mapper的Statement的id值;接口方法内的参数,就是传递给sql的参数。

当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MapperStatement。在Mybatis中,每一个SQL标签,比如、、、标签,都会被解析为一个MapperStatement对象。

举例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespace为com.mybatis3.mappers.StudentDao下面 id 为 findStudentById 的 MapperStatement。

2.Mybatis的一级、二级缓存

(1)一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。

(2)二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置 ;

(3)对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear 掉并重新更新,如果开启了二级缓存,则只根据配置判断是否刷新。

SpringBoot、SpringCloud面试题

1.springboot的优点

简化开发,提高整体生产力
Spring Boot 使用 JavaConfig 有助于避免使用 XML,同时避免大量的Maven导入和各种版本冲突
Spring Boot 引导的应用程序可以很容易地与 Spring 生态系统集成,如Spring JDBC、Spring ORM、Spring Data、Spring Security等等
Spring Boot 应用程序提供嵌入式HTTP服务器,如Tomcat和Jetty,可以轻松地开发和测试web应用程序。
Spring Boot 提供命令行接口工具,用于开发和测试应用程序
Spring Boot 提供了多种插件,可以使用内置Maven工具开发和测试 应用程序
Spring Boot 没有单独的 Web 服务器需要,这意味着不再需要启动 Tomcat或其他任何东西

2.Springboot自动装配的原理

1@EnableAutoConfiguration注解、 @Configuration注解和 @ConditionalOnClass注解组成了Spring Boot自动配置的核心,
首先它得是一个配置文件,其次根据类路径下是否有这个类去自动配置。具体是通过maven读取每个starter中的spring.factories文件,
该文件配置了所有需要被创建在spring容器中的bean。

3.springboot 的核心注解

Spring Boot 的核心注解是@SpringBootApplication,它也是启动类使用的注解,主要包含了 3 个注解:
@SpringBootConfiguration:它组合了 @Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:具有打开自动配置的功能,也可以关闭某个自动配置的选项。
@ComponentScan:用于Spring组件扫描。

4.SpringCloud的常用组件

1.注册中心:eureka(基本不用了)、zookeeper、consul 、Nacos。
2.服务调用:Ribbon、Feign(基本不用)、OpenFeign(Feign的替代品)
3.服务降级、熔断:Hystrix(停更了,不推荐)、sentinel。
4.服务网关:Zuul(弃用)、gateway。
5.配置中心:Config(弃用)、Apollo、Nacos。
6.服务总线:Bus(弃用)、Nacos。

5.服务降级、熔断、降级

服务降级:fallback,如果系统不可用有兜底的方法,给予消费者识别。
服务熔断:保险丝,直接拒绝访问。服务降级-->进行熔断-->恢复调用链路
服务限流:limit 流量太多,不要让服务器打满。 sentinel流控两个维度,一个qps,还有一个是线程数的阈值

6.分布式事务

两阶段提交
会有TM来管理这个请求的服务,只有所有的服务都percommit,才会发出通知来commit,如果有一个失败或者超时就rollback。

7.ribbon的原理

restTemplate + @LoadBalanced注解实现负载均衡(通过注册中心上服务信息map(会有所有服务的名称和地址的映射管理,替换掉服务名称))

8.Sentinel限流的底层原理是

滑动窗口算法

9.seata原理

默认AT模式是二阶段提交的事务:
提交异步化,非常快速地完成。
回滚通过一阶段的回滚日志进行反向补偿。

mysql、redis面试题

Redis 的数据类型:
String,list,set,zset,hash
String字符串存入最大的容量是512m
常见的操作就是set,get
自增就是 incr decr
redis中的lists在底层实现上并不是数组,而是链表

所以支持从左边新增和从右边新增 lpush rpush ,取出的话就是 lrange
List可以简单的实现一个消息队列。
set集合相关的操作也很丰富,如添加新元素、删除已有元素、取交集、取并集、取差集 实际应用常见列入qq贴标签。
sadd sismember 判断是否存在 smembers 查看集合元素 sunion 两个集合取并集
有序集合 zadd myzset 1 baidu.com 列出集合 zrange myzset 0 -1

聊聊redis持久化 – 两种方式
RDB 将redis存储的数据生成快照并存储到磁盘 AOF将redis执行过的所有写指令记录下来,数据恢复的时候,把所以指令执行下
如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效
AOF文件重写机制(rewrite),将指令压缩,例如,100条incr 直接压缩成一个set,缺点就是数据恢复速度慢,占用磁盘空间大。

主从复制
主服务器会将持久化好的RDB文件发送给从服务器,然后再将其读取到内存中
Redis主从复制的缺点:没有办法对master进行动态选举,需要使用Sentinel机制完成动态选举
哨兵模式
哨兵模式是基于主从模式的,确定是无法在线扩容
在Master主服务器发生故障的时候,可以实现Master和Slave服务器的切换
Sentinel是独立于redis的进程,来监控redis的健康,端口为26379

缓存穿透:redis和数据库中都没有这个数据,然后请求直接打到了服务器上面。
解决方案:1.在接口层面加上校验 2.当查询没有数据库的时候,将key-value对写为key-null,然后过期时间设置为30秒。

缓存击穿:当一个key失效了,然后大量请求这个key(微博热搜),把db压垮。
解决方案:设置热点数据永不失效(做个定时任务完成)

缓存雪崩:大量的数据失效,然后打到db上。
解决方案:设置热点数据永不失效,或者是把随机生成失效时间。

Redis 分布式锁
当且仅当 key 不存在,将 key 的值设为 value。若给定的 key 已经存在,则 SETNX 不做任何动作。Redisson。

Redis如果保证和mysql的数据一致
简单优化,每次查询先查缓存没有然后再查数据库,再回写到redis中。就是先更新的话,就会先删除redis的缓存,然后在更新mysql.
高级:2. 直接通过Cannal组件,监控mysql中的binlog的日志,把更新后的数据同步到redis。

jvm面试题

1.有哪些类加载器

1.启动类加载器Bootstrap ClassLoader ,负责加载JAVA_HOME\lib目录中并且能被虚拟机识别的类库到JVM内存中。
2.扩展类加载器 Extension ClassLoader,该加载器主要是负责加载JAVA_HOME\lib\,该加载器可以被开发者直接使用
3.应用程序类加载器Application ClassLoader(默认),该类加载器也称为系统类加载器,它负责加载用户类路径(Classpath)上所指定的类库
4.自定义加载器。

2.阐述双亲委派模型

1.当加载器收到加载类的请求是,会优先让父加载器加载。
2.使用这种模型来组织类加载器之间的关系的好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,而且保证一个类就有一个加载器加载

3.自定义类加载器加载类

1.class MyClassLoader extends ClassLoader
		//新建一个类加载器
        MyClassLoader cl = new MyClassLoader("myClassLoader");
        //加载类,得到Class对象
        Class<?> clazz = cl.loadClass("classloader.Animal");
        //得到类的实例
        Animal animal=(Animal) clazz.newInstance();
        //
        animal.say();

4.类加载过程

加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸载(Unloading)七个阶段

5.内存划分

线程共享:堆(对象)、方法区(常量,静态变量,class文件)

线程私有:虚拟机栈、本地方法栈、程序计数器。

堆中又分为新生代和老年代(1:2),新生代中又分为eden,from,to(8:1:1)

虚拟机栈中包含区域:操作数栈,局部变量表,动态连接(多态),完成出口。

垃圾回收算法:新生代eden到from标记清除算法,from到to复制算法,老年代标记整理算法。

6.jvm设置的主要运行内存参数

-Xms 30m 堆初始大小

-Xmx 30m 堆最大大小

-Xss 1m 虚拟机栈大小

-XX:MaxMetaSpaceSize=30m 元空间大小

7.线上问题排查

7.1怎么排查死锁

  1. jps -l 查询出正在运行的java 进程
  2. jstack -l 12316 查看堆栈信息

7.2 cpu飙升

  1. top查看cpu占用高的进程号
  2. top -p 进程号 +H ,进去进程号内部获取cpu高的线程号
  3. jstack 进程号
  4. 有可能是gc线程,那么就需要 jmap -histroy pid 查看对象占用内存信息

可视化工具有:jconsole可以查看堆栈信息、jvisualvm jvis vm

web和安全面试题

1.Xss攻击

xss攻击:即是攻击者利用前台的输入框,中输入不合法的内容,嵌入到程序中,可找造成劫持用户会话,或者插入恶意的内容,重定向用户等。
解决方案:通过三方的jsoup包来校验接口请求参数,尽量采用POST而非GET提交表单;对“<”,“>”,“;”,“””等字符做过滤;

2.csrf攻击

csrf攻击:用户先登录了网站A,又登录了网站B,但是网站B获取了网站A的cookie并且去访问网站A,这样的恶意攻击。
解决方案:每一个词请求,都会去先请求一个csrftoken,然后将未加密的token放到header中,放在讲加密的token放入到cookie里面,然后服务器校验这两个字段是否一致。

3.点击劫持

场景:可能网页上面被铺上了一个透明的网页,然后诱导用户去点击。
解决方案:请求头中有个x-frame-option的属性设置,设置为sameorigin,来保证同域名来才可以用iframe嵌套显示。

4.URL跳转漏洞

定义:借助未验证的URL跳转,将应用程序引导到不安全的第三方区域,从而导致的安全问题。
解决方案:Content-Security-Policy,设置网站来源地址。

5.sql注入

利用防sql注入框架,如mybaits中不允许使用${}

6.文件校验

文件校验 ,校验文件大小,校验文件类型(文件头)。

7.横向越权校验

实现角色其访问资源关联。角色下的用户只能访问其下的接口资源。

8.对称加密

对称加密:常用的是AES(高级加密标准)算法。加密和解密都是使用的同一种算法。
非对称加密:常用的是RSA算法(互为质素的原理)。它使用了一对密钥,公钥(public key)和私钥(private key)
公钥所有人都可以持有,私钥只能验证者持有(信箱模型)。
实际应用场景是,是消息用对称加密进行加密,然后把密钥和加密信息,通过公钥加密发送出去。

zookeeper、kafka面试题

1.CAP理论和BASE理论

CAP 是指:一致性 Consistency、可用性 Availablity、分区容错性 Partition Tolerance
zookeeper为cp,一致性和分区容错性,erauke 是Ap.

nacos ap和cp在使用的时候可以切换;

cap理论中的一致性是强一致性,是很难做到的,所以就有了base理论,保证最终一致性即可。分为基本可用性、软状态、最终一致性。

2.zookeeper

观察者模式的分布式服务管理框架,文件系统+通知机制
zookeeper只要半数以上的机器存活,集群就可以运转了。每个节点的大小为1mb的数据。

3.zookeeper的选举机制

第一次的话,前两台机器都会来选自己,因为票数都没有超过半数,所以不确定master,第三台机器来启动的时候,也会投给自己因为机器myid最大,所以其它机器也会投给他,所以超过半数,所以选举为leader,后续集群,那么选票都没3多,所以leader不变了。

后面如果leader挂的话,那么选举规则是按照,epoch(每个leader任期的id),zxid(变更的事务id),sid(机器id)按照顺序比较,分别谁大谁胜出。

4.zookeeper的分布式锁原理。

1.原理每个客户端连接zk集群,在locks目录下创建一个临时带序号的锁(序号是自增的)
2.判断自己是不是当前的最小节点,是则获取锁,不是的话,就对上一个节点进行监听(watch)
3.获取到锁处理完后,则通过delete删除节点,且下一节点获取

5.kafka

是一个高可用、高吞吐量的消息系统。
好处:削峰;解耦模块;异步处理。
broker – partition --replication
AR是所有副本的统称,ISR与leader保持一定程度一致的副本,OSR与leader同步滞后的副本。HW:高水位,表示消费者消费到哪了。
ack 这个配置参数是确认kafka的消息写成功的配置。0,发出就认为写成功。1(默认)只要集群leader收到确认写成功。-1,所有节点都收到了,才能确认。
确认分区的算法是 hash(key)% partitionNum,当key为null的时候,存储到哪个partition呢?先到缓存里面找null的时候指定的分区,如果没有就随机生成一个,放入到缓存中,如果在此失效,就需要再次随机。

6.kafka的leader选举机制

1.首先由zookeeper在boker中选举出一个控制器,控制器的选举策略和zookeeper的leader选举策略是一样的。然后由控制器来选举副本leader。
2.每个broker,都在会到控制器上注册一个watch,如果控制器挂了就重新选举控制器。
3.控制器选举partition的leader,首先会到ISR的集合中去找,然后根据配置的分区选择算法选择分区leader。

7.kakfa的存储结构

1.每个partition相当于一个巨型文件被平均分到多个大写相等的segment(段)数据文件里。
但是每个段 消息数量不一定相等,这个特性便于老数据快速删除,默认情况下一个文件是一个g
2.每个partition仅仅须要支持顺序读写就可以了。segment生命周期由服务器端配置决定。
segment file 组成:index file 和 data file
日志索引 通索引文件二分查找和offset来实现快速查找。

8.kafka重复消费的解决方案

生产端1.启动kafka幂等性
消费端:1.取消自动提交,2.下游做幂等性。

9.kafka消息丢失解决方案

生产者到server丢失:
如果是网络问题,服务器端没有收到,将ack配置为服务器端的ack配置为1,
如果服务器端Leader保存上了,follower未保存上,然后Leader出现问题,导致消息丢失,则把ack改为-1。
消费者消费时候丢失:
关闭自动提交配置,只有在消费完成后才会手动提交offset

自我介绍

项目阐述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值