面试八股(自用)

发布于:2024-10-17 ⋅ 阅读:(7) ⋅ 点赞:(0)

什么是java序列化,什么时候需要序列化?

序列化是指将java对象转化成字节流的过程,反序列化是指将字节流转化成java对象的过程。

当java对象需要在网络上传输 或者 持久化到存储文件中,就需要对java对象进行序列化处理。

JVM的主要组成部分,描述一下类加载过程?

java代码在编译之后,不会直接编译成操作系统可以识别的机器码,而是编译成只有jvm可以识别的字节码。jvm充当翻译的作用,动态的将java代码翻译成不同操作系统可以识别的机器码。即实现了一次编译,处处运行--->跨平台

组成部分

1.类加载器(Class Loader):jvm将java代码 编译字节码之后,jvm可以将字节码读入到内存里,从而进行解析运行等过程,该过程称为类加载机制。

2.运行时数据区(Runtime Data Area):jvm将管理的内存划分成不同的区域,即不同的数据放在不同的地方。分为:方法区,堆区,虚拟机栈,程序计数器,本地方法栈

3.执行引擎(Execution Engine):代码运行由执行引擎完成

 

类加载的过程

1.加载:查找并加载类的二进制数据,生成class对象实例

2.链接:当类加载到jvm内存区后,开始链接操作

2.1验证阶段,检查格式,语义,字节码,符号引用。

2.2准备阶段,为类的静态变量 分配内存初始化为默认值

补充:静态变量和实例变量的区别

  • 静态变量:存储在方法区中,只有一份,属于类,所有的实例都共享同一个静态变量。
  • 实例变量:存储在堆内存中,每创建一个实例,就会在堆内存中为该实例分配一块内存区域来存储实例变量,每个实例都有自己独立的实例变量。

2.3解析阶段,将类,接口,字段和方法的符号引用转为直接引用(即JVM可以直接使用内存地址来访问目标)

3.初始化:将静态变量初始化为指定的值,并执行静态代码块。该阶段是类装载的最后一个阶段,此时类才会开始执行java字节码

4.使用:可以在程序中访问和调用静态成员,以及new创建对象实例

5.卸载:当该类的所有实例都被回收,该类就会被卸载

动态代理是什么,有什么应用,如何实现动态代理?

动态/静态 代理讲解

动态代理:动态代理:在运行时,创建目标类,可以调用和扩展目标类的方法。

例如:客户要找老板,但是客户多老板少,此时人力公司(JDK/CGLIB)动态派遣秘书解决客户的问题,客户只需根据需求动态获取秘书即可。

人力公司JDK需要目标类实现它的接口,而CGLIB需要继承目标类。

如果代理类实现了接口,就使用JDK代理,否则使用CGLIB代理

JDK底层通过反射调用实现动态代理,CGLIB是通过实时调用实现动态代理。

应用场景如

  • 统计每个 api 的请求耗时

  • 统一的日志输出

  • Spring的 AOP 功能模块就是采用动态代理的机制来实现切面编程

数据库事务的隔离级别,请描述一下乐观锁和悲观锁?

 先明确事务四大属性:原子性、一致性、隔离性、持久性。

原子性:要么全部成功要么全部失败回滚。

一致性:指事务状态的一致性,比如A和B互相转账的钱一共是1000,不管转几次,如何转,都要是1000

隔离性:不被其他事务干扰

持久性:对数据库的更改是永久的

在数据库操作中,并发的情况下可能出现如下问题

更新丢失(Lost update),脏读(Dirty Reads),不可重复读(Non-repeatable Reads),幻读

更新丢失:后修改的覆盖前面修改的

脏读:A事务读取到B事务修改但未提交的数据,此时B执行回滚,那么A读到的数据就是经过B修改的脏数据

不可重复度:一个事务对同一行数据重复读取两次,但是却得到了不同的结果。

幻读:虽然查出来的数据结果可能是正确的,但是肯定会影响数据行状态的一致性。这里的数据行指的是,我一开始预期是有10条记录,结果却有11条记录,并且由于事务的隔离,我看不到有11条记录,还以为10条记录。 

事物的隔离级别以及对应可以解决的并发问题(隔离级别由低到高): 

Read uncommitted(读未提交)-->解决更新丢失,可能出现脏读

举例:如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据

Read committed(授权读取、读提交)-->解决脏读,可能出现不可重复读

举例:读事务允许其他事务访问该行数据,但未提交的写事务禁止其他事务访问该行数据

Repeatable read(可重复读取)-->解决不可重复读取和脏读,可能出现幻读

举例:读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务

Serializable(序列化)-->解决脏读、不可重复读,幻读

最严格的事务隔离,它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行

乐观锁和悲观锁

乐观锁:总是假设最好的情况,认为共享资源每次被访问的时候不会出现问题,线程可以不停地执行,无需加锁也无需等待,只在提交修改的时候去验证对应的资源(也就是数据)是否被其它线程修改了(具体方法可以使用版本号机制或 CAS 算法)。

悲观锁:总是假设最坏的情况,认为共享资源每次被访问就会出现问题(比如共享数据被修改),所以每次在获取资源的时候都会上锁,这样其他线程想拿到这个资源就会阻塞,直到锁被上一个持有者释放。也就是说,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。缺点:线程阻塞,可能出现死锁问题

springcloud五大组件是什么,它们是如何运行的?

 一、服务治理组件

Nacos:服务注册中心配置中心的结合体,采用C/S架构,包含两大组件:

Nacos Server:可以作为服务注册中心配置中心,帮助Nacos Client实现服务的注册、发现,配置的动态刷新。

Nacos Client:通过spring-cloud-starter-alibaba-nacos-discovery,在Nacos Server中实现服务的注册和发现;通过spring-cloud-starter-alibaba-nacos-config,在Nacos Server中实现配置的动态刷新

二、负载均衡组件

Ribbon:客户端负载均衡和服务调用工具,不需要独立部署,几乎存在于每一个使用 Spring Cloud 构建的微服务中,是 Spring Cloud 体系中最核心、最重要的组件之一

三、熔断器组件

Hystrix:有效地阻止分布式微服务系统中出现联动故障,如雪崩,以提高微服务系统的弹性。Spring Cloud Hystrix 具有服务降级服务熔断线程隔离请求缓存请求合并以及实时故障监控等强大功能。

 四、服务网关组件

 GateWay:在微服务架构中,一个系统往往由多个微服务组成,而这些服务可能部署在不同机房、不同地区、不同域名下。这种情况下,客户端(例如浏览器、手机、软件工具等)想要直接请求这些服务,就需要知道它们具体的地址信息,例如 IP 地址、端口号等。

可以通过服务网关,直接请求到服务网关,再由服务网关根据不同的标识信息将请求转发到微服务实例。可以在服务网关中处理一些非业务功能的逻辑,例如权限验证、监控、缓存、请求路由等。

五、分布式配置组件

Config:在分布式微服务系统中,几乎所有服务的运行都离不开配置文件的支持,为了解决这些问题,通常我们都会使用配置中心对配置进行统一管理。可以为微服务架构中各个微服务提供集中化的外部配置支持。

什么是sql注入,如何防止sqI注入?

 是一种常见的web应用安全漏洞,例如通过输入框输入非法的sql代码从而操纵数据库获得敏感数据。

是否发生sql注入可以通过查看日志数据库记录进行确认,如果确实发生了sql注入,立即隔离受影响的系统减少风险

使用参数化查询而不是字符串拼接,是预防sql注入最基础最有效的方法。

预编译:绑定变量,也就是相当于将数据和代码分离把传入的参数绑定为一个变量而不是sql语句,用  ?  表示,达到无法改变sql结构。

说白话就是:不使用字符串拼接,而是将不确定数据部分用 ? 表示,不管输入什么,都只会作为参数而不是sql语句。


接口与抽象类区别,final关键字为什么不能修饰抽象类?

对于抽象类,时刻需要谨记: 抽象类是不能够直接实例化的, 如果要使用一个抽象类,就必须要有该抽象类的子类. 如果抽象类的子类不是抽象类的话,就一定要重写该抽象类的所有抽象方法.

java中一个类只能继承一个类,但是却可以实现(implements)多个接口. 如果实现接口的类不是抽象类的话,则该子类必须重写接口中所有的抽象方法.

具体两者的区别:抽象类和接口的区别

定义抽象类是让其他类继承的,如果定义为final 该类就不能被继承,这样彼此就会产生矛盾,所以final 不能修饰抽象类


详细描述一下HashMap的实现原理

 HashMap底层是一个数组,数组的每个元素是一个链表或者红黑树,

添加元素时,计算得到该key的hash值。将hash值对数组的长度进行取模,以此来获取key要存在数组的哪一个位置。如果该位置没有key,直接插入。如果该位置有key,将这个key和要插入的key进行比对是否一致,如果一致,说明是同一个key,直接覆盖;如果不一致,就把该key的节点追加到这个链表上。jdk1.8之后为了减少链表搜索时间(jdk1.8引入红黑树的原因),当链表长度大于8的时候,将链表转为红黑树

白话总结:

1.计算插入的key的hash值

2.根据值进行算法判断插入的位置

3.如果该位置没有key,直接插入

4.如果该位置有key,并且是同一个key,则覆盖

5.如果不是同一个key,则追加到链表/红黑树

线程池有哪些状态

 running: 线程池可以接受新任务处理队列中的任务
shutdown: 线程池不再接受新任务,但会继续处理队列中的任务
stop: 线程池不再接受新任务不再处理队列中的任务并中断正在执行的任务
tidying: 所有任务已终止,线程池中的线程数量为0,即将进入TERMINATED状态(该状态进行资源清理操作)。
terminated: 线程池已完全终止。

如何防止死锁

死锁:两个或两个以上的线程在执行的过程中,去争夺同一个共享资源,造成相互等待的现象。如果没有外部干预,线程会一直阻塞。

死锁产生的条件:

必须四个条件都满足才会产生死锁

1.互斥:共享资源只能被一个线程占用,其他线程无法干扰

2.请求(资源y)和保持(不释放资源x):线程1已经取得资源x,在等待资源y的时候,不释放x。

3.不可抢占:其他线程不能强行抢占线程1占有的资源

4.循环等待:线程1等待线程2占有的资源,线程2等待线程1占有的资源

如果防止死锁:

产生死锁只能人工干预或者kill该线程

防止死锁只需要破坏死锁四个条件任意一个,其中互斥条件不能被破坏,因为它是互斥锁的基本约束,只能破坏其他三个条件。

请求和保持-->一次性申请所有的资源,则不需要等待

不可抢占-->占用资源的线程在申请其他资源的时候,如果申请不到,则主动释放占用的资源

循环等待-->按序申请来预防,即线性化申请

mybatis支持延迟加载吗,延迟加载的原理

支持

延迟加载是相对于联表操作而言的,单表操作没有延迟加载。比如用户表和商品表,如果只需要用户信息,而不需要对应的商品信息,则可以使用延迟加载(懒加载),也就是在需要用到的数据才进行加载,不需要用到的时候就不加载,可以提高数据库性能,因为单表查询比关联查询要快。

延迟加载的原理

主要依赖于动态代理和动态sql,先为主查询对象(用户表)生成一个动态代理类类并返回,该类可以通过内部的方法判断是否开启了延迟加载,如果开启,则会执行事先存储好的关联对象的查询sql,并将结果通过反射赋值给关联对象。