java面试整理

目录

1、JDK、JRE、JVM区别和联系

2、==和equals的区别

3、final

4、String、StringBuffer、StringBuild

5、重载和重写的区别

6、接口和抽象类

7、list和set的区别

8、hashCode和equals

9、ArrayList和linkedList区别

10、HashMap和HashTable的区别

11、ConcurrentHashMap原理,jdk7和jdk8版本的区别

12、如何实现一个IOC容器

13、java类加载器有哪些

14、什么叫双亲委派机制?

15、什么是字节码?

16、JAVA中异常体系

17、Gc如何判断对象可以被回收

18、线程的生命周期,线程有哪些状态

19、sleep()、wait()、join()、yield()的区别

20、线程安全的理解

21、Thread、Runable的区别

22、ThreadLocal的原理和使用场景

23、并发、并行、串行的区别

24、并发的三大特性

25、为什么用线程池?解释下线程池的参数?

26、线程池中线程复用原理

27、Spring是什么

28、谈谈你对AOP的理解

29、谈谈你对IOC的理解

30、BeanFactory和ApplicationContext有什么区别

31、springBean生命周期

32、spring benan的作用域

33、Spring框架中的单例Bean是线程安全的吗?

34、Spring框架中都用到了那些设计模式

35、事务的并发问题

36、Spring事务的实现方式以及隔离级别

37、spring事务的传播机制

38、Spring事务什么时候失效

39、什么是bean的自动装配,有哪些方式

40、Spring boot、SpringMVC和Spring有什么区别

41、SpringMVC工作流程

42、spring中的九大主键

43、springboot自动配置原理

44、如何理解springboot中的Starter

45、什么事嵌入式服务器?为什么要使用嵌入式服务器

46、mybatis的优缺点

47、#{}和${}的区别

48、简述Mybatis的插件运行原理,如何编写一个插件

49、索引的基本原理

50、java创建对象的几种方式

52、加载ApplicationContext.xml文件的方式有哪些?

52、Spring创建对象

53、依赖注入

54、AOP底层原理

55、全局异常捕获

56、实现拦截器的步骤

57、SpringMVC和Spring容器的关系

58、mysql锁的类型有哪些

59、事务的特性和隔离级别

Redis

60、Rides中的RDB和AOF机制

61、redis 内存淘汰机制

62、redis为什么是单进程单线程

63、为什么redis需要把所有数据放到内存中?

64、redis支持的数据类型

65、是否使用过Redis集群,集群的原理是什么?

66、使用redis有哪些好处?

67、redis的回收策略

68、redis 设置过期时间

69、缓存雪崩、缓存穿透、缓存击穿

70、Redis事务

         71、Shiro

(手打,可能会有些单词拼写错误,见谅~)


1、JDK、JRE、JVM区别和联系

jvm:java虚拟机。它只认识 xxx.class 这种类型的文件,它能够将 class 文件中的字节码指令进行识别并调用操作系统向上的 API 完成动作。所以说,jvm 是 Java 能够跨平台的核心

jre:运行环境。它主要包含两个部分,jvm 的标准实现和 Java 的一些基本类库。它相对于 jvm 来说,多出来的是一部分的 Java 类库。

jdk:开发工具包。包含jre和jvm。

2、==和equals的区别

==:用于比较引用和比较基本数据类型时具有不同的功能。

equal:用来检测两个对象是否相等,即两个对象的内容是否相等。

3、final

被final修饰的变量不能被改变,被final修饰的类不能被继承,被final修饰的方法不能被重写。

4、String、StringBuffer、StringBuild

String:是一个不可变字符串,底层使用final修饰

  • 在修改字符串操作比较多的时候用StringBuilder或StringBuffer.

  • 在要求线程安全的情况下用StringBuffer

  • 在不要求线程安全的情况下用StringBuilder

5、重载和重写的区别

  • 重载:方法名一致,参数列表不同,参数类型不同。与返回值无关

  • 重写:方法名一致,参数列表一致,返回类型一致,存在子类中。

6、接口和抽象类

  • 抽象类:只能单继承。可以存在普通成员函数。成员变量可以是多种类型的。

  • 接口:多实现。只能只存在public abstract方法。只能是 publi static final类型的。

7、list和set的区别

  • list:有序可重复。按对象进入顺序保存对象,允许多个Null元素对象,可以用iteratiterator遍历取出所有元素。在逐一遍历,还可以使用get(int index)获取指定下标元素。

  • set:无序不可重复最多允许一个Null元素对象,取元素时只能通过iteratoriterator取得所有元素,在逐一遍历各个元素。

8、hashCode和equals

  • 两者都是比较对象

  • equals比较,比较的比较全面、比较复杂,性能低,所以加上hashCode取得hash码更加快速,准确。

  • 直接使用hashCode来比较的话,不同的对象可能会拥有相同的hash码,所以比较的结果不是那么的准确。

9、ArrayList和linkedList区别

  • ArrayList基于数组实现,连续存储在内存中。从中间插入元素比linkedList慢,因为他会将其之后的元素复制,再写入。从末尾添加就不会比linkedList慢~

  • linkedList基于链表实现,分散在内存中。

10、HashMap和HashTable的区别

  • HashMap:非线程安全的,允许key和value为null。

    • 底层基于链表和数组实现。java8开始链表高度达到8,数组长度超过64,链表转化为红黑树,元素以内部类Node节点存在。

    • key为nul,存在下标0的位置。

  • HashTable:线程安全的,每一个方法synchronized修饰。不允许key和value为null

11、ConcurrentHashMap原理,jdk7和jdk8版本的区别

jdk1.7

  • 数据结构:ReentrantLock+Segment+HashEntity,一个Segment中包含一个HashEntity数组,每个HashEntity又是一个链表结构

jdk1.8

  • synchronized+CAS(乐观锁)+Node+红黑树,Node的val和next都用volatile修饰,保证可见性,查找、替换、赋值操作都是使用CAS

12、如何实现一个IOC容器

  • 配置文件配置包扫描路径

  • 递归包扫描获取.class文件

  • 反射、确定需要交给ioc管理的类

  • 对需要注入的类进行依赖注入

13、java类加载器有哪些

  • 根加载器,bootstraClassLoader

  • 扩展加载器:ExtenuationClassLoader

  • 应用程序加载器:AppClassLoader

  • 自定义加载:比如阿里巴巴觉得JDK8不好用,自己编写一个alibbaJDK.(高端定制)

14、什么叫双亲委派机制?

  • 类加载器去加载类的时候,从上往下加载,加载到了类,后边的就不加载了。

双亲委派机制的作用就是保证程序的安全

15、什么是字节码?

.class文件中的二进制数据就是字节码。

代理类要实现的接口是MethodInterceptor

而且需要一个增强剂的类(Enhancer

16、JAVA中异常体系

java中的所有异常都来自顶级父类Throwable。Throwble下有两个子类Exception和Error

  • Exeception:不会导致程序终止。分为runTimeExeception和CheckedExeception检查异常。

  • Error:表示程序无法处理的错误,一旦发生错误,将停止程序运行。如内存溢出

17、Gc如何判断对象可以被回收

  • 强引用:就算内存不足也不会被回收

  • 弱引用:内存不足时,发现就会被回收

  • 软引用:发现就会被回收

  • 虚引用:形同虚设,随时都会被回收

18、线程的生命周期,线程有哪些状态

  • 线程通常有五种状态:

    • 创建(新建)

    • 就绪

    • 运行

    • 阻塞

    • 死亡

  • 阻塞的情况又分为三种

    • 等待阻塞:运行线程执行wait()方法会使线程处于等待状态,不能被自动唤醒,必须依靠其它线程调用notifyAll()方法

    • 同步阻塞:运行线程在获取对象的同步锁时,若该同步被其他线程占用,则JVM会把线程放入“锁池”中

    • 其他阻塞:运行的线程执行sleep或join方法,或者发出I/O请求时,JVM会把该线程置为阻塞状态。当sleep状态超时,join等待线程终止或者超时,或者I/O处理完毕,线程会重新进入就绪状态。

19、sleep()、wait()、join()、yield()的区别

  • sleep:sleep 方法是属于 Thread类中的,sleep 过程中线程不会释放锁,只会阻塞线程,让出cpu给其他线程,但是他的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态,可中断,sleep 给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会

  • wait:属于 Object 类中的,wait 过程中线程会释放对象锁,只有当其他线程调用 notify 才能唤醒此线程。wait 使用时必须先获取对象锁,即必须在 synchronized 修饰的代码块中使用,那么相应的 notify 方法同样必须在 synchronized 修饰的代码块中使用,如果没有在synchronized 修饰的代码块中使用时运行时会抛出IllegalMonitorStateException的异常

  • join:等待调用join方法的线程结束之后,程序再继续执行,一般用于等待异步线程执行完结果之后才能继续运行的场景。例如:主线程创建并启动了子线程,如果子线程中要进行大量耗时运算计算某个数据值,而主线程要取得这个数据值才能运行,这时就要用到 join 方法了

  • yield和 sleep 一样都是 Thread 类的方法,都是暂停当前正在执行的线程对象,不会释放资源锁,和 sleep 不同的是 yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。还有一点和 sleep 不同的是 yield 方法只能使同优先级或更高优先级的线程有执行的机会

20、线程安全的理解

多个线程的结果与单线程的结果一致,或者说是多线程运行后的结果是预期的结果

 

21、Thread、Runable的区别

Thread和Runable实际是继承关系,操作复杂线程推荐使用thread,如果只是执行一个简单线程建议使用runable

22、ThreadLocal的原理和使用场景

通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用ThreadLocal创建的变量只能被当前线程访问,其它线程则无法访问及修改。
Thread.currentThread():获取当前线程的引用,既代码段正在被哪一个线程调用。
  • ThreadLocal的实现原理就是ThreadLocal里面有一个静态的ThreadLocalMap

    • ThreadLocalMap是ThreadLocal里面的一个静态类

    • ThreadLocal的值是放入了当前线程的一个ThreadLocalMap实例中,所以只能在本线程中访问,其他线程无法访问。

ThreadLocal类的应用场景:承载一些线程相关的数据

因为javaee三层架构里面,如果使用事务,要在service层里面进行开启事务(因为处理逻辑业务都是service层),然后又因为如果我们要使用事务,那么就必须保证执行sql语句的connection连接和开启事务的connection连接都要保持是同一个对象,所以我们要确保在service层和dao层的两个connection连接都是同一个,但是怎么保证connection连接对象都是同一个呢
 据,service层处理业务),connection连接对象我们应该是在service层出现的,但是你却放到了dao层,这样数据的处理和逻辑业务的处理没有分离开来,javaee的三层开发就没有他的效果了,所以这一种方式的解决方法不好,所以我们就通过ThreadLocal的方式来存储这个connection对象,这样就能够保证在service层和dao层的数据保证一致了

 

23、并发、并行、串行的区别

  • 串行:在时间上不可能发生重叠,前一个任务没弄完,下一个任务就只能等待

  • 并行:在时间上是重叠的,两个任务在同一时刻互不干扰的同时执行

  • 并发:允许两个任务彼此干扰。同一时间点,只有一个任务运行,交替执行

24、并发的三大特性

  • 原子性

    • 不可中断,要么全部执行,要么全部不执行

    • 关键字:synchronized

  • 可见性

    • 对其他线程的可见性,a修改了线程的值,其它线程也能发现

    • 关键字:synchronizedvolatilefinal

  • 有序性

    • 虚拟机在编译时,对于那些改变顺序之后不会对最终结果造成影响的代码,虚拟机不会按照我们期望的循序执行,有可能对他们重新排序,实际上,对于那些代码重新排序之后,虽然对变量的值没有造成影响,但是,可能会出现线程安全问题

    • 关键字:synchronizedvolatile

 

25、为什么用线程池?解释下线程池的参数?

  • 频繁的创建和销毁线程,比较的消耗资源

  • 提高线程的可管理性

corePoolSize:代表核心线程数,也就是正常情况下创建工作的线程,这些线程创建后并不会消除,而是一种常驻线程
maxinumPoolSize:代表最大线程数,表示最大允许创建的数量。
keepAliveTime:代表超出核心线程之外的空闲存活时间。
workQueue:用来存放执行的任务
ThreadFactory:线程工厂,用来生产线程执行任务
Handler:任务拒绝策略。有两种:(1)当我们调用shutdown等方法来关闭线程池后,但在线程池内还有没有完成的任务,我们还想继续提任务,但是由于线程池已经关闭,就会遭到拒绝。(2)当线程已经达到最大线程数,已经没有能力提交新的任务,也会遭到拒绝。

 

26、线程池中线程复用原理

首先线程池会有一个管理任务的队列,这个任务队列里就是存放着各种任务,线程池会一直不停的循环的去查看消息队列里有没有接到任务,如果没有,则继续循环,如果有了则开始创建线程。

 

27、Spring是什么

  • Spring是一个开源框架

  • Spring为简化企业应用开发而生

28、谈谈你对AOP的理解

切面编程,解耦合,不破坏代码结构

29、谈谈你对IOC的理解

控制反转,传统是new一个对象,而用了spring交给spring去管理,创建对象。
第一步:编写applicationContext.xml文件,固定的,不需要自己创建。找个项复制来就行。
第二步:在此文件中配置想交给spring容器管理的对象实例。
第三步:加载applicationContext.xml文件,得到applicationContext对象实例,
第四步:调用getBean方法拿到配置的实例对象。

30、BeanFactory和ApplicationContext有什么区别

BeanFactory:在启动中不会实例化Bean,在拿取的时候才会。

ApplicationContext:启东时就会实例化。

31、springBean生命周期

  • Spring容器根据配置中的bean定义实例化bean。

  • Spring使用依赖注入填充所有属性,如bean中所定义的配置。

  • 如果bean实现BeanNameAware接口,则工厂通过传递自身的实例来调用setBeanName()

  • 如果bean实现了BeanFactoryAware接口,工厂传递自身的实例来调用setBeanFactory()

  • 如果存在与bean关联的任何BeanPostProcessors,则调用preProcessBeforeiniyialization()方法

  • 如果bean指定了init方法(<bean>的init-method属性),那么将调用他。

  • 如果存在与bean关联的任何BeanPostProcessors,则将调用postProcessAfterinitialization()方法

  • 如果bean实现DisposableBean接口,当Spring容器关闭时,会调用destory().

  • 如果为bean指定了destroy()方法(<bean>的destroy-method 属性),那么将调用它。

 

 

32、spring benan的作用域

  • Singleton:每个spring ioc容器仅有一个单实例

  • prototype:每次请求都会产生一个新的实例

  • Request:每次HTTP请求都会产生一个新的实例,且该bean仅在当前Http请求有效

  • session:每次HTTP请求都会产生新的bean,且只在当前Http Session有效

  • global-session:在一个全局的Http Session中一个bean对应一个实例。(该作用域仅在基于web的Spring ApplicationContext情形下有效)

 

33、Spring框架中的单例Bean是线程安全的吗?

不是,Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态(比如Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。最浅显的解决办法就是将多态bean的作用域由“singleton”变更为“prototype”。

34、Spring框架中都用到了那些设计模式

  • 代理模式—在AOP和remoting中被用的比较多。

  • 单例模式—在spring配置文件中定义的bean默认为单例模式。

  • 模板方法—用来解决代码重复的问题。

  • 工厂模式—BeanFactory用来创建对象的实例。

  • 适配器--spring aop

  • 装饰器--spring data hashmapper

  • 观察者-- spring 时间驱动模型

  • 回调--Spring ResourceLoaderAware回调接口

35、事务的并发问题

  • 脏读:一个事务读取到了另一个事物还未提交的数据

  • 虚度:一个事物多次读取同一行数据,读到了不同的结果

  • 幻读:一个事务多次读取同一张表,读到了不同的行数

36、Spring事务的实现方式以及隔离级别

实现方式:加上一个注解@Transactional

隔离级别:

  • 对未提交:啥也不能解决,任由脏读发生

  • 读已提交:解决了脏读问题

  • 可重复读:解决了脏读、幻读(spring默认隔离级别)

  • 串行化度:解决了脏读、虚度、幻读,但是性能差

37、spring事务的传播机制

  • 【掌握】PROPAGATION_REQUIRED: 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务。绝大多数情况都是用这个

  • 【掌握】 PROPAGATION_SUPPORTS: 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行,如果没有事务,我也不会创建。有很小一部分会用到这个传播行为

剩下五个了解,几乎用不到。

  • PROPAGATION_MANDATORY 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常

  • PROPAGATION_REQUIRED_NEW 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当 前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager

  • PROPAGATION_NOT_SUPPORTED 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager

  • PROPAGATION_NEVER 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常

  • PROPAGATION_NESTED 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务

38、Spring事务什么时候失效

  • 数据库引擎不支持事务

  • @transactional加在private方法上(@Transactional只能加在public方法上,如果需要在private方法中加入事务,可以使用Aspect配transactionManager使用.)

  • 异常被catch

  • 异常类型错误

  • 没有被Spring管理

  • 没有配置TransactionManager

39、什么是bean的自动装配,有哪些方式

开启自动装配,只需要在xml配置文件<bean>中定义"autowire"属性。

<bean id="cutomer" class="com.llt.xxx.Customer" autowire=""/>

autowire属性有五种装配方式:

  • no-这是默认设置,表示没有自动装配

  • byName-根据bean属性名称进行自动装配。

  • byType-根据bean的类型进行自动装配

  • contructor 类似byType,不过应用于构造器的参数,如果一个bean与构造器参数的类型相同,则自动装配,否则导致异常

  • autodetect:如果有默认构造器就是用contructor进行装配,否则使用byType进行自动装配

40、Spring boot、SpringMVC和Spring有什么区别

  • SpringFrame SpringFramework 最重要的特征是依赖注入。所有 SpringModules 不是依赖注入就是 IOC 控制反转。 当我们恰当的使用 DI 或者是 IOC 的时候,我们可以开发松耦合应用。松耦合应用的单元测试可以很容易的进行。

  • SpringMVC Spring MVC 提供了一种分离式的方法来开发 Web 应用。通过运用像 DispatcherServelet,MoudlAndView 和 ViewResolver 等一些简单的概念,开发 Web 应用将会变的非常简单。

  • SpringBoot Spring 和 SpringMVC 的问题在于需要配置大量的参数。

41、SpringMVC工作流程

  • 用户发送请求到前端控制器 DispatcherServlet

  • dispatcherServlet 收到请求调用HandlerMapping处理映射器

  • HandlerMapping找到具体的处理器,生成处理器及拦截器一并返回给DispatcherServlet

  • DispatcherServlet 调用HandlerAdapter处理适配器

  • HandlerAdpter经过适配器调用具体的controller

  • Controller执行完成返回ModelAndView

  • HandlerAdapter将controller执行结果返回给dispatcherServlet

  • DispatcherServlet将ModelAndView传给视图解析器ViewReslover

  • ViewReslover解析后返回具体的视图view

  • 前端控制器根据视图进行视图渲染

  • 最后DispatcherServlet响应给客户

42、spring中的九大主键

  • HandlerMapping:处理映射器

  • HandelrAdapter:适配器

  • HandlerExeptionReslover:捕获异常,交给render方法进行渲染

  • ViewResplver:视图解析器

  • RequestToViewNameTranslator:根据ViewNma查钊View

  • LocaleResolver:国际化

  • ThemeRsolver:解析主题

  • MultiparReslover:文件上传

  • FlashMapManager:用来管理Flash,主要用在重定向传递参数

43、springboot自动配置原理

@import + @Configuration + Spring spi
自动配置由各个starter提供,使用@Configuration+@Bean定义配置类,放到META-INF/spring。factories下
使用spring spi烧苗平META-INF/搜spring.factories下的配置类
使用@import导入自动配置类
@Configuration是一个符合注解,复合注解里边有一个@EnableAutoConfiguration,这个注解的作用就是开启自动装配。

 

44、如何理解springboot中的Starter

使用spring+SpringMVC,如果需要映入mybatis等框架,需要到xml中定义mybatis需要的bean

starter就是定义一个starter的jar包,写一个@Configuration配置类,将这些bean定义在里面,然后在starter包的META-IN/spring。factories中写入该配置类,springboot会按照约定来加载该配置类

开发人员只需要将相应的starter包依赖拖进应用,进行相应的属性配置,就可以直接进行代码开发,使用对应的功能。

 

45、什么事嵌入式服务器?为什么要使用嵌入式服务器

节省了下载安装tomcat,应用也不需要再打war包,然后放到webapp目录下运行

只需要安装了java虚拟机,就可以直接在上面部署应用程序了

springboot已经内置了tomcat.jar,运行main方法时会去启动tomcat,并利用tomcat的spi机制加载SpringMVC

46、mybatis的优缺点

  • 基于SQL语句编程,解除sql与程序代码的耦合,并重用

  • 与JDBC相比,消除了大量冗余代码

  • 很好的与各种数据库兼容

  • 能够与spring很好地集成

缺点:

  • sql语句的编写工作量大,对开发人员编写sql语句的功底有一定要求

  • sql语句因爱数据库,移植性差,不能随意更换数据库

47、#{}和${}的区别

#{}:预编译,占位符是以拼接字符连接,提高安全性

${}:字符串替换,是拼接符,容易造成sql注入

 

48、简述Mybatis的插件运行原理,如何编写一个插件

Mybatis 仅可以编写针对 ParameterHandler、ResultSetHandler、StatementHandler、Executor 这 4 种接口的插件,Mybatis 使用 JDK 的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是 InvocationHandler 的 invoke()方法,当然,只会拦截那些你指定需要拦截的方法。

编写插件:实现 Mybatis 的 Interceptor 接口并复写 intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。

 

49、索引的基本原理

  • 索引用来快速的寻找那些具有特定值的记录,如果没有索引,一般来说执行查询时遍历整张表

  • 索引的原理就是把无序的数据编程有序的查询

50、java创建对象的几种方式

有4种显式地创建对象的方式:

1.用new语句创建对象,这是最常用的创建对象的方式。

2.运用反射手段,调用Java.lang.Class或者java.lang.reflect.Constructor类的newInstance()实例方法。

3.调用对象的clone()方法。

4.运用反序列化手段,调用java.io.ObjectInputStream对象的readObject()方法.

52、加载ApplicationContext.xml文件的方式有哪些?

  • ClassPathXmlApplicationContext 对象加载

    • 从classpath路径下面去加载配置文件

  • FileSystemXmlApplicationContext

    • 从系统磁盘中加载配置文件

52、Spring创建对象

  • 有参构造:就是需要参数才能够创建对象

  • 无参构造:参构造就是利用反射类的newinstance方法创建对象。

  • 静态工厂:创建一个工厂类,直接类名.方法调用

  • 非静态工厂:先把工厂实例创建,因为非静态方法只能被实例调用

53、依赖注入

  • set注入

  • 构造函数注入

  • 注解注入:@Resource和@Autowired注解注入

54、AOP底层原理

就是动态代理

JDK动态代理和CGLIB动态代理:

  • JDK动态代理:首先是利用反射来实现的,它是面向接口的,也就是被代理的目标类需要实现接口才可以被代理,反射的缺点就是性能不高。

    • 重要方法

       public static Object newProxyInstance(ClassLoader loader,
                                                Class<?>[] interfaces,
                                                InvocationHandler h)

      有三个参数,类加载器,接口反射类的数组,InvocationHandler

      代理类要实现的接口就是InvocationHandler。

55、全局异常捕获

  • @ControllerAdvice:如果有页面返回,那么就是用@ControllerAdvice

  • @RestControllerAdvice:如果只是返回JSON数据,那么就用@RestControllerAdvice

56、实现拦截器的步骤

实现HandlerInterceptor接口,覆写其三个方法,并且交给Spring容器管理,也就是加上@Component注解。其中三个方法的名称:

  • boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)//在控制器方法请求之前执行
  • void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView)//在控制器方法执行完成之后执行。
  • void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex)//视图渲染之后执行

 

57、SpringMVC和Spring容器的关系

在Spring整体框架的核心概念中,容器是核心思想,就是用来管理Bean的整个生命周期的。Spring和SpringMVC的容器存在父子关系,即Spring是父容器,SpringMVC是其子容器,子容器可以访问父容器的对象,父容器不能访问子容器的类。**例如:Controller中能调用Service,但是Service不能调用Controller。Spring父容器负责所有其他非@Controller注解的Bean的注册,而SpringMVC只负责@Controller注解,处理器映射、视图解析器的Bean的注册,使得他们各负其责、明确边界。配置到子容器的只能是子容器自己访问,配置到父容器的,父子容器都能访问。

 

58、mysql锁的类型有哪些

基于所得属性分类:共享锁,排它锁

基于锁的粒度分类:行级锁,表级锁,页级锁,间歇锁,临建锁

59、事务的特性和隔离级别

事务基本特性ACID分别是:

  • 原子性:要么全部成功,要么要不失败

  • 一致性:事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少

  • 隔离性:一个事务的修改在最终提交前对其他事物是不可见的

  • 持久性:一个事务一旦提交,事物的操作便永久性的保存在DB中。即使此时再执行回滚操作也不能撤消所做的更改

Redis

60、Rides中的RDB和AOF机制

  • RDB:(快照持久化,默认开启),Redis创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis主从结构,主要用来提高Redis性能),还可以将快照留在原地以便重启服务器的时候使用。

  • AOF:AOF持久化 的实时性更好,默认没有开启,开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof。

 

61、redis 内存淘汰机制

redis 提供 6种数据淘汰策略:

  • volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

  • volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

  • volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)

  • allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

  • no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!

 

62、redis为什么是单进程单线程

因为单线程代码清晰,线程安全,不用去考虑各种锁

63、为什么redis需要把所有数据放到内存中?

redis为了达到最快的读写速度,将数据都读到内存中,并通过异步的方式将 数据写入磁盘;所以redis具有快速和数据持久化的特征;

64、redis支持的数据类型

  • string值是字符串类型

  • list 值是一个集合

  • set 值是一个无重复数据的集合

  • sorted set 值是一个无重复数据并且排序的集合

  • hash 值可以理解是一个对象;

65、是否使用过Redis集群,集群的原理是什么?

Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master继续提供服务。//哨兵
Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。//集群

66、使用redis有哪些好处?

(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
(2) 支持丰富数据类型,支持``string``,list,``set``,sorted ``set``,hash
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除

67、redis的回收策略

最近最少使用的数据淘汰,将要过期的数据淘汰,已经过期的数据淘汰

68、redis 设置过期时间

我们 set key 的时候,都可以给一个 expire time,就是过期时间,通过过期时间我们可以指定这个 key 可以存活的时间。

定期删除:redis默认是每隔 100ms 就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意这里是随机抽取的。为什么要随机呢?你想一想假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载!

惰性删除 :定期删除可能会导致很多过期 key 到了时间并没有被删除掉。所以就有了惰性删除。假如你的过期 key,靠定期删除没有被删除掉,还停留在内存里,除非你的系统去查一下那个 key,才会被redis给删除掉。这就是所谓的惰性删除,也是够懒的哈!

69、缓存雪崩、缓存穿透、缓存击穿

缓存雪崩:缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。

  • 解决办法

    • 事前:尽量保证整个 redis 集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。

    • 事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL崩掉

    • 事后:利用 redis 持久化机制保存的数据尽快恢复缓存

 

缓存穿透:一般是黑客故意去请求缓存中不存在的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。

  • 解决办法:

    • 有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

缓存击穿:缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力

  • 解决办法

    • 设置热点数据永远不过期。

    • 加互斥锁,互斥锁参考代码如下:

      public static String getData(String key) throws InterruptedException{
          //从缓存读取数据
          String result = getDataFromRedis(key);
          //缓存中不存在数据
          if (result == nul1){
              //去获取锁,获取成功,去数据库取数据
              if (reenLock.tryLock()){
                  //从数据库获取数据
                  result = getDataFromMysql(key);
                  //更新缓存数据
                  if(result != null){
                      setDataToCache(key,result);
                  }
                  //释放锁
                  rennLock.unlock();
              }
              //获取锁失败
              else{
                  //暂停100ms再重新去获取数据
                  Thread.sleeo(100);
                  result = getData(key);
              }
          }
          return result;
      }

      说明:

            1)缓存中有数据,直接走上述代码13行后就返回结果了
      
           2)缓存中没有数据,第1个进入的线程,获取锁并从数据库去取数据,没释放锁之前,其他并行进入的线程会等待100ms,再重新去缓存取数据。这样就防止都去数据库重复取数据,重复往缓存中更新数据情况出现。
      
            3)当然这是简化处理,理论上如果能根据key值加锁就更好了,就是线程A从数据库取key1的数据并不妨碍线程B取key2的数据,上面代码明显做不到这点

70、Redis事务

Redis 通过 MULTI、EXEC、WATCH 等命令来实现事务(transaction)功能。在传统的关系式数据库中,常常用 ACID 性质来检验事务功能的可靠性和安全性。在 Redis 中,事务总是具有原子性(Atomicity)、一致性(Consistency)和隔离性(Isolation),并且当 Redis 运行在某种特定的持久化模式下时,事务也具有持久性(Durability)。

71、Shiro

登录流程: 首先前端用户传入用户名个密码,后台创建UsernamePasswordToken,将用户名和密码封装到这个对象中,然后用SecuirityUtil.getSubject()获得Subject对象,Subject对象就是shiro用来登录的对象,调用login(token)方法来执行登录操作,就会进入UserRealm中的dogetAuthcation认证方法,对login方法进行异常捕获,如果抛出异常,则登录失败,如果没有抛出异常,doGetAuthcation返回一个简单的登录信息,表示用户登录成功。

登录成功之后,用注解@RequireRoles和RequirePermissions验证该用户是否有角色和权限。

 

 

(手打,可能会有些单次拼写错误,见谅~)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值