Java面试题1

1、JavaWeb三大组件:Listener、Fliter、Servlet

2、springMVC执行流程:

①用户先发送http请求到服务器

②请求到达前端控制器(DispatcherServlet),前端控制器首先会对请求进行统一处理,比如判断请求是否需要拦截、读取请求参数等。

③前端控制器请求处理映射处理器(HandlerMapping)去查找Handler

④处理器映射向前端控制器返回执行链,执行链包含Handler以及各种拦截器(Handler其实就是controller)

⑤前端控制器拿到执行链调用处理器适配器(HandlerAdapte ),处理器适配器会根据请求去执行Handler,Handler执行完毕后返回一个ModelAndView

⑥处理器适配器会将ModelAndView返回给前端控制器,前端控制器会请求视图解析器去解析视图

⑦视图解析器会向前端控制器返回一个view视图

⑧前端控制器会对视图进行渲染,前端控制器向用户响应结果,其实数据填充到模板页面中生成最终的HTML页面然后传递给前端

3、spring依赖注入方式

构造方法注入:在类的构造函数中声明所需的依赖项,Spring IoC容器在实例化该Bean时,会根据构造函数的参数类型和顺序,自动将对应的Bean注入到构造函数中

set方法注入:通过Bean的setter方法来注入依赖。每个需要注入的属性都应有一个对应的setter方法

属性注入:分为两种

@Autowired  :根据类型注入,由spring提供
@Resource:先根据名称注入,再根据类型注入,由j2EE提供

4、springboot优点:


约定大于配置
开箱即用
内置tomcat

5、springIOC

基本概念;

  1. 控制反转:传统程序设计中,我们自己在对象中主动创建依赖对象,而在IoC模式下,创建被依赖对象的责任交给了IoC容器,由容器在对象初始化时不等对象请求就主动将依赖对象注入到对象中。

  2. 依赖注入(Dependency Injection, DI):是实现IoC的一种具体方式,即组件之间的依赖关系由容器在运行期决定,形象地说就是“由容器动态地将某个依赖关系注入到组件之中”。Spring框架主要通过构造器注入、setter方法注入等方式来实现依赖注入。

  3. Spring IoC容器:是Spring用来管理Bean(即应用程序中的组件或Java对象)的容器。容器负责创建Bean实例,管理Bean之间的依赖关系,以及负责Bean的生命周期管理。

6、springAOP

AOP就是面向切面编程,用于将与业务无关,但是却对多个Bean产生影响的公共逻辑抽取并封装成一个可重用的模块,该模块称为切面,好处如下。

1、重用代码
2、简化代码
3、封装切面逻辑
4、促进松耦合

AOP的关键概念包括:

  1. 切面(Aspect):切面是关注点的模块化,包含了对横切关注点的实现,比如日志记录切面。
  2. 通知(Advice):通知定义了切面中的具体动作和执行点,比如在方法执行前、执行后或抛出异常时执行的代码。
  3. 切点(Pointcut):切入点定义了在哪些Joinpoint应用切面中的Advice,即匹配连接点的规则。

7、SPI 支持接口和抽象类,

SPI全名Service Provider interface,翻译过来就是“服务提供接口”,再说简单就是提供某一个服务的接口, 提供给服务开发者或者服务生产商来进行实现。

SPI:JDK的SPI支持接口抽象,springSPI支持注解

8、Spring自动装载原理(重点)*****

主要由以下几个核心环节实现

@SpringBootApplication注解:它是一个复合注解,由@SpringBootConfiguration, @EnableAutoConfiguration, 和 @ComponentScan组成

@SpringBootConfiguration表明他是一个spring配置类

@ComponentScan扫描标记的包以及子包,用于组件扫描

@EnableAutoConfiguration启用了自动配置类

@EnableAutoConfiguration里面有一个AutoConfigurationImportSelector类,这个类里有一个selectimports方法,通过SPI原理,它会遍历META-INF中的spring.factories文件,文件里是key与value结构的,value是我们的全类名,从中读取并实例化指定的自动配置类

9、retentionPolicy枚举类型:


                            源代码      类文件         JVM运行时
source                    存在
class(默认)        存在        存在
runtime                   存在        存在               存在

10、JWT(JSON Web Token)包含哪几个部分

①Header头部(明文):里面主要包含了typ(传递的数据类型,通常是JWT)和alg(使用的签名算法),这些信息会被转换成JSON格式,然后经过Base64Url编码形成JWT的第一个部分

②Payload载荷(铭文):主要是传递的数据,载荷同样会被转换成JSON格式,并进行Base64Url编码,生成JWT的第二个部分,建议不要存储敏感信息

③Signature签名(可加密):依据头部里的算法生成

  • 签名的生成需要将前面两部分(Header和Payload的Base64Url编码后的内容)用.连接起来,然后与一个密钥(secret)一起,通过指定的算法(如之前头部声明的alg)进行HMAC计算或RSA/ECDSA签名。
  • 计算出的签名结果也会经过Base64Url编码,作为JWT的第三个部分。

11、解决session共享问题

①将session数据持久化存储写入数据库或者别的持久层,服务器接收请求后向持久层发送请求

②将数据保存在客户端,利用令牌实现session共享,原理是JWT

传统的session和cookie缺点是不能分布式

12、session和cookie的区别和关系

关系:session和cookie都可以用来实现跟踪用户状态,二者的关系是session依赖于cookie

①当浏览器第一次发送请求并获取session后,服务端在服务器内部创建一个session对象,并将该session的id以(JSESSIONID=id值)的cookie写会浏览器

②当浏览器二次请求时,会自动携带cookie(JSESSIONID=id值),服务端根据cookie记录的id值获取相应的session

区别:

1存储位置不同:cookie保存在客户端,session保存在服务器端

2存储数据大小不同:cookie存储的数据不超过4K,session可以存储任意大小

3生命周期不同:cookie可以主动设置生命周期

4cookie不安全

我们项目中没用过session,用的是JWT,因为用session要处理session共享问题

13、门面模式

门面模式(Facade Pattern)是一种结构型设计模式,它的主要目的是为子系统中的一组接口提供一个统一的高层接口。这样做可以简化客户端与子系统的交互,隐藏系统的复杂性,并且提供更加易用的接口给客户端使用。

日志用的门面模式常用的日志为logback,log4f,log4f2

spring自带的是logback

maven常用操作:

clean(清理)

package(打包)

deploy(部署)

14、http协议组成部分

1、url(请求路径)

2、head(请求头)

3、body(请求体)

15、jwt有哪几部分?

JWT是啥:把信息进行安全的封装,再用json串的形式传递

jwt(json web tokens)

组成部分:header,payload,签名(Signature)

解决session共享问题:

16、jwt如何实现session共享(token)

服务器认证以后,生成一个 JWT,发回给用户,以后,用户与服务端通信的时候,都要发回这个 JWT(里面包含用户的信息,如用户id,权限等)。服务器完全只靠这个JWT认定用户身份。为了防止用户篡改数据,服务器在生成这个JWT的时候,会加上签名(详见后文)。服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。

17、springboot如何统一返回格式(ApiWapper-ljj-spring-boot-starter)

1、定义统一的响应对象

首先需要创建一个统一的响应对象,例如ApiResponseResult类,它通常包含以下字段:

  • code:响应码,用于表示请求是否成功。

  • message:响应信息,描述请求的结果或错误详情。

  • data:实际的数据,如果有的话。

  • 在这个类中定义失败和成功的返回格式

    @Data
    public class ApiResponse{
        private Integer code = 200;//设置默认值200,表示“成功”
        private String msg;
        private Object data;
    }
 public static ResultData  success(Object body)
    {
        ResultData resultData = new ResultData();
        resultData.setMsg("成功");
        resultData.setData(body);
        return  resultData;
​
    }
​
    public static ResultData  error(int code,String message)
    {
        ResultData resultData = new ResultData();
        resultData.setCode(code);
        resultData.setMsg(message);
        return  resultData;
​
    }

18、你在开发前端过程用到了什么组件(你对前端的看法)

 Pinia :新一代状态管理工具,是 Vue 的存储库,它允许跨组件/页面共享状态。实际上,pinia就是Vuex的升级版,Pinia的优势就是,更加简洁的语法,完美支持Vue3的Composition api 和 对TypesCcript的完美支持。

vue-router:路由管理插件,允许开发者定义路由规则,当 URL 发生变化时,Vue Router 会自动更新视图,展示对应的组件,从而实现页面的动态切换。根据URL分配到对应的处理程序

vite:前端构建工具,能够显著提高开发效率,近乎即时的页面加载速度和热模块替换

axios:跨域,调用后端的一些接口;Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 node.js 中的异步通信。它是一个非常流行的选择,用于在前端应用中发送 AJAX 请求到服务器,以获取或发送数据。Axios 的主要优点是它支持多种请求方式,能够拦截请求和响应,转换请求和响应数据,并且可以取消请求;Axios 允许你注册请求和响应拦截器,这些拦截器可以用来修改请求或响应,例如添加认证令牌、改变请求数据格式等

type/node:使用@别名

elementplus:是一个基于 Vue 3 的现代 UI 组件库,专为开发者提供一套完整的设计解决方案

19、vue中route和router的区别:

route指的是,表示当前激活的页面:它获取有关当前页面的信息

router:用在页面之间的跳转,例如:router.push({ name: "login" });跳转到名为login的页面

20、idea的打断点debug功能三个常用的键位:

F7:进入方法内部

F8:往下执行

F9:跳过到下一个断点

21、getmappering(查询)

postmappering:新增

putmappering:修改

deletemappering:删除

22、软删除(逻辑删除)和硬删除(物理删除)

软删除:只是把数据的状态修改了,比如0代表商品下架,1代表在售,我们软删除的时候只需要把1改为0即可,只修改状态。

硬删除:就是把数据全删了,不方便再次查找

23、JDBC执行流程

24、mybatis执行流程

第一步,加载mybatis配置文件和映射文件

第二步,构建会话工厂Sqlsessionfactory

第三步,构建会话对象sqlsession,sqlsession对象包含了执行SQL语句的所有方法

第四步,sqlsession底层new了一个executor执行器

第五步,excutor执行器找到对应的mappedStatement

第六步,StatementHandler 根据 MappedStatement 中的 SQL 语句及参数映射信息设置 SQL 参数并执行 SQL。

第七步,执行SQL语句之后,statementhandler处理数据库返回的结果集,并按照mappedstatement中定义的结果映射转换为Java对象。输入参数映射:paramerterHandler会将参数映射到SQL语句上

第八步,输出参数映射:结果集会将结果转化为我们需要的那个方法上的返回对象
    
最后,mappedstatement将结果映射为Java对象返回到客户端

Mybatis的优点

(1)原生JDBC的缺点

1、使用JDBC连接数据库没有办法实现 java 代码和 sql 语句之间的解耦.

2、使用JDBC连接数据库在接受查询的时候,需要自己手动写对象映射关系,效率低。

3、JDBC默认未使用连接池,如果使用连接池,需要我们自己书写,增加工作量.

MyBatis入门简单,上手快。

1.正是由于半自动化自定义SQL,所以比较灵活,可以更加精确的定义SQL。9

2.SQL写在XML里,从程序代码中彻底分离,降低耦合度,便于统一管理和优化。

3.便于程序升级迭代。

25、mybatis四种拦截器

1执行器拦截器(Executor):负责 SQL 语句的执行,包括缓存策略、批处理等

2语句拦截器(StatementHandler):负责预编译 SQL 语句以及执行 SQL 语句(监控表的更新或者修改)

3参数拦截器(ParameterHandler):负责将传入的参数设置到预编译 SQL 语句中(lastupdateby)

结果集拦截器(ResultHandler):负责从数据库返回的结果集中提取数据

  1. Executor 拦截器

    • 用于拦截执行器(如 SimpleExecutor、ReuseExecutor 或 BatchExecutor)的行为。
    • 可以用来修改 SQL 执行逻辑或添加额外的操作,比如记录 SQL 执行时间。
  2. ParameterHandler 拦截器

    • 用于拦截参数处理器,可以改变传入 SQL 的参数值或类型。
    • 例如,在参数处理器将参数设置到 PreparedStatement 之前对其进行修改。
  3. ResultSetHandler 拦截器

    • 用于拦截结果集处理器,可以改变结果集的处理方式。
    • 例如,在从结果集中提取数据之前或之后进行一些操作。
  4. StatementHandler 拦截器

    • 用于拦截 Statement 的创建和执行。
    • 可以用来修改 SQL 语句或预编译语句的创建过程,例如添加额外的 SQL 输出。

26、反射(Reflection)

动态语言与静态语言

动态语言:在运行时代码可以根据某些条件改变自身结构的语言。如C#,PHP,JavaScript等

静态语言:与动态语言相对应,即运行时不能改变自身结构的语言,如Java,C,C++

Java不是动态语言,但是我们可以通过反射机制获得类似动态语言的特性,所以说Java具有一定动态性。

反射概念

在Java中,反射(Reflection)是一种强大的工具,它允许程序在运行时获取和操作类、接口、构造器、方法和字段等。反射是Java语言的一个重要特性,它为开发人员提供了许多灵活性,尤其是在构建框架和库时非常有用。

获取Class对象的三种方式:注意class对象是不能创建的,只能获取

1.Class c1 =类名.class

public class MyClass {
    public static void main(String[] args) {
        Class<?> myClass = MyClass.class; // 获取 MyClass 的 Class 对象
        System.out.println(myClass.getName()); // 输出: com.example.MyClass
    }
}

2.通过 instance.getClass() 方法,如果你有一个类的实例,你可以调用 getClass() 方法来获取其 Class 对象

public class MyClass {
    public static void main(String[] args) {
        MyClass instance = new MyClass();
        Class<?> myClass = instance.getClass(); // 获取 MyClass 实例的 Class 对象
        System.out.println(myClass.getName()); // 输出: com.example.MyClass
    }
}

3.通过 Class.forName() 方法,通过全类名动态加载类,并获取其 Class 对象

public class MyClass {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> myClass = Class.forName("com.example.MyClass"); // 动态加载 MyClass 的 Class 对象
        System.out.println(myClass.getName()); // 输出: com.example.MyClass
    }
}

注意,一个class类只会有一个class对象,不管用这个类创建多少个对象,这些对象的hashcode值都是一样的,说明他们是同一个对象。

类的初始化顺序一定是先初始化父类再初始化子类

Java反射的主要用途包括:

  1. 获取类信息:获取类名、包名、父类、接口等。

获取类名:c1.getName()

获取类的属性:c1.getFields(),只能获取public的属性,getDeclaredFields(),获取全部属性

获取方法:c1.getMethods(),只能获取public的方法,getDeclaredMethodes()获取全部方法

  1. 创建实例:通过类名或Class对象动态创建实例。
  2. 访问类成员:获取并操作类的字段、方法和构造器。
  3. 调用方法:即使方法是私有的,也可以调用。
  4. 修改字段值:可以读取和设置字段的值,即使字段是私有的,反射可以在程序运行阶段对字段进行修改。

基本概念:

  • Class对象:每个类都有一个Class对象,它是Class类的实例。Class对象包含有关类的所有信息。
  • Field对象:表示类中的字段(变量)。
  • Method对象:表示类中的方法。
  • Constructor对象:表示类中的构造器。

效率:通过new获取对象的操作要比通过反射获取对象速度快得多。

反射获取构造方法

反射获取成员变量

反射获取成员方法

反射的使用

MyBatis拦截器处理参数时使用,在做商城项目时,我们的数据表中都有lastUpdatBy这个字段,我们绝大多数业务都需要对lastUpdateBy进行赋值,所以我们就在Mybatis拦截器中通过反射获取参数,然后通过ThreadLocal中的localuser进行统一对其赋值。ThreadLocal为每一个线程提供一个独立的变量副本,使得每个线程在访问给变量时获取的都是自己独有的线程副本,我们定义了一个LocalUser对象存储在ThreadLocal中,然后就可以获取ThreadLocal中的Localuser对象为lastUpdateBy赋值。

JDBC底层:通过反射注册驱动

我们写的starter:AOP的地方:分页,springsecurity,token认证

27、什么是ThreadLocal(===================)

ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。ThreadLocal能够实现跨类跨方法实现变量的传递。

ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。这里有几点需要注意:

因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这是也是 ThreadLocal 命名的由来。
既然每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题。
ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。

总的来说,ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景

二、ThreadLocal与Synchronized的区别
ThreadLocal<T>其实是与线程绑定的一个变量。ThreadLocal和Synchonized都用于解决多线程并发访问。

但是ThreadLocal与synchronized有本质的区别:

1、Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。

2、Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本

,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。

而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。

一句话理解ThreadLocal,threadlocl是作为当前线程中属性ThreadLocalMap集合中的某一个Entry的key值Entry(threadlocl,value),虽然不同的线程之间threadlocal这个key值是一样,但是不同的线程所拥有的ThreadLocalMap是独一无二的,也就是不同的线程间同一个ThreadLocal(key)对应存储的值(value)不一样,从而到达了线程间变量隔离的目的,但是在同一个线程中这个value变量地址是一样的。

应用场景traceId,存储用户信息

28、vue父子传递参数

默认是父传子,需要defineProps这个参数,使用前需要先导入defineProps

当然也可以子传父,需要写defineEmits,使用前需要先导入defineEmits

子传父编码规范就是defineEmits(["update:modelValue"]);;;modelValue是要更新的属性名

VUE3.4之后我们用defineModel,它是一个简化父子组件双向数据绑定的实用工具,无需声明props和emits,用的时候直接定义,例如

const modelValue1 = defineModel("modelValue ");要改变的时候只需要改变modelValue1的值就行,modelValue1只是一个变量,随便起名字都可以,括号里的modelValue可以省略不写,前提是前边v-model:modelValue里的modelValue没有写,他会默认是modelValue这个名字;使用的时候只需要在子组件的标签上加上defineModel就行。

核心是父组件中:<Tuku v-model="formData.img"  v-model:img=formData.imgb > </Tuku>里的

v-model:img 的img名称要和子组件中 const b =defineModel("img")括号里的名称要保持一直。

29、v-if和v-show的区别

v-if :

是一个条件渲染指令,它会根据表达式的真假来判断是否应该渲染出声明它的元素。当表达式的结果为假时,元素及其子元素不会被编译或渲染到 DOM 中。这意味着,当 v-if 的条件变为 false 时,DOM 元素会被完全移除,当条件再次变为 true 时,DOM 元素会重新创建并插入到页面中。

v-show:

v-show 不同于 v-if,它仅控制元素的 CSS display 属性。无论 v-show 的条件如何,元素始终会被编译和保留在 DOM 中,只是当条件为假时,元素的 display 属性会被设置为 none,从而使元素不可见。

30、nginx是什么

Nginx 是一个开源的、高性能的HTTP和反向代理服务器,同时也是一个IMAP/POP3/SMTP邮件代理服务器。它以其稳定性、丰富的功能集、简单的配置文件和低资源消耗而闻名。Nginx 特别适合处理高并发连接,常被用于负载均衡、网页缓存及反向代理,以提升网站的性能和承载能力。

「 SSL/TLS终端 」:Nginx可以作为SSL/TLS终端代理,对客户端和后端服务器之间的通信进行加密和解密。通过SSL/TLS终端代理,Nginx可以提供安全的通信通道,保护数据的机密性和完整性。Nginx还支持SSL/TLS协议的加密算法和安全性配置,可以提供高度安全的通信环境。

31、注解(Annotation)和注释(comment)的区别

注解不仅可以给人看,也可以给计算机看,注释只能给人看。注解不是程序本身,但是他却可以对程序做出解释,并且可以被程序使用。

32、中间件是什么

中间件是一种独立的系统软件或服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源。中间件位于客户机/ 服务器的操作系统之上,管理计算机资源和网络通讯。是连接两个独立应用程序或独立系统的软件。相连接的系统,即使它们具有不同的接口,但通过中间件相互之间仍能交换信息。

执行中间件的一个关键途径是信息传递。通过中间件,应用程序可以工作于多平台或 OS 环境。

中间件是介于操作系统和应用软件之间,为应用软件提供服务功能的软件,有消息中间件,交易中间件,应用服务器等。由于介于两种软件之间,所以,称为中间件。

33、ORM(对象关系映射)是什么

对象-关系映射(Object Relational Mapping,简称ORM,对象关系映射)是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。 简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。

34、元数据是什么

就是描述数据的数据,比如我们要填一张个人信息表,个人信息表就是一个数据,而描述个人信息表的比如名字,性别,年龄等数据就成为元数据。

35、API是什么

API 代表:应用程序编程接口,在某些或其他方面,很多大型公司会建立自己的API提供给用户或者内部使用,API就是一种为你客户提供服务的方法。一个API并不是等同于一整个远程服务器,他是服务器用来接受请求和发送响应的一部分

36、开发中有没有用到价格这个数据,你是怎样使用的?

有,我们在写一个物流仓储系统的时候,采购时需要查看价格,我们的做法是把价格单独抽出来做一张表,为什么要这样做呢?因为价格是个敏感字段,它会随着时间有所变动,我们不仅需要拿到当前的价格,也需要知道它的历史价格。方便我们后续对数据进行查询以及对比。

37、VUE中v-model:name与:name的区别(:其实是v-bind)

v-model用于表单元素和组件的双向数据绑定

v-bind用于将vue实例的数据属性绑定到HTML元素的任何属性上,属于单向绑定

38、为什么不推荐使用多表联查

查询性能低

39、为什么不推荐使用外键

因为查询效率很低,并且删除的时候很麻烦。

40、数据库设计三大范式

第一范式:每一列都是原子的,不可再分。这意味着表中的每一列都包含单一类型的值,且每个单元格只包含一个值,不允许有重复组或列表。

第二范式:在满足第一范式的基础上,表中的所有非主键字段(列)必须完全依赖于整个主键,而不是主键的一部分;如果一个表具有复合主键(即由多个列组成的主键),那么所有非主键列都必须依赖于整个复合主键,而不仅仅是其中的一部分

第三范式:在满足第二范式的基础上,表中的任何非主键列不能依赖于其它非主键列,只能直接依赖于主键。这意味着消除传递依赖,即如果存在A → B → C的依赖关系,C应该直接依赖于A,而不是通过B间接依赖

但是,实际开发中我们是可以不遵循第三范式的,也叫反范式化,例如在追求高性能或简化数据访问的场景下

好处是:

1、提高查询性能:避免了多表连接可能带来的性能瓶颈,减少表之间的连接操作,因为数据已经被复制或汇总在单个表中,这可以显著加快查询速度

2、简化查询:反范式化可以使得复杂的多表查询变得简单,因为所需的数据可以在单个表中找到

3、提高数据可用性:在分布式系统或高可用性环境中,反范式化可以减少对远程数据源的依赖,提高数据的本地可用性

4、减少事务处理成本

缺点是:

1、数据一致性问题:由于数据的冗余,当数据发生变化时,需要在多个地方进行更新,否则会导致数据不一致

2、增加存储成本:冗余数据会占用更多存储空间

3、维护难度加大:需要额外的逻辑和程序来确保冗余数据的同步和一致性

41、事务的四大特性

原子性(Atomicity):事务中的所有操作要么全部完成要么全部不完成

一致性(Consistency):完成事务后,数据前后要保持一致

隔离性(Isolation):事务之间互不干扰

持久性(Durability):事务完成后,数据应该永久保存

在 Spring 中,事务管理通常通过 @Transactional 注解来实现,只需要在目标方法上加上这个注解就行

42、spring事务失效的八种场景

1、自调用(Self-Invocation)

即自己调用自己,一个类的方法在调用同一个类的另一种方法的时候,事务会失效。

类内部非直接访问带注解标记的方法 B,而是通过类普通方法 A,然后由 A 调用 B。 自己玩自己

@Service
public class Demo {
  public void A() {

    this.B();
  }
  
  @Transactional
  public void B() {
     ......
  }
}

解决方法是将这些方法拆分到不同的类中,或者通过 Spring 的代理来调用这些方法。

在该Service类中使用AopContext.currentProxy()用AOP上下文获取代理对象,让代理对象去执行目标方法,前提是要在启动类里加上

@EnableAspectJAutoProxy(exposeProxy = true)//导出业务代码

然后在目标方法上加上

@Transactional注解,在执行方法前开启事务,执行成功则提交事务,失败则回滚事务
 @SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
@EnableTransactionManagement
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

}

Java

((ServiceA)AopContext.currentProxy()).doSave(user);

或者

2.非public修饰的方法

@Transactional注解只能在在public修饰的方法下使用。

/**
 * 私有方法上的注解,不生效(因私有方法Spring扫描不到该方法,所以无法生成代理)
 */
@Transactional
private boolean test() {
    //test code
}

3、数据库不支持事务

MySQL中,MyISAM引擎不支持事物,InnoDB 支持事物

4、异常类型不匹配

@Transactional 注解默认只管理运行时异常(如RuntimeException及其子类)和错误(如Error)。当抛出未被捕获的运行时异常(RuntimeException)时,Spring 会触发事务回滚操作,将之前的操作撤销;对于受检异常(即必须被捕获或声明抛出的异常,例如IOExceptionSQLException等),除非你明确配置事务管理器去处理它们,否则事务不会回滚。

可以通过 rollbackFor 和 noRollbackFor 属性来指定需要回滚或不需要回滚的异常类型。

@Transactional(rollbackFor = SQLException.class)

   @Transactional
    public void insertAll(PoMaster master) throws Exception {
        poMasterDao.insert(master);
        if(1 == 1){
            // 故意引发受检异常,事务默认不会回滚
            throw new SQLException("A checked exception occurred.");
        }
        poItemDao.insertList(master.getItems());
    }

5、捕获异常未抛出,自己消化掉了异常

@Transactional
public void A(){
	try{
	   ......
	}catch(Exception e){
	   // 未抛异常
	}
}

6、传播属性设置问题

前两个REQUIRED与SUPPORTS常用些

propagation属性错误

@Transactional默认的事务传播机制是:REQUIRED,若指定成了NOT_SUPPORTED、NEVER事务传播机制,则事物不生效

7、Bean没有纳入Spring IOC容器管理

该类没被Spring管理,事物也是不生效的

8、事务方法内启动新线程进行异步操作

主线程没有拿到子线程的异常,所以代理类以为正常执行了

43、采购的业务周期

44、spring框架中哪里用到了反射

在 Spring 框架中,反射机制被广泛用于以下几个方面:

1. 依赖注入:Spring 使用反射机制获取对象并进行属性注入,从而实现依赖注入。

2. AOP:Spring AOP 使用 JDK 动态代理或者 CGLIB 字节码增强技术来实现 AOP 的切面逻辑,这其中就包含了对被代理对象方法的反射调用。JDK 动态代理适用于实现了接口的目标对象。Spring 使用 java.lang.reflect.Proxy 类来创建代理对象,并且使用 InvocationHandler 接口来定义方法拦截逻辑。

3. MVC 框架:Spring MVC 框架使用反射来调用相应的控制器方法,从而实现请求的处理。

4. 数据库访问框架:Spring 的 JDBC 框架使用反射机制来实现对数据库的访问。

5. 容器管理:Spring 容器也使用了反射机制来管理对象的实例化和依赖注入。

需要注意的是,虽然反射机制为开发者提供了极大的便利性,但是过度使用反射也可能导致性能问题,在使用时需要进行适量控制。

45、spring实现代理的方式有两种

JDK动态代理和CGLIB代理

JDK动态代理是基于Java反射机制实现的,它要求目标对象必须实现一个或多个接口。通过java.lang.reflect.Proxy类和InvocationHandler接口来创建代理对象。Spring AOP使用JDK动态代理来代理实现了接口的bean。

使用条件:

  • 目标对象必须实现至少一个接口。
  • 代理对象实现相同的接口。

CGLIB(Code Generation Library)是一个高性能的代码生成库,它可以在运行时动态地生成一个新的类,并继承原始的目标类。因此,即使目标对象没有实现任何接口,CGLIB也可以为它创建代理。

使用条件:

  • 目标对象不需要实现接口。
  • 代理对象会继承目标对象。

Spring根据目标bean是否实现了接口来决定使用哪种代理方式。如果bean实现了接口,则使用JDK动态代理;如果没有实现接口,则使用CGLIB代理。

46、springsecurity原理

使用的是责任链模式,

首先,请求会先到我们的listener里再发送到filter,filter不仅只有一个,有多个,filter1到filtern(filter1到filtern是一个过滤链)之间有一个delegatingfilterproxy,他也是一个filter,这个filter里面有一个filterchainproxy,它里面有一个securityfilterchain过滤器链,里面有许多security filter,这个采用的是责任链模式,每个security filter负责各自的权限管理,默认有15种,在这个过滤器链之间我们可以手动添加我们自己的权限管理,比如添加JWTTokenFilter,解析token里的角色把它放到security上下文里边,看我们的方法上面有没有@ReAirchired这个注解,这个注解里看包不包含我们填在securitycontext上下文里的角色,如果包含继续执行方法(这里运用到AOP原理,在执行方法前做一些事情判断包不包含我们写入上下文里的角色),不包含则返回错误403(未授予权限);

47、你们项目权限怎么做的

运用springsecurity实现的,可以先讲一下springsecurity的原理,后端用springsecurity,前端用的手写指令

前端:

在router中的meta属性中提前设置好角色(roles)的属性,角色的属性根据不能的分类划分好

例如:

并将登录用户的角色保存在pinia中,pinia里的代码如下

自定义指令,在src下边建directiv文件夹,在文件夹里写我们自己的指令auth.ts

Array.isArray(role):这是一个类型检查函数,用于确定 role 是否是一个数组。它返回一个布尔值,如果提供的参数是一个数组,则返回 true,否则返回 false

return pinaRoles.some((item: any) => role.includes(item)):这部分代码是一个返回语句,它使用了数组的 some() 方法来检查 pinaRoles 数组中的任何一个元素是否存在于 role 数组中。

  • pinaRoles.some(...)some() 方法测试数组中是否存在至少一个元素满足提供的函数所指定的条件。如果存在这样的元素,some() 方法返回 true;否则,返回 false

  • (item: any) => role.includes(item):这是一个箭头函数,作为 some() 方法的参数。它接收 pinaRoles 数组中的每一个元素作为参数 item,并检查 item 是否存在于 role 数组中。

在菜单中运用指令,当访问的时候获取router中设置的角色,判定设置的角色是否包含登录用户的角色,如果包含就有权限访问,如果不包含就移除,使用remove()方法,就不会显示无权限访问的页面。

后端:

48、你了解的spring设计模式有哪些

责任链模式

适配器模式

代理模式

元模式

49、RBAC相关的表

用户表,

角色表

权限表

用户角色关联表

权限角色关联表

资源表

资源权限关联表

50、mybatis一对多和多对多

一对多:

假设你有两个实体:DepartmentEmployee,其中 Department 可以有多个 Employee,这是一个典型的一对多关系。在 MyBatis 中,你可以使用 <resultMap><collection> 元素来处理这种关系。

实体1:

public class Department {
2    private int deptId;
3    private String deptName;
4    private List<Employee> employees;
5
6    // getters and setters
7}

实体2:

public class Employee {
2    private int empId;
3    private String empName;
4    private int deptId; // 外键
5
6    // getters and setters
7}

Xml:

<resultMap id="DepartmentResultMap" type="com.example.Department">
2    <id property="deptId" column="dept_id"/>
3    <result property="deptName" column="dept_name"/>
4    <collection property="employees" ofType="com.example.Employee">
5        <id property="empId" column="emp_id"/>
6        <result property="empName" column="emp_name"/>
7    </collection>
8</resultMap>
9
10<select id="selectDepartmentWithEmployees" resultMap="DepartmentResultMap">
11    SELECT d.dept_id, d.dept_name,
12           e.emp_id, e.emp_name
13    FROM department d
14    LEFT JOIN employee e ON d.dept_id = e.dept_id
15    WHERE d.dept_id = #{deptId}
16</select>

这段 MyBatis 的 XML 配置代码定义了一个名为 DepartmentResultMap 的结果映射,它用于将数据库查询结果映射到 com.example.Department 类的对象上。让我们逐行解析这个配置:

  1. <resultMap id="DepartmentResultMap" type="com.example.Department">

    • id="DepartmentResultMap": 这是一个唯一标识符,用于区分不同的结果映射。在这个例子中,我们将其命名为 DepartmentResultMap
    • type="com.example.Department": 这指定了与该结果映射关联的 Java 类。在这个例子中,是 com.example.Department 类。
  2. <id property="deptId" column="dept_id"/>

    • property="deptId": 这是指定的 Java 类中对应的属性名称。在这里,deptId 是 Department 类的一个属性。
    • column="dept_id": 这是指定的数据库列名,它将与 Java 属性 deptId 相关联。当 MyBatis 执行 SQL 查询时,它将从结果集中读取名为 dept_id 的列,并将其值映射到 Department 对象的 deptId 属性上。
  3. <result property="deptName" column="dept_name"/>

    • 同样地,property="deptName" 指定了 Department 类中的 deptName 属性,而 column="dept_name" 表示从数据库查询结果中读取 dept_name 列的值,并将其映射到 deptName 属性。

接下来的部分处理 Department 类中的一对多关系:

  1. <collection property="employees" ofType="com.example.Employee">

    • property="employees": 这是 Department 类中集合类型的属性名,用于存放 Employee 对象的列表。
    • ofType="com.example.Employee": 这指定了集合中元素的类型,在这里,元素类型是 com.example.Employee 类。
  2. <id property="empId" column="emp_id"/>

    • 这是针对 Employee 类中的 empId 属性和数据库中的 emp_id 列的映射。
  3. <result property="empName" column="emp_name"/>

    • 这是针对 Employee 类中的 empName 属性和数据库中的 emp_name 列的映射。

通过这种方式,MyBatis 能够将 SQL 查询的结果集映射到 Java 对象中,包括处理复杂的关联关系,如一对多。这使得在应用程序中处理数据库数据更加直观和方便。

多对多:

多对多关系通常需要一个关联表。假设你有两个实体:CourseStudent,它们之间通过一个关联表 course_student 连接。

第一个实体:

public class Course {
2    private int courseId;
3    private String courseName;
4    private List<Student> students;
5
6    // getters and setters
7}

第二个实体:

public class Student {
2    private int studentId;
3    private String studentName;
4
5    // getters and setters
6}

xml文件:

<resultMap id="CourseResultMap" type="com.example.Course">
2    <id property="courseId" column="course_id"/>
3    <result property="courseName" column="course_name"/>
4    <collection property="students" ofType="com.example.Student">
5        <id property="studentId" column="student_id"/>
6        <result property="studentName" column="student_name"/>
7    </collection>
8</resultMap>
9
10<select id="selectCourseWithStudents" resultMap="CourseResultMap">
11    SELECT c.course_id, c.course_name,
12           s.student_id, s.student_name
13    FROM course c
14    LEFT JOIN course_student cs ON c.course_id = cs.course_id
15    LEFT JOIN student s ON cs.student_id = s.student_id
16    WHERE c.course_id = #{courseId}
17</select>

在上述示例中,<collection> 元素用于映射一对多关系,而 LEFT JOIN 语句确保即使没有关联记录,主表的数据也能被返回。此外,MyBatis 允许使用 <association> 元素来处理一对一关系,这在处理反向引用时非常有用。在实际应用中,你可能还需要考虑性能优化,比如使用延迟加载或嵌套查询。

collection用于mybatis一对多或多对多关系映射

mybatis的关联查询
    1、一对一
        <association property="对象的属性名" javaType="查询的结果集装到哪个对象里">
            <id column="id" property="id"/>
            <result column="username" property="username"/>
        </association>
    2、一对多
        <collection property="对象中集合的属性名" ofType="集合的泛型">
            <id column="aid" property="id"></id>
            <result column="uid" property="uid"></result>
        </collection>
    3、多对多
        user(uid, username, password):张三、李四
            user_role(uid, rid)
        role(rid, role_name):校长、老师

        <collection property="对象的属性名" ofType="集合的泛型">
            <id column="rid" property="id"></id>
            <result column="role_name" property="roleName"></result>
        </collection>

51、Vue父子传参以及子组件调用父组件的方法

先写一个子组件,要使用的话先在父组件里导入子组件Son,导入就是import{Son }from‘。。。’,使用的时候直接在要用的位置加上子组件的标签名,例如

先在父组件里定义const parentName=ref("我是父传递的")

<Son v-model:name="parentName"></Son >然后在标签里写入(name是属性变量,parentName是局部变量,不一回事)

父组件里定义一个const parentName=defineModel("name")

v-model后边的name必须要和我们父组件里defineModel里填的名称保持一致,而parentName这个只是一个变量名,可以随意些,这句话是把父组件的parentName的值给子组件的name属性。

在子组件里要定义一个sonName,如

const sonName=defineModel("name"),注意,这里的name必须要和父组件里v-model:后边的name保持一致,在子页面显示就直接这样:名字:{{sonName}},直接调用就行。

父调用子里的方法,把子的方法通过defineExpose把方法暴露出来,在父组件里引入子组件就可以直接使用子的方法,在子组件上加上ref;

而子调用父的方法可以借助vue3中useAttrs,在子组件导入useAttrs

52、DOM元素是什么

DOM 元素,全称为 Document Object Model 元素,是构成网页结构的基础组成部分。DOM,即文档对象模型,是一种用于表示 HTML 或 XML 文档的标准数据结构。在网页开发中,DOM 提供了一种将网页内容和结构表示为树状结构的方式,其中每个节点都是文档的一部分,如元素、属性、文本等。

53、java设计模式

策略模式

根据你的需求,去调到不同的类上边去实现接口,根据请求的类型来判断具体用哪个类给你服务

我们在仓储系统的出库管理中使用到了策略模式,我们的出库方式有很多种,如销售出库,破损出库,调拨出库等等,根据不同的出库类型我们为每种出库类型定义了各自的类,根据请求的不同找到各自对应的类,然后去掉用类的方法实现功能。

观察者模式:

我们在做仓储系统的时候,对于货品的入库和出库有不同的操作,我们自己定义了一个事件,通过使用listener观察者,当一个事件发生变化时,其他依赖于他的事件都会发生改变。观察者模式的主要组成部分是被观察者(事件),观察者(这些对象监视事件的状态,当事件状态发生变化时,每个观察者都会收到通知,通常会调用他们的update方法);主要用的一对多关系的依赖。

观察者模式(Observer Pattern)是一种软件设计模式,属于行为型模式之一。它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。

原型模式

单例模式:里面的DCL

DCL 模式通过两次检查实例是否已经创建,从而在保证线程安全的同时减少同步的开销。具体来说,DCL 模式在第一次检查实例是否为空时,如果没有创建实例,则进行同步锁的获取,再次检查实例是否为空,然后再创建实例

适配器模式

责任链模式

观察者模式

策略模式

代理模式

构建者模式

54、mybatis一级缓存与二级缓存

一级缓存(是默认开启的)

概念:它指的是mybatis中的SqlSession对象的缓存。当我们执行完查询之后,查询的结果会存储在SqlSession为我们提供的一块区域中。当我们再次查询同样的数据,mybatis会先去SqlSession中查询是否有,有的话直接拿出来使用。当SqlSession对象消失时,Mybatis的一级缓存也就消失了。

特点:

1、  同一 SqlSession 内部,执行相同查询条件的多次查询,只要在缓存有效期内,只会执行一次数据库查询。

2、  不同的 SqlSession 之间缓存数据互不影响,即一级缓存仅对当前 SqlSession 有效

  • 第一次发起查询用户id为 1 的用户信息,先去找缓存中是否有id为 1 的用户信息,如果没有,从数据库查询用户信息。 得到用户信息,将用户信息存储到一级缓存中。

  • 如果sqlSession去执行 commit操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

  • 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。              

二级缓存(默认关闭)

概念:二级缓存是基于 namespace(即 Mapper XML 文件的命名空间)级别的缓存,可以被多个 SqlSession 共享。这意味着,对于同一 Mapper 下的相同查询请求,即使由不同的 SqlSession 执行,如果二级缓存中已有结果,都可以复用缓存数据,避免了数据库查询

特点:

  • 二级缓存默认是关闭状态,需要在 MyBatis 配置文件(mybatis-config.xml)中开启。

  • 二级缓存可以跨 SqlSession 边界共享数据,适用于多用户间存在大量重复查询且数据相对静态的场景。

不用二级缓存的原因:因为开启二级缓会在内存中开辟一块空间,比如我们有多个应用的话,每个应用都会创建二级缓存会造成内存的大量占用。

总结:

  • 一级缓存:自动管理,无需配置,适用于单个 SQLSession 内的重复查询。
  • 二级缓存:需要显式配置,适用于跨 SQLSession 的重复查询,有助于提高整个应用程序的性能。

55、数据库乐观锁与悲观锁

56、stream流常用操作

在Java中,Stream操作流是Java 8新引入的一个功能,它提供了很多强大的操作,方便我们进行集合的处理和操作。常用的Stream操作方式有:

1.过滤:使用filter()方法可以过滤掉集合中不符合条件的元素。

2.映射:使用map()方法可以对集合中的每一个元素进行映射处理。

3.排序:使用sorted()方法可以对集合中的元素进行排序。

4.去重:使用distinct()方法去掉集合中的重复的元素。

5.统计:使用count()方法可以对集合中的元素进行统计。

6.聚合:使用reduce()方法可以对集合中的元素进行聚合计算。

7.遍历:使用forEach()方法可以遍历集合中的每一个元素。

8.匹配:使用anyMatch()、allMatch()、noneMatch()方法可以对集合中的元素进行匹配判断。

9.分组:使用qroupingBy()方法可以按照某一个属性进行分组。

10.转换:使用collect()方法可以将集合中的元素转换为另一个集合。

11.平均:使用average()方法可以用于计算一组元素的平均值。

12.min:取最小值

13.max:取最大值

14.skip:跳过

57、采购单的状态

新建(采购员)-审核通过(高级管理)-运输中-入库(采购员)-审核不通过(高级管理员可以)-作废(采购人跟高级管理者都可以)

58、数据库懒加载

<mapper namespace="com.by.dao.UserDao">
    <resultMap type="User" id="findAllResultMap">
        <id column="id" property="id"></id>
        <result column="username" property="username"/>
        <result column="address" property="address"/>
        <result column="sex" property="sex"/>
        <result column="birthday" property="birthday"/>
         <!--
          property:属性名
          ofType:泛型
          select: 要调用的 select 映射的 id,select属性指定了要调用的另一个SQL映射ID,用于加载关联对象。
          column : 传递给 select 映射的参数
          fetchType="lazy":懒加载,默认情况下是没有开启延迟加载的,局部配置
         -->
        <collection property="accounts" ofType="Account" fetchType="lazy"
                    select="com.by.dao.AccountDao.findAccountById" column="id">	
        </collection>
    </resultMap>

    <select id="findAll" resultMap="findAllResultMap">
      select * from user
    </select>
</mapper>

懒加载应用场景:

  1. 对象关系映射(ORM):在ORM框架中,比如Hibernate、MyBatis等,当我们从数据库中获取一个对象时,并不一定需要立即加载与之相关的所有关联对象的数据。只有当需要访问这些关联对象时,才会触发数据库查询以加载相关数据。

  2. 分页查询:在处理大量数据时,如果一次性加载所有数据到内存中,可能会导致内存溢出或加载时间过长。因此,可以通过分页的方式只加载当前页面的数据,而其他数据则在用户滚动或请求时按需加载。

  3. 数据缓存:在缓存系统中,也可以使用懒加载的概念,即仅在首次请求时从数据库加载数据到缓存中,之后直接从缓存中读取。

懒加载与急加载区别:

  • 急加载(Eager Loading):指的是在加载主对象的同时也加载其所有相关联的对象。这种方式确保了数据的一致性,但可能导致大量的数据被加载到内存中,从而影响性能。
  • 懒加载(Lazy Loading):只在需要时才加载相关联的对象。这种方式可以显著减少初始加载的时间和内存消耗,但在实际使用中可能会导致额外的数据库查询。

假设有一个实体Employee和一个实体Department,并且一个员工属于一个部门。我们可以使用@ManyToOne注解来定义这种关系,并通过fetch属性设置懒加载策略。

@Entity
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    private Department department;

    // 构造函数、getter和setter省略
}

@Entity
public class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    // 构造函数、getter和setter省略
}

在这个例子中,Department对象将会在访问employee.getDepartment()时才被加载。如果没有调用这个方法,则不会触发数据库查询。

59、echarts里的series什么意思

series 是图表配置项中的一个重要部分,它代表了图表中的一系列数据点和相应的可视化设置。series 选项用于定义图表上显示的数据系列,每一个 series 项都代表一组数据及其相关的样式和行为配置。

series 项通常包含以下几个关键属性:

  • type: 数据系列的类型,如 'line'(折线图)、'bar'(柱状图)、'pie'(饼图)等。
  • name: 数据系列的名称,用于图例和其他地方的显示。
  • data: 该数据系列的数据数组,包含具体的数值或数据点。
  • 其他配置: 包括颜色、标签样式、动画效果等,这些配置取决于图表类型。

60、使用echarts的方式

1、先在npm里下载:

npm install echarts

 然后在使用的页面引入:

import * as echarts from 'echarts';

然后在官网上找模板引用即可

注意

legend: {
            data: ['库存量', '销量']//legend属性里data里的名称库存量,销量等必须要和series
//里的name属性的名称一一对应,如
series: [//series是数据
            
        
            {
                name: '库存量',//数据源的名称
                type: 'bar',//示例图的形式,bar是柱形图,line是线形图
                data: data.map((item: any) => item.qty),//要展示的数据源
                markPoint: {
                    data: [
                        { type: 'max', name: '最大值' },//最大值
                        { type: 'min', name: '最小值' }//求最小值
                    ]
                },
                markLine: {
                    data: [{ type: 'average', name: '平均值' }]//求平均值
                }

            },

各种属性:

toolbox是工具盒子

里面第一个可以查看文本样式版的数据,第二个查看折线,第三个查看柱状,第四个更新,第五个下载

xAxis:指X轴

yAxis:指 Y轴

61、使用懒加载的时候,在我们要序列化的实体类上边加上注解

@JsonIgnoreProperties(value = {"handler"})
它的作用是序列化的时候忽略自动生成的handler属性,否则程序会报错

61、强弱软虚引用

1强引用:普通的引用

2软引用:像星巴克避雨一样,如果内存空间充足,允许软引用存在,若内存空间不足,GC删除软引用

3弱引用:势利眼的沙县小吃,不买东西不让你进去休息。不管内存够不够用,GC在清除的时候只要发现弱引用就直接删除

4虚引用

62、半双工与全双工

半双工:在半双工通信模式下,数据可以在两个方向上传输,但不能同时在两个方向上传输。这意味着通信的一方发送数据时,另一方只能接收。类似于对讲机

优点:相对于单工(Simplex)模式,半双工提供了双向通信的能力

缺点:由于数据传输不能同时进行,因此可能会出现通信延迟或效率较低的问题

全双工:在全双工通信模式下,数据可以在两个方向上同时传输。这意味着发送和接收可以同时进行,无需等待对方停止发送。

优点:提供了更高的通信效率和带宽利用率,因为数据可以在两个方向上同时流动

缺点;:成本相对较高

63、nginx是什么

是一个开源的高性能的Web服务器软件,它具有以下几个主要作用:

1. Web服务器:Nginx可以作为一个轻量级的Web服务器,处理和响应HTTP请求。它支持静态文件的快速传输,并且能够处理大量的并发连接。

2. 反向代理服务器:Nginx可以作为反向代理服务器,将客户端请求转发到后端的多个服务器上。这样可以实现负载均衡,提高系统的可靠性和性能。

3. 负载均衡器:Nginx可以将客户端请求分发到多个后端服务器上,实现负载均衡。它可以根据不同的策略(如轮询、IP哈希、最少连接数等)将请求分发到不同的服务器上,以达到优化资源利用和提高系统性能的目的。

4. 静态文件服务器:Nginx可以高效地提供静态文件的访问和传输。通过将静态文件缓存到内存中,可以大大减轻后端服务器的负载。

5. SSL/TLS终端代理:Nginx可以作为SSL/TLS终端代理,对外提供HTTPS服务。它可以负责SSL/TLS握手、证书验证等操作,将加密的请求转发到后端服务器。

总的来说,Nginx是一个功能强大、灵活且高效的服务器软件,可以用于构建高性能的Web应用、负载均衡集群、反向代理等场景。

64、CICD的创建流程

选择数据源——代码扫描、单元测试(并行关系)——jar打包(构建)、docker镜像构建(串行关系)——应用部署——结果通知

65、Redis面试题——缓存穿透,缓存击穿,缓存雪崩

1.穿透:两边都不存在,不管是Redis缓存中还是数据库中都不存在数据

解决方法有返回空值,布隆过滤器,使用黑名单

返回空值:就是我们的请求先去Redis缓存中查找数据,如果不存在就去数据库中查找,如果数据库中不存在就返回一个空值,下次我们再次查找这个数据的时候,就直接从Redis缓存中拿这个空值,这样就降低了数据库的访问压力,不会频繁访问数据库。

布隆过滤器:建一个位数组(Bit Array),初始时所有位都被设为 0,当我们插入元素的时候,这些插入元素的位置索引变为1。

记住一句话如果存在则不一定存在,如果不存在则一定不存在。意思是说我们对于要查询的元素,使用所有选定的哈希函数计算出该元素对应的位索引。如果这些位索引处的位都是 1,则认为该元素可能存在于集合中;如果其中有任意一位是 0,则确定该元素不在集合中。

使用黑名单:如果我们查询一个Redis缓存没有的数据的时候,接着去数据库查询,若数据库没有这个元素,把它的索引放到一个黑名单里,下次我们再次查询的时候,先查询黑名单里的索引,如果这个索引在黑名单里,那么说明我们查询的是不合法数据,就不再去访问缓存区或数据库了。

2.击穿

它的意思是当我们Redis缓存中某个或某几个热点数据项失效后,短时间内大量并发请求发送到后端数据库,造成数据库访问压力增大,可能导致服务不可用。

解决办法:缓存预热、互斥锁、双写策略

缓存预热:在服务启动或者定时任务中,预先加载数据到缓存中,这样避免了启动时大量数据打到后端数据库

3.雪崩

就是Redis缓存中大量的热点数据失效,同一时间大量的请求打到后端数据库,造成数据库压力激增,很可能引发服务不可用

解决办法:数据过期时间随机化

数据过期时间随机化:对热点数据设置一个随机的过期时间,这样可以有效避免同一时间大量热点数据失效

66、Redis数据类型应用场景

String:

分布式锁:小米卖车,如果同一个用户在不同设备上登录自己的账号,然后在同一时间抢商品,我们必须保证只能有一个账号能抢到。

session会话:比如京东的个人账户,我们的session中不可能把个人的所有信息都存进去,例如购物车,商品这些是不会存进去的,这些数据我们会存储在一个Redis缓存中,这个缓存就用到了string类型,我们可以根据session中的用户id进行查找这些数据。

业务缓存:京东的页面,或者京东的网页布局都可以放进去

计数器:库存扣减,下单减商品

限流:我们在防止黑客攻击的时候,给他的ip后边追加一些时间戳,时间戳后边加一个计数器用来记录一秒内访问次数,假如在一秒内他的访问次数超过了20次,那么我们就给他的ip限流

全局唯一ID:

Hash:电商购物车,大K是用户id,小k是商品id,值是购买量或者价格

BitMap:用户签到

Zset:排行榜或者热搜

List:消息队列

Set:

bitmap和int都属于string

67、Resultful Api设计风格

Restfule风格是一种软件架构风格,而不是标准,只是提供了一种设计原则和约束条件。主要适用于客户端和服务器端交互的软件。是基于http协议实现。目的是为了提高系统的可伸缩性,降低应用之间的耦合度,方便框架分布式处理程序。基于这个风格的软件可更加的简单、更有层次,更易于实现缓存的机制。
     在resultful风格中,用户请求的url使用同一个URL而用请求方式:get/post/delete/put等方式对请求的处理方法进行区分。这样可以在前后台分离的开发中让前端开发人员不会对请求的资源地址产生混淆,形成一个统一的接口。

三、特点

(1)每种url代表了一种资源。
(2)客户端和服务器之间,传递这个资源的某种表现层。
(3)客户端通过四个http动词,对服务器资源进行操作。实现表现层状态的转化。

在http协议中,四个表示操作方式的动词:GET/Post/Put/Delete,他们分别对应四种基本操作。
Get用来获取资源。Post用来新建立资源,也可以更新资源。Put用来更新资源。Delete用来删除资源。

68、Redis过期删除策略以及内存淘汰策略

过期删除策略:

1.惰性删除:键如果过期了我们不管,下次我们访问的时候先查询这个键是否过期,如果过期了我们删除这个键,如果不过期我们返回这个键。

2.定期删除:每隔一段时间对数据库的键进行一次扫描,如果找到过期的就删除,每次只查询一部分,不会一下子全部删掉。

内存淘汰策略

如果我们的内存到达一定的临界了,我们会进行内存淘汰策略

LRU:全称Least recently used,淘汰最近最少使用的数据(有时间概念,最近)

LFU:全称Least-frequently used,淘汰使用频率最低的数据(没有时间概念)

Redis提供了八种内存淘汰策略

1.默认策略,noeviction,就是如果内存满了,那么我们不删除数据,不让写入数据,想要写入数据就报错。

2.volatile-lru:只对设置了过期时间的key有效,淘汰最近最少使用的数据

3.volatile-lfu:只对设置了过期时间的key有效,淘汰使用频率最少的数据

4.volatile-random:随机淘汰一个设置了过期时间的数据

5.volatile-ttl:挑选过期速度越快(马上过期)的数据进行删除

6.allkeys-lru:对全部key都有效,整个键空间(即数据库中的所有键),根据lru算法删除最近最少使用的数据

7.allkeys-lfu:对全部key都有效,整个键空间(即数据库中的所有键),根据lfu算法删除使用频率最少的数据

8、allkeys-random:从数据库中所有的键中随机选择一个数据进行删除

69、Redis主从同步机制

首先要知道两个命令:

sync 命令会通知操作系统将内存缓冲区中的数据写回到磁盘,保证了数据的一致性和持久性

bgsave 是 Redis 中的一个命令,用于异步保存数据库的快照(snapshot)到磁盘。这个命令触发了一个后台进程来执行持久化操作,从而不会阻塞客户端的其他请求。

全量复制:

1.从服务器向主服务器发送同步命令sync

2.主服务器接收到请求后使用bgsave命令生成一个rdb文件,然后使用一个缓冲区来记录从现在开始所有的写命令

3.主服务器执行完bgsave命令以后会把生成的rdb文件发送给从服务器

4.从服务器拿到这个rdb文件然后加载到内存中,然后主服务器会把刚刚写进缓存的命令同步给从服务器,从服务器再去执行这些命令

增量复制:之后主数据库每执行一个写命令,都会将写命令发送给从数据库。

70、Redis和MySQL如何保持数据一致性

在实际项目中经常会使用到Redis缓存用来缓解数据库压力,但是当更新数据库时,如何保证缓存及数据库一致性,一般我们采用延时双删策略。

目前系统中常用的做法是一个查询接口,先查询Redis,如果不存在则查询数据库,并将结果放入到Redis中。

为什么是删除缓存,而不是更新缓存呢?主要是如果缓存的内容是带有树型结构或者List,Map,那么更新其中一个内容相对较慢。

常见更新策略

1 先删缓存,再更新数据库

2 先更新数据库,再删除缓存

3 普通双删

4 延迟双删

主要介绍一下延迟双删

它的原理是线程A先删除缓存,在线程A更新数据库之前,线程B去查询数据库,数据库更新后,线程C查询数据库,此时线程B拿的是旧数据,线程C拿到的是新数据,线程B和线程C都会把数据放入缓存中,在更新完数据库后线程B明显缓存中的数据与数据库中的数据不一致,而线程C如果热点数据失效也会出现与线程B相同的问题,所以我们在更新完数据库后延迟三到五秒再删除一次缓存,此时线程B和线程C发送请求的时候发现缓存中的数据被删除变为空了,请求就会打到数据库,此时BC去数据库拿到的都是新数据,避免了缓存数据与数据库数据不一致。

极端情况是线程D,它的意思是在删除缓存后数据库更新前查询数据,此时拿到的数据还是旧数据,但是他在我们第二次删除缓存之后才把从数据库拿到的旧数据放入缓存中,此时请求发送到缓存中拿到的还是旧数据。

71、Redis集群模式主要有

主从复制模式:主当机了其他的都不行了

哨兵模式:主宕机了从也宕机了那数据还是会丢失,管理起来也麻烦,不支持分区

Redis Cluster模式:天生分布式,一个主宕机了其他的都可以担当这个主。

72、后端与前端部门如何进行沟通

使用swagger工具生成文档实时更新

73、项目中静态资源如何存放

阿里云OSS私仓或腾讯云COS私仓,前端资源用nginx,

74、Lua脚本的原子性,Java代码中为什么要使用Lua脚本

使用Lua的原因:

主要就是Lua脚本具有原子性,比如说我们在下单的时候,我们要先判断库存量是否足够,这至少需要两个命令,假如我们看到库存量是刚好够我们用的,如果此时另一个线程先于我们把数据库里的库存量减少了,此时数据库里实际的库存量其实不够我们使用了,那这个时候就可能造成我们数据库库存变为负数,这是不允许的,所以我们使用Lua脚本保证一个线程在工作的时候不被别的线程打断。

Lua 脚本的原子性指的是脚本在整个执行过程中不会被打断,也不会有其他客户端的命令插入其中执行

Lua脚本的原子性表现在:

排他性执行:当一个 Lua 脚本开始执行后,Redis 会阻止其他客户端的命令执行,直到该脚本完全执行完毕,脚本中的所有命令要么全部执行,要么一个也不执行

单线程模型:Redis 本身是一个单线程的服务器,当 Redis 开始执行 Lua 脚本时,其他客户端的所有请求都会被暂时搁置,直到脚本执行完成

事务性:在 Redis 中,事务是指一系列连续执行的命令序列,在这个序列中,命令的执行顺序不会被打断

错误处理:如果 Lua 脚本在执行过程中出现错误,Redis 会停止执行脚本并返回错误给客户端

一致性与隔离性:这种原子性只保证脚本作为一个整体被执行,而不保证数据的一致性或隔离性

75、序列化与反序列化

定义

序列化:把对象转化为字节序列的过程叫做对象的序列化

反序列化:把字节序列转化为对象的过程叫做对象的反序列化

什么时候用到序列化/反序列化

RPC(Remote Procedure Call)即远程过程调用,允许一台计算机调用另一台计算机上的程序得到结果,而代码中不需要做额外的编程,就像在本地调用一样。   

实现序列化和反序列化要实现Serializable接口

在 Java 中实现了 Serializable 接口后, JVM 会在底层帮我们实现序列化和反序列化。

实现 Serializable 接口就算了, 为什么还要显示指定 serialVersionUID 的值?

如果不显示指定 serialVersionUID, JVM 在序列化时会根据属性自动生成一个 serialVersionUID, 然后与属性一起序列化, 再进行持久化或网络传输. 在反序列化时, JVM 会再根据属性自动生成一个新版 serialVersionUID, 然后将这个新版 serialVersionUID 与序列化时生成的旧版 serialVersionUID 进行比较, 如果相同则反序列化成功, 否则报错.

如果显示指定了 serialVersionUID, JVM 在序列化和反序列化时仍然都会生成一个 serialVersionUID, 但值为我们显示指定的值, 这样在反序列化时新旧版本的 serialVersionUID 就一致了.

在实际开发中, 不显示指定 serialVersionUID 的情况会导致什么问题? 如果我们的类写完后不再修改, 那当然不会有问题, 但这在实际开发中是不可能的, 我们的类会不断迭代, 一旦类被修改了, 那旧对象反序列化就会报错. 所以在实际开发中, 我们都会显示指定一个 serialVersionUID, 值是多少无所谓, 只要不变就行.

static 属性为什么不会被序列化

因为序列化是针对对象而言的, 而 static 属性优先于对象存在, 随着类的加载而加载, 所以不会被序列化.

mybatis里resultType和resultMap区别

resultTyperesultMap 是用于配置查询结果如何映射到 Java 对象的两个属性

resultType 主要用于简单的查询结果映射。当你使用 resultType 时,MyBatis 期望查询结果中的列名能够与 Java Bean 的属性名相匹配,并且类型兼容。这意味着对于简单的实体类(POJOs),你可以直接使用 resultType 指定一个类的全限定名或者别名,MyBatis 会自动进行列到属性的映射

假设一个User的Javabean对象

public class User {
    private int id;
    private String name;
    private int age;

    // getters and setters
}

xml配置文件这样写

<select id="selectAllUsers" resultType="com.example.User">
    SELECT id, name, age FROM users
</select>

resultMap 则用于更复杂的映射场景。它允许你定义映射规则,包括一对一关系、一对多关系等。resultMap 可以指定列名与属性之间的映射,并支持嵌套结果集、延迟加载等功能

再加一个Address的Javabean对象

public class Address {
    private int id;
    private String street;
    private String city;

    // getters and setters
}

xml文件这与写,我们定义了一个名为 userResultMapresultMap,它包含了对 UserAddress 实体类的映射。association 标签用来处理 UserAddress 之间的一对一关系。

<resultMap id="userResultMap" type="com.example.User">
    <id property="id" column="user_id"/>
    <result property="name" column="user_name"/>

    <association property="address" javaType="com.example.Address">
        <id property="id" column="address_id"/>
        <result property="street" column="address_street"/>
        <result property="city" column="address_city"/>
    </association>
</resultMap>

<select id="selectUsersWithAddresses" resultMap="userResultMap">
    SELECT u.id AS user_id, u.name AS user_name, a.id AS address_id, a.street AS address_street, a.city AS address_city
    FROM users u
    JOIN addresses a ON u.address_id = a.id
</select>

76、bean的生命周期

77、布隆过滤器作用

布隆过滤器使用位数组和哈希函数来快速判断元素是否存在于集合中,但会有一定的误报率。它可以用于解决缓存穿透问题,通过预加载数据库中的所有数据到布隆过滤器,来判断数据是否存在(如果存在可能存在,如果不存在一定不存在)。由于布隆过滤器无法删除元素且需对表中的所有数据进行预热,因此通常不首选此方法,而是用黑名单来解决缓存穿透问题。

78、Ribbon中常见的负载均衡算法

①轮询算法(默认):最简单的负载均衡策略,按照顺序循环地将请求分发到每个服务实例。每个服务实例依次接收请求,实现基本的公平分配。(你一次我一次)

②随机:随机选择一个服务实例来处理请求。

③权重:根据每个服务的权重,权重越高被选择概率越高。

④响应时长:根据每个服务实例的历史响应时间,响应时间越快的服务实例被选中的概率越高。

⑤ip哈希

⑥url哈希

79、Ribbon第一次调用为什么很慢

ribbion采用了懒加载的策略,只有第一次访问的时候才会去创建LoadBalanceClient,并且需要通过TCP三次握手建立服务端与客户端的连接,所以第一次比较耗时

解决方法:就是使用饥饿加载

在properties文件里配置

rabbon.eager-load.enabled=true//开启饥饿加载
rabbon.eager-load.clients=user-service//开启饥饿加载的服务,多个服务用逗号隔开

80、Ribbon内核原理

Nacos Servcer:服务端组件

Nacos Client :客户端组件

有两个mall-order服务,我们的mall-user可能会去调用我们mall-order服务,此时我们mall-order已经注册到Nacos Server服务列表了,客户端会定期从我们服务端拉取服务列表,将服务放到缓存里边,通过ribbon里的算法,启动的时候会找到我们其中的一个服务,用一级目录(服务名)替换成对应的ip和端口,然后再去具体调用我们那个服务。

81、为什么要使用双token拦截器

业务token:用户id、过期时长、用户名、角色、权限

刷新token:时长一般为半年,里面放用户id、过期时长

超级管理员下线,用户角色修改的时候,避免单token无感知,及时刷新token;双token系统通常用于提高安全性和分离不同级别的权限。里面主要有两个token

业务token(Access Token):这是用户用来直接访问资源的token,他的特点是有效期比较短,一旦过期,用户需要重新认证来获取token,这样做的好处是即使我们业务token泄露了,攻击者进行不当操作的时间有限。

刷新token(Refresh Token ):这是当业务token过期的时候重新获取业务token的,他的特点是有效期一般比较长,刷新token一般不会发送给客户端,而是保存在服务器端,当需要刷新业务token的时候,服务端通过提供刷新token去请求新的业务token。可以让用户在无感知的情况下获取新的业务token。当我们获取新的业务token的时候,其实刷新token也延长了寿命,相当于刷新token变为永久的了。

另外使用双token的原因还有:

安全性高:通过短期的业务token和长期的刷新token,可以在不影响用户体验的情况下减少安全风险,即使业务token丢失,也可以凭借其有效期短来把损失控制在一定范围内

用户体验好:双token系统可以在不影响用户体验的前提下,实现后台的安全管理和策略调整。用户不需要频繁登录,同时也能保证系统的安全性。

权限管理:双token系统可以更好地管理用户的权限,在开放平台中,第三方可以通过刷新token来获取一个临时的业务token,这样即使不获取用户的用户名和密码也能赋予第三方一定权限,也保证了用户的信息安全

灵活性好:在需要进行更细粒度的权限控制或者策略调整时,双token系统提供了更多的灵活性。例如,可以在不改变refresh token的情况下,调整access token的有效期或权限范围。

82、什么是CAP原则

C:一致性:一致性要求所有节点在同一时刻看到相同的数据。具体而言,如果一个节点成功提交了一个更新,那么随后对该数据的所有访问都应该反映出这个更新。

A:可用性:可用性要求每个请求无论成功还是失败都应该在合理的时间内得到响应。

P:分区容错性:分区容错性是指即使网络分区发生,系统仍然能够正常运行。

一般开发中我们会保证AP或者CP,一致性只需要保证最终一致即可

83、Nacos配置中的动态刷新原理

84、你知道的负载方案有哪些

DNS 负载均衡:利用 DNS 的轮询机制将域名解析到不同的 IP 地址上,从而将请求分发到不同的服务器

硬件负载均衡器:F5

软件负载均衡器分为客户端和服务端

客户端负载均衡器:Ribbon

服务端负载均衡器:Nginx,OpenResty

85、说一下ES及应用场景

ES是非关系型数据库

一、全文搜索

1.电商搜索:快速查找商品信息,支持模糊匹配、关键词高亮显示、过滤、排序等功能。

2.站内搜索:网站内部的页面、文章、博客等内容的搜索,提供类似Google的搜索体验。

3.论坛和社交媒体:用户发表的内容搜索,如帖子、评论、话题等

二、日志分析与监控

ELK日志采集。加上traceId

1.运维监控:收集系统指标、网络流量数据,实时或历史数据分析,可视化展示系统状态和趋势

2.应用日志:跟踪应用程序的行为,帮助开发人员迅速定位错误、诊断问题

3.服务器日志:收集、索引和分析服务器产生的各类日志,用于故障排查、性能优化、安全审计等。

三、数据分析

1.业务分析:实时或批量分析业务数据,生成报表,进行趋势分析、关联分析等

2.时序数据分析:存储和分析时间序列数据,例如设备传感器数据、用户行为数据等

四、搜索推荐

实现个性化搜索和推荐功能,根据用户的搜索历史和行为模式,智能推荐相关内容

86、使用ES为什么要安装分词器

在 Elasticsearch 的 IK Analyzer 中,ik_smartik_max_word 是IK 分词器针对中文分词提供的两种策略,但分词效果和粒度不同:

ik_smart: 这种模式更侧重于保持语义完整性,尽量进行较少的、更有意义的拆分,减少无意义的子词组合,提高搜索准确率,降低误报率。

ik_max_word: 此模式致力于最大化地拆分文本,即尽可能多地生成可能的词语组合,包括单字、双字直至整个短语。它的特点是尽力穷举所有可能的词汇,提高召回率,但在某些情况下可能会造成噪声较多。

参考网址:https://blog.51cto.com/u_15116285/6100979

官方插件下载地址:

https://github.com/medcl/elasticsearch-analysis-ik/releases

87、MDC是什么

MDC是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的Map,可以往其中添加键值对,MDC中记录的内容可以被同一线程执行的代码访问,该线程的子线程会继承父线程MDC的内容。

需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理的最开始保存这些数据

原理:MDC的工作原理基于当前线程绑定的Map,通过Map存储键值对,每个线程都有自己独立的MDC实例,当你在一个线程中为该线程的MDC添加键值对时,这些键值对只对当前线程有效,保证多线程中每个日志记录互不干扰。当请求来的时候会把请求头里的traceId放在InheritableThreadLocal里,然后打印时去取就行了。

InheritableThreadLocal就是ThreadLocal的增强版,他继承了ThreadLocal并扩展了功能,他支持子线程继承父线程中的值

88、说一下mongondb及应用场景

mongondb非关系型数据库,基于文档存储,mysql使用的时候先建表,而mongondb不需要,只需要存储对象,这样提高了我们的开发效率,我们做过一个

微信小程序拼车,引用高德地图的API接口,将小车的经过的城市存储到mongondb中,这样可以查看定位以及我们的行进路线

商城首页的广告,楼层(首页是灵活多变的方便修改)

89、Http工作原理

当请求来的时候,会先通过TCP三次握手建立连接,建立连接以后客户端会向服务端发送请求,接下来服务端处理请求,然后服务端发出响应,接着客户端处理响应并将数据渲染到页面,最后TCP四次挥手断开连接。

90、spring cloud里目前遇到的有两个bug

1.当我们使用GetMapping去获取对象的时候,正常的springMVC里会把对象转化成URL并在后边用问号拼接参数的值,但是springcloud并不会将GetMapping以及DeleteMapping都不会把他转化成URL,必须在请求的类型前加上@SpringQueryMap这个注解

例如:

 @GetMapping ("/api/order")
    List<Product> select(@SpringQueryMap OrderQuery query);

2.feigen里使用@RequetMapping时,springMVC扫描到这个注解的时候会把这个类当成自己的controller,是一个bug没有和飞跟兼容好

@FeignClient(name = "order-service")
//@RequestMapping("/api/order")//spring mvc看到的@RequestMapping,会把他当成自己应用的controller,
// 但是实际不是的,是个bug,与feign没有兼容好导致的
public interface OrderServiceClient {
    @PostMapping("/api/order")
    OrderingOk createorder(@RequestBody OrderingDto dto);

    @GetMapping ("/api/order")
    List<Product> select(@SpringQueryMap OrderQuery query);

}

91、面试模版

1.自己专业简单介绍,我有几年的Java开发经验,

2.兴趣爱好,生活态度

3.参与过的项目,使用的技术栈,中间件等

4.优势或者对之前公司的贡献

可能问:你的优点:性格,技术,你的缺点

91、@SuppressWarnings 注解

在Java编程中,@SuppressWarnings 是一个注解,用来指示编译器忽略某些警告。当你在一个类、方法或其他程序元素上使用 @SuppressWarnings 注解时,你可以指定一个或多个要抑制的警告类型。这对于那些你知道不会有实际问题,但编译器还是会发出警告的情况特别有用。

@SuppressWarnings({"unchecked", "rawtypes"})

具体来说:

  • unchecked:这个参数告诉编译器忽略所有未经检查的警告。未经检查的警告通常涉及到泛型的使用。例如,当你将一个非泛型类型的对象赋值给一个泛型类型的变量时,编译器会发出警告。这种情况经常发生在处理包含不确定类型的集合时,比如使用 List 而不是 List<T>

  • rawtypes:这个参数用来忽略所有与原始类型的使用相关的警告。原始类型是指没有指定泛型参数的类型,例如使用 List 而不是 List<String>。当编译器看到原始类型的使用时,它会发出警告,因为这可能导致运行时类型安全问题。

@Operation

是 OpenAPI 规范(以前称为 Swagger 规范)的一个注解,通常用于描述 API 接口的功能

92、GitFlow

GitFlow是一种为Git版本控制系统设计的分支管理工作流

分支分类:

master主分支;这个分支代表了随时可以部署到生产环境的代码状态,也就是项目的最新稳定版本。

develop开发分支:作为日常开发的基础分支,用于整合所有即将发布的新功能和改进。它是开发团队合作的主要分支。

feature功能分支:专注于单一功能,每个 feature 分支都对应着要实现的一个具体功能或者改进点。

release发布(测试)分支:在Git版本控制系统中是用来准备新版本发布的专用分支。它介于开发分支(如 develop)和主分支(如 main 或 master)

hotfix维护分支:维护分支(一般叫hotfix)是唯一可以直接从master分支fork出来的特殊分支,用于快速修复生产环境中发现的紧急问题的分支。修复完成后,应马上合并回master和develop分支,同时master分支用新版本号打上Tag。

93、原生的JDBC、MyBatis 与 MyBatis Plus 的区别以及各自的优缺点

MyBatis是一个半自动化的ORM框架,相较于原生的JDBC,mybatis将SQL语句与业务代码解耦,降低了与业务代码的耦合性,支持动态SQL,并且mybatis不需要需要手动加载驱动、建立连接。

MyBatis 和 MyBatis Plus 是两个用于 Java 应用程序的持久层框架,它们都基于 SQL 映射的概念来实现数据访问

mybatis

94、K8S的优点:

Kubernetes(简称 K8s)是一个开源的容器管理系统,用于自动化部署、扩展和管理容器化的应用程序

1、容器自动化编排:提供了一套完整的工具链来自动化容器的部署、扩展和管理。它可以处理从容器的启动到健康检查、自动重启等一系列运维任务。

2、自动伸缩:用于处理从几台到几千台主机的大规模集群。它支持水平和垂直伸缩,可以根据负载自动调整应用程序实例的数量,以应对流量的波动。

3、服务发现和负载均衡:提供了内置的服务发现机制,使得容器间的通信变得更加容易。它还支持自动负载均衡,可以将流量均匀地分布到多个容器实例上。

95、在模板中使用 v-for 指令来遍历数组或其他集合时,通常会建议为每一个元素提供一个唯一的 key 属性

1、提高性能当数据发生变化时,Vue 会比较新的虚拟 DOM 与旧的虚拟 DOM,然后计算出最小的变化,并应用到真实的 DOM 上

2、避免重复渲染:如果没有提供 key 属性,Vue 只会简单地逐个更新元素,即使元素的位置发生了变化也不会检测到。这可能导致不必要的重新渲染。就像一个里程碑一样

96、前端Watch与computed的区别

watch(A,(newvalue,oldvalue))=>{}

computed(()=>{

A:

B:

C:

})

watch只监听一个,效率高,而computed监听多个效率低

97、线程中wait和sleep区别

wait会释放锁,而sleep不释放锁

wait属于object类,sleep属于thread类

wait是实例方法,sleep是静态方法

wait需要手动唤醒,sleep不需要手动,到点自动苏醒

98、我们在哪里见到过幂等性

幂等性在Java中指的是多次执行操作与一次执行操作的结果一样

数据库层面

查询幂等性,查询天生支持幂等性

修改幂等性,执行同一条更新命令,结果都一样

新增幂等性,可能幂等性,比如我们的id是唯一的(保证主键唯一),我们执行插入的时候,第一次插入成功,第二次插入失败,这对我们的结果没有影响,也体现了幂等性

删除幂等性,删除的结果都一样,都是没有了

HTTP方法上

GET:幂等,多次请求同一个url返回的结果一样

PUT:可能是幂等,如果每次请求是相同的,那么是幂等的,如果每次请求只修改部分资源,那就是非幂等的;

DELETE:可能是幂等,删除了就没有了,不管删几次结果一样

POST:非幂等,比如可能会新增多个资源

消息队列上

幂等性确保即使消息被多次消费,也不会导致重复处理。这对于保证消息的可靠性和一致性非常重要。生产者不重复生产消息,消费者不重复消费消息。

99、JDK1.8的新特性(面试题)

1、Lambda表达式

Lambda表达式可以理解为是一种匿名函数,简化了匿名内部类的使用,并提高代码的可读性和性能Lambda表达式的基本语法: 接口/父类 引用名=(参数)->表达式

2、接口增强(允许接口拥有方法体):

default修饰的方法是普通实例方法,可以用this调用,可以被子类继承和重写

static修饰的方法不能被子类继承

3、函数式接口

函数式接口是只有一个抽象方法的接口,通常用于支持lambda表达式和函数式编程风格。函数式接口通常使用@FunctionalInterface注解来标记判断其是否只包含一个抽象方法,主要是为了编译器检查和代码清晰度。

4、方法引用

方法引用可以理解为式Lambda表达式的一种语法糖,是Lambda表达式的简化形式,可以提高代码的可读性和简洁性。语法:类名::方法名

5、Stream API

Stream API是一种抽象流,它是提供了一种新的处理数据的方式,可以对集合或数据元素进行函数式编程数据处理。

Stream API 的主要特征:惰性求值、管道化、并行处理、函数式编程

stream常用方法

//filter(Predicate<? super T> predicate):过滤
list.stream().filter(s -> s.startsWith("a"));

//map(Function<? super T, ? extends U> mapper):元素转换
list.stream().map(String::toUpperCase)

//sorted 排序
list.stream().sorted();

//distinct():去除重复元素。
list.stream().distinct();

//limit(maxSize):限制 Stream 中元素的最大数量。
list.stream().limit();

//boolean allMatch(Predicate<? super T> predicate) 此流的所有元素是否与提供的predicate匹配。
list.stream().allMatch();

//skip(long n) 跳过前面n个元素
list.stream().skip();

//使用Collector对流进行归纳
list.stream().collect(Collector.toList())

//count统计Stream元素数量
list.stream().count()

//forEach遍历Stream所有元素
list.stream().ForEach();

//max(Comparator<? super T> comparator)查找最大值
list.stream().max();

//返回一个包含此流的元素的数组 toArray();
list.stream().toArray();

//findFirst() 和 findAny():查找第一个元素或任意元素(在并行流中)
list.stream().findFirst();
list.stream().parallelStream().findAny();

6、Optional类

Optional是一个工具类,可以是一个为null的容器对象,它的主要作用就是为了避免null值异常,防止NullpointException。

7、新的日期和时间API

Java 8引入了全新的日期和时间API, 通过引入 java.time 包来提供一套更为强大、线程安全且易于使用的日期和时间 API。 对原有的java.util.Datejava.util.Calendar进行了重新设计和扩展。新的日期和时间API设计上比较简洁,同时也提供了许多方便的日期处理方法和函数

常见的时间和日期类型

  • LocalDate:表示年月日。

  • LocalTime:表示时分秒。

  • LocalDateTime:表示日期和时间。

  • Duration:表示两个时间之间的持续时间。

  • Period:表示两个日期之间的时间间隔。

常见的日期和时间方法

  • plusDays:增加指定的天数。

  • minusDays:减少指定的天数。

  • withYear:设置年份。

100、Bean的作用域

Spring中的Bean什么时候被实例化?

在 Spring 容器启动时,会读取配置文件中的 Bean 定义信息,然后根据 Bean 定义的信息实例化相应的 Bean 对象。具体来说,Bean 的实例化时间与 Bean 的作用域有关:

1. 单例作用域(Singleton):在 Spring 容器启动时,会立即实例化单例作用域的 Bean,将它们存储在容器的 Bean 工厂中。线程共享的,所以线程不安全的

2. 原型作用域(Prototype):在请求获取原型作用域的 Bean 时,Spring 容器才会实例化该 Bean,并返回给请求方。每次请求都会创建新的实例,所以线程安全

3. 其他作用域:如 Web 作用域和 Session 作用域等,它们的实例化时间依赖于具体的使用场景。

需要注意的是,Bean 的实例化过程可能会涉及到复杂的依赖关系,因此 Spring 容器会根据 Bean 定义信息中的依赖关系,先实例化依赖的 Bean,再实例化当前 Bean。同时,Spring 还支持使用构造方法注入和属性注入两种方式,可以在 Bean 实例化的过程中,将所需的依赖注入到当前 Bean 中。

101、说一下es及其应用场景(traceId)

es是开源分布式搜索引擎和分析引擎,也是天然的分布式数据库。常用于全文搜索、日志分析与监控、数据分析。

在日志分析方面,EFK是一套流行的数据搜索、分析和可视化解决方案。

(1)Filebeat 是一个轻量级,资源消耗较小的日志采集工具。

(2)Elasticsearch 用于对采集的数据进行搜索和分析。

(3)Kibana 是一个开源的数据可视化工具,主要用于以仪表板、图表和报告的形式展示存储在Elasticsearch中的数据。

你们的es中都存了哪些内容

(1)商品信息:用于分词搜索商品信息

(2)日志信息

(3)skywalking链路追踪信息?

搭建es时,为什么要安装分词器

es默认支持英文分词,没有提供对中文的支持,因此需要安装中文分词器插件(IK Analyzer)

在IK Analyzer 中有ik_smartik_max_word两种分词器

ik_smart: 这种模式更侧重于保持语义完整性,尽量进行较少的、更有意义的拆分

ik_max_word: 此模式致力于最大化地拆分文本,即尽可能多地生成可能的词语组合,包括单字、双字直至整个短语

102、spring事务的原理(*)

声明式事务管理:加上@transactional注解来声明事务,在xml文件中声明事务管理,优点是简化了事务管理的代码,缺点是灵活性低

编程式事务管理:通过编程方式手动控制事务的开始、提交和回滚,优势是灵活性强,缺点是容易出错,增加代码复杂度;

@Transactional 注解加在类上时,表示该类中所有公共方法(public 方法)都需要在一个事务中执行

@Transactional 注解加在方法上,表示该方法需要在一个事务中执行。Spring 会确保该方法在事务上下文中运行,并在方法执行完毕后根据情况提交或回滚事务。
Spring中实现事务,其实本质就是通过AOP来实现的,@Transactional注解修饰的方法执行前后,Spring的事务管理器会帮我们进行事务的一系列操作。

 事务管理的执行流程

spring容器启动的时候会去扫描带有 @Transactional 注解的类和方法,为带有注解的方法创建一个代理对象(CGLIB 代理或 JDK 动态代理),当业务方法被调用的时候,实际调用的是代理对象的方法,在调用方法前后做一些事情,调用方法前开启事务,在调用方法后如果方法执行成功就提交事务,如果方法执行失败就回滚事务;

JDK动态代理:是通过反射机制实现的,它要求被代理的对象必须实现一个或多个接口。JDK动态代理的核心类是java.lang.reflect.Proxy,通过这个类可以创建一个实现了指定接口的代理对象。使用起来比较简单,但灵活性较低

CGLIB 代理:CGLIB(Code Generation Library)是一个强大的高性能代码生成库,它可以在运行时动态地生成一个给定类的子类,从而实现代理功能。CGLIB通过继承的方式实现,因此它不要求被代理的对象实现任何接口。使用上稍微复杂一些,但是更加灵活,性能也更好

  1. 扫描和生成代理对象

    • Spring 容器启动时,会扫描带有 @Transactional 注解的类和方法。
    • 对于每个带有 @Transactional 注解的方法,Spring 会生成一个代理对象(通常是 CGLIB 代理或 JDK 动态代理)。
  2. 方法调用

    • 当业务方法被调用时,实际调用的是代理对象的方法。
    • 代理对象在方法调用前后插入事务管理逻辑。
  3. 事务开始

    • 代理对象在方法调用之前,通过 TransactionInterceptor 切面调用 PlatformTransactionManager 的 getTransaction 方法,获取并开始事务。
  4. 业务逻辑执行

    • 代理对象调用实际的业务方法,执行业务逻辑。
  5. 事务提交或回滚

    • 业务方法执行完毕后,代理对象通过 TransactionInterceptor 切面调用 PlatformTransactionManager 的 commit 或 rollback 方法,提交或回滚事务。
    • 如果业务方法正常结束,事务将被提交。
    • 如果业务方法抛出异常,事务将被回滚。

103、单例模式的实现方式:

饿汉式:饿汉式是最简单的一种实现方式,类在加载时就完成了实例化,因此是线程安全的。

懒汉式:懒汉式在第一次使用时才创建实例,但需要额外的同步机制来保证线程安全。

双检锁式:双重检查锁定是一种优化的懒汉式实现,减少了不必要的同步开销。

静态内部类式静态内部类在类加载时不会立即实例化,而是在第一次调用 getInstance 方法时才加载内部类,从而实现懒加载。

枚举类枚举是实现单例的最简单和最安全的方式,天然支持序列化和反序列化,而且避免了反射攻击。

你了解的设计模式:单例模式,责任链模式,代理模式,观察者模式,策略模式,适配器模式;

104、接口和抽象类的区别

定义:抽象类是使用 abstract 关键字声明的类接口是接口是使用 interface 关键字声明的

继承和实现:一个类只能继承一个抽象类(子类必须实现抽象类中所有方法,除非子类也是抽象类),而一个类可以实现多个接口

构造:抽象类可以有构造函数但是接口不能有构造函数,因为接口不能被实例化

成员变量:抽象类可以有静态变量,实例变量,常量,而接口只能有常量

访问控制:抽象类可以用各种访问权限修饰符修饰,但是接口只能用public修饰

105、k8s是什么以及它的特点

k8s是一个开源的容器编排平台,用于自动化容器化应用程序的部署、扩展和管理

优点:

Pod 是 Kubernetes 中最小的可部署单元,可以包含一个或多个容器。Pod 中的容器共享网络和存储资源

自动化部署:Kubernetes 可以自动部署和管理应用程序,确保应用程序始终处于期望的状态

自动伸缩:Kubernetes 可以根据负载自动调整应用程序的规模

负载均衡:可以将流量均匀地分发到后端,提高系统的性能和可靠性

自我修复:可以自动检测和替换失败的容器,确保应用程序的高可用性,如果某个节点失效,Kubernetes 会自动将该节点上的 Pod 重新调度到其他健康的节点上

服务发现:通过 Service 提供内置的服务发现

106、mybatis如何连接多个数据库(通过mybatisplus实现)

引入依赖

<!--多个数据源-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>3.6.0</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>

在application.properties文件中配置

#指定数据源
spring.datasource.dynamic.primary=master

spring.datasource.dynamic.strict=false
spring.datasource.dynamic.datasource.master.url=jdbc:mysql://192.168.21.37:3306/mall?useUnicode=true&allowPublicKeyRetrieval=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true
spring.datasource.dynamic.datasource.master.username=root
spring.datasource.dynamic.datasource.master.password=root123
spring.datasource.dynamic.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.dynamic.datasource.slave.url=jdbc:mysql://192.168.21.37:3306/210_bm_product?useUnicode=true&allowPublicKeyRetrieval=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true
spring.datasource.dynamic.datasource.slave.username=root
spring.datasource.dynamic.datasource.slave.password=root123
spring.datasource.dynamic.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driver

默认mall数据库,哪个地方使用bm-product-service数据库,哪个地方加入注解@DS("slave")

105、ELK中Filebeat和Logstash的区别(ELK和EFK的区别)

Filebeat
  • 轻量级日志收集器:Filebeat 是一个轻量级的日志文件收集器,主要用于将日志文件从服务器发送到 Logstash 或 Elasticsearch。
  • 低资源消耗:由于其轻量级特性,Filebeat 在资源消耗上非常低,适合在资源受限的环境中使用。
  • 文件监控:Filebeat 可以监控指定的日志文件或目录,实时读取新增的日志内容,并将其发送到指定的目标。
  • 简单配置:Filebeat 的配置相对简单,适合快速部署和使用。
Logstash
  • 数据处理管道:Logstash 是一个数据处理管道,可以从多个来源收集数据,进行转换和处理,然后发送到指定的目标。
  • 丰富插件:Logstash 提供了大量的输入、过滤和输出插件,可以处理各种类型的数据,支持复杂的日志处理逻辑。
  • 数据转换:Logstash 可以对数据进行解析、过滤、 enrich(丰富)和转换,使其更适合存储和查询。
  • 高性能:虽然 Logstash 相对资源消耗较高,但可以通过配置和优化来处理大规模的数据流。

2. 使用场景

106、MySQL union和unionall区别

UNIONUNION ALL 都用于合并两个或多个 SELECT 语句的结果集,它们的主要区别在于如何处理重复的行。

UNION :自动去除重复行,适用于需要唯一结果的场景,性能稍差。

UNION ALL:保留所有行,包括重复行,适用于不需要去重的场景,性能较好。

107、子查询,关联查询,多表联查

子查询

子查询是在另一个查询语句中嵌套的查询。子查询可以出现在 SELECTFROMWHEREHAVING 子句中

子查询在select中
SELECT e.id, e.name, e.salary, 
       (SELECT d.name FROM departments d WHERE d.id = e.department_id) AS department_name
FROM employees e;
子查询在FROM中
SELECT e.id, e.name, e.salary, d.name AS department_name
FROM employees e,
     (SELECT id, name FROM departments) d
WHERE e.department_id = d.id;

关联查询

关联查询是通过两个或多个表之间的关联字段来获取数据。常见的关联类型有内连接(INNER JOIN)、左连接(LEFT JOIN)、右连接(RIGHT JOIN)

内连接inner join:
SELECT e.id, e.name, e.salary, d.name AS department_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.id;

左连接(LEFT JOIN)
SELECT e.id, e.name, e.salary, d.name AS department_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id;

右连接(RIGHT JOIN)
SELECT e.id, e.name, e.salary, d.name AS department_name
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.id;

多表联查

多表联查是指在一个查询中同时涉及多个表的查询。通常使用连接(JOIN)来实现

SELECT e.id, e.name, e.salary, d.name AS department_name, p.name AS project_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.id
INNER JOIN projects p ON e.id = p.employee_id;

108、mybatis动态SQL

MyBatis 提供了强大的动态 SQL 功能,允许根据条件动态构建 SQL 语句。动态 SQL 主要通过 <if><choose><when><otherwise><where><set><foreach> 等标签来实现。

1. <if> 标签

<if> 标签用于根据条件判断是否包含某个 SQL 片段。

<select id="selectUsers" parameterType="map" resultType="User">
    SELECT * FROM user
    <where>
        <if test="name != null">
            AND name = #{name}
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
        <if test="email != null">
            AND email = #{email}
        </if>
    </where>
</select>

2. <choose><when><otherwise> 标签

<choose><when><otherwise> 标签类似于 Java 中的 switch 语句,用于多条件选择。

<select id="selectUsers" parameterType="map" resultType="User">
    SELECT * FROM user
    <where>
        <choose>
            <when test="name != null">
                AND name = #{name}
            </when>
            <when test="age != null">
                AND age = #{age}
            </when>
            <otherwise>
                AND email = #{email}
            </otherwise>
        </choose>
    </where>
</select>

3. <where> 标签

<where> 标签会自动处理 ANDOR 关键字的拼接,并且会自动去掉多余的 ANDOR

<select id="selectUsers" parameterType="map" resultType="User">
    SELECT * FROM user
    <where>
        <if test="name != null">
            name = #{name}
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
        <if test="email != null">
            AND email = #{email}
        </if>
    </where>
</select>

4. <set> 标签

<set> 标签用于更新语句中动态设置字段,会自动处理逗号的拼接,并且会自动去掉多余的逗号。

<update id="updateUser" parameterType="User">
    UPDATE user
    <set>
        <if test="name != null">
            name = #{name},
        </if>
        <if test="age != null">
            age = #{age},
        </if>
        <if test="email != null">
            email = #{email}
        </if>
    </set>
    WHERE id = #{id}
</update>

5. <foreach> 标签

<foreach> 标签用于遍历集合,常用于 IN 子句。

<select id="selectUsersByIds" parameterType="map" resultType="User">
    SELECT * FROM user
    WHERE id IN
    <foreach item="item" index="index" collection="ids" open="(" separator="," close=")">
        #{item}
    </foreach>
</select>

6. <trim> 标签

<trim> 标签用于自定义前缀和后缀,可以替代 <where><set> 标签。

<select id="selectUsers" parameterType="map" resultType="User">
    SELECT * FROM user
    <trim prefix="WHERE" prefixOverrides="AND |OR ">
        <if test="name != null">
            AND name = #{name}
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
        <if test="email != null">
            AND email = #{email}
        </if>
    </trim>
</select>

109、动态代理和静态代理的区别

  • 静态 AOP 实现:AOP 框架在编译阶段对程序源代码进行修改,生成了静态的 AOP 代理类(生成的 *.class 文件已经被改掉了,需要使用特定的编译器),比如 AspectJ。需要我们手动编写代码区创建代理对象·;

  • 动态 AOP 实现:AOP 框架在运行阶段动态生成代理对象(在内存中以 JDK 动态代理,或 CGlib 动态地生成 AOP 代理类),如 SpringAOP。

110、若依的优点和缺点

优点:开发很快,生成一些页面,

缺点:不灵活,比如定制一些规则的时候不方便,要按照他的规范;需要一定学习成本

111、深拷贝和浅拷贝的区别

深拷贝:深拷贝是完全复制了一个对象,是一个新的对象,对新对象的任何操作不会影响到原对象

浅拷贝:只复制了对象的指针而没有复制对象,浅拷贝就是只拷贝了基本数据类型和字符串,对于属性对象他们复制的是地址值;他们指向的还是同一个哈希地址。

更详细的解释:浅拷贝是指在创建新对象时,对于对象中的基本数据类型的成员变量会复制其值,而对于引用类型成员变量则只复制其引用,也就是说新旧对象共享同一个引用类型成员变量指向的对象。如果通过新对象修改了引用类型成员变量指向的对象,那么原对象也会受到影响

spring原型模式是深拷贝

112、java常见的引用类型

常见引用类型分为强、软、弱、虚

强引用:普通的变量引用

软引用:将对象用SoftReference软引用类型的对象包裹,正常情况不会被回收,但是GC做完后发现释放不出空间存放新的对象,则会把这些软引用的对象回收掉。软引用可用来实现内存敏感的高速缓存。

  1. 当所剩内存空间不够我们新的对象存储的时候,直接干掉软引用。

  2. 当所剩内存空间够我们新对象的存储的时候,不会删除我们的软引用对象。

弱引用:将对象用WeakReference弱引用类型的对象包裹,弱引用跟没引用差不多,GC会直接回收掉,只要GC执行了,他就会被回收掉.

虚引用:虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系,几乎不用

threadlocal中的key是弱引用,而value是强引用

113、七层网络协议

你了解的网络协议:HTTP/HTTPS/FTP/SSH/MQTT

114、MyBatis中${}和#{}的区别

#{}用于预编译参数,即在SQL语句中使用占位符(?),并在执行SQL时将实际参数值绑定到这些占位符上。因此可以防止SQL注入问题。

${}用于直接字符串替换,即在SQL语句中直接插入参数值,而不进行预编译,不能预防sql注入问题。

115、如何设计一个秒杀系统

对于前端优化而言,可以从「页面静态化 + CDN」、请求频率限制进行优化。

其中「页面静态化 + CDN」指的是将不变的静态数据固定下来,然后放入 CDN(分布式缓存服务器) 服务器(分布式的缓存服务器),从而提高用户请求的响应速度,降低服务器的并发压力。请求频率限制,则是通过抢购概率与抢购频率限制,降低后端服务器的服务压力。

对于后端优化而言,一般有「增加缓存层 + 预热数据」、「MQ 异步处理」、「限流、熔断、降级」、业务侧优化这 4 种优化方式。

其中「增加缓存层 + 预热数据」指的是将热点数据存入缓存,并在活动开始前提前加载到缓存中,降低数据库层的读取压力。「MQ 异步处理」指的是对于非必要的业务逻辑,通过 MQ 进行异步处理,降低请求处理延时,同时提高业务系统整体稳定性。

「限流、熔断、降级」是对于整体微服务的保护,其中限流指的是对请求进行限制,当超过限流阈值时,直接拒绝请求,保护系统本身;熔断指的是保护下游系统,当请求下游系统连续错误超过阈值时,自动不去请求下游系统,避免因重试流量过大击垮下游系统。

降级指的是当请求失败时,自动返回默认数据,提高用户体验。业务侧优化,则是指从业务层面去进行逻辑优化,从而降低技术复杂度,使得业务与技术复杂度达到一个平衡的状态,有利于更好地实现秒杀系统的高可用与高并发。

116、Comparable和Comparator区别

相同点:Comparable和Comparator都是用于比较排序

不同点:

Comparable实现compareable接口,重写compareTo方法

Comparator外部比较

  • 接口所在包不同:java.lang.Comparable、java.util.Comparator

  • 排序方法不同:Comparable重写方法compareTo(T o)跟类耦合的,只能指定一个排序方式,Comparator重写方法compare(T o1, T o2)可以指定多种排序方式跟类解耦的

  • 排序规则数量限制不同:Comparable唯一字段排序,Comparator可以有多个字段排序

  • 灵活性:Comparable灵活性单一,Comparator灵活性高

117、你了解过哪些数据库

关系型:MySQL、oracle、

非关系型:MongoDB、es、Redis、

MySQL和oracle的区别:MySQL不要钱,oracle要钱

oracle功能更强

oracle支持分布式,MySQL不支持

118、你了解的分库分表的工具

shardingjdbc,mybatisplus

119、用过存储过程吗?

了解过,开发中禁止使用

缺点会给数据库带来很大压力,我们目的就是为了减小数据库压力,保持快进快出的原则,

好处是减轻开发压力,业务开发不需要管了,降低代码耦合

120、你们项目怎么部署的(CICD几个步骤)

上传

121、什么时候序列化

跨进程调用的时候用到序列化,你们现在序列化加那个版本id了吗?没有,因为我们现在是传递数据的格式为json,以前我们传递的是byte字节的时候会加上

122、JWT 身份认证优缺点

1、无状态

有效避免了 CSRF 攻击:CSRF一般被翻译为 跨站请求伪造,为什么能避免攻击:一般情况下我们使用 JWT 的话,在我们登录成功获得 JWT 之后,一般会选择存放在 localStorage 中。前端的每一个请求后续都会附带上这个 JWT,整个过程压根不会涉及到 Cookie。因此,即使你点击了非法链接发送了请求到服务端,这个非法请求也是不会携带 JWT 的,所以这个请求将是非法的

2、跨域支持:可以作为 HTTP 头的一部分或 URL 参数传递

3、安全性:内部签名有加密算法,签名有加密算法并通过前两部分base64编码生成

123、进程和线程的区别

进程是操作系统进行资源调度和分配的基本单位。它是一个正在执行中的程序实例,包含了程序运行所需的资源,如内存地址空间、文件描述符等。

线程是CPU调度和分派的基本单位一个进程可以包含多个线程,这些线程共享进程的资源,如内存空间、文件描述符等,但每个线程有自己独立的程序计数器、栈等

创建线程的方式

1.继承 Thread 类并重写 run 方法创建线程,缺点:实现简单但不可以继承其他类。

2.实现 Runnable 接口并重写 run 方法。避免了单继承局限性,编程更加灵活,实现解耦。

3..实现 Callable 接口并重写 call 方法,创建线程。可以获取线程执行结果的返回值,并且可以抛出异常。

两者区别: 1. Callable可以获取结果值,可以抛出异常,通常要结合FutureTask一起使用。(futureTask.get()没有得到子线程结果之前,一直会阻塞调用者线程)但是Runnable不能获取结果值也不能抛出异常

2.Callable 可以获取子线程的异常

启动新线程的方法,调用start和run方法的区别

线程对象调用 run 方法不开启线程。仅是调用方法。

线程对象调用 start 使用 native开启新的子线程,使用子线程调用 run 方法

线程相关的基本方法

1.线程等待(wait):让当前线程进入waiting状态

2.线程睡眠(sleep):让当前线程进入time-waiting状态

3.线程让步(yield);yield 方法不会阻塞当前线程,只是将当前线程从运行状态变为就绪状态,让其他线程优先执行

4.线程中断(interrupt):它不会立即停止线程的执行,而是设置线程的中断标志,会进入中断状态不会进入阻塞状态

5.Join 等待其他线程终止:使当前线程等待指定的线程终止调用 join 方法的线程会阻塞,直到被等待的线程结束

6.线程唤醒(notify)

wait和sleep的区别

1. 来自不同的类

wait():来自 Object 类;

sleep():来自 Thread 类;

2.关于锁的释放:

wait():在等待的过程中会释放锁;

sleep():在等待的过程中不会释放锁

3.使用的范围:

wait():必须在同步代码块中使用;

sleep():可以在任何地方使用;

线程池的分类

newCachedThreadPool:创建一个可根据需要创建新线程的线程池,适合处理大量的短小任务,能够快速响应新任务,但需要注意资源消耗

newFixedThreadPool:创建一个固定大小的线程池,适合处理大量短小的任务,能够有效控制线程数量,避免资源浪费

newSingle ThreadExecutor:创建一个单线程的线程池,适合需要保证任务执行顺序的场景。

newScheduledThreadPool:创建一个支持定时和周期性任务执行的线程池,适合需要定时或周期性执行任务的场景。

124、类加载器有几个

1.启动类加载器(Bootstrap Class Loader):也称为根类加载器,它负责加载Java虚拟机的核心类库,如java.lang.Object, java.lang.String等。启动类加载器是虚拟机实现的一部分,它通常是由本地代码C或C++实现的,不是Java类。

2.扩展类加器(Extension Class Loader):它是用来加载Java扩展类库的类加载器。扩展类库包括javax和java.util等包,它们位于jre/lib/ext目录下。

3.应用程序类加载器(App Class Loader):也称为系统类加载器,它负责加载应用程序的类。它会搜索应用程序的类路径(包括用户定义的类路径和系统类路径),并加载类文件。

4.自定义类加载器 custom Class Loader开发人员可以根据需要实现的类加载器。

类加载器之间从上到下有父子关系,上层是下层的父加载器

125、解决哈希冲突的方式

1、使用链表(链地址法):每个哈希表的槽位(bucket)包含一个链表(或其他数据结构,如红黑树)。当发生哈希冲突时,将冲突的键值对添加到相应槽位的链表中

2、再次哈希:通过增加哈希表的大小,减少冲突的概率

3、开放寻址法:所有键值对都存储在哈希表的数组中,当发生冲突时,通过某种探查策略找到下一个可用的位置

4、公共溢出区:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表

126、ArrayList和LinkList的区别

ArrayList:

1.ArrayList 是基于动态数组实现的。底层是一个对象数组,可以动态调整大小。

2.内存连续

3.查询的时间复杂度为O(1)

LinkList:

1.底层是双向链表

2.内存不连续

3.查询时间复杂度为O(n)

127、值传递和引用传递的区别

值传递:实际参数的值被复制到形参中,函数内部对形参的修改不会影响实际参数。

引用传递:实际参数的引用(内存地址)被传递给形参,函数内部对形参的修改会影响实际参数

在Java中,无论是基础类型传递还是对象传递,总是按值传递的

128、HashMap ,Hashtable两者之间的区别

线程安全:

HashMap 不是线程安全的,在多线程环境下,如果多个线程同时对 HashMap 进行写操作,可能会导致数据不一致或死锁等问题。HashMap 由数组,链表,红黑树组成

Hashtable 是线程安全的。它的方法都是同步的,即在多线程环境下可以安全使用

允许的键和值:

HashMap 允许一个 null 键和多个 null 值

Hashtable 不允许 null 键和 null 值。

初始容量:

HashMap的初始化容量为16,而Hashtable的初始化容量是11

同步性:

Hashtable是同步(synchronized)的,适合用于多线程环境,而HashMap不是同步的,更适用于单线程的环境

HashMap转为红黑树条件

当数组的长度大于64 和 链表的节点达到8的时候 才转成红黑树

当红黑树的节点小于6的时候,转成链表

,为什么要变为红黑树如果出现极端情况下,查询速度会很慢

扩容因子0.75,倍数为2倍

129、为什么mybatis不建议开启二级缓存

一级缓存:一级缓存是MyBatis默认开启的,它的作用域是一个SqlSession。在同一个SqlSession中,如果执行相同的SQL查询,第一次查询后的结果会被缓存起来,之后的查询会直接从缓存中获取结果,而不需要再次访问数据库。这种缓存只在SqlSession内部有效,当SqlSession关闭或者调用了clearCache()方法时,一级缓存会被清空。

二级缓存:

二级缓存的作用域则更广,它是基于namespace的,也就是说,同一个namespace下的mapper共享同一个二级缓存。二级缓存需要手动开启,并且可以跨SqlSession共享数据。当一个SqlSession查询完数据并关闭后,数据会被存储在二级缓存中,其他SqlSession可以直接从二级缓存中获取数据,而不需要再次查询数据库。

 它可能导致数据不一致性、内存占用过高、缓存同步问题和对复杂查询结果的管理困难

  • 数据更新频率:如果数据更新频繁,不建议开启二级缓存。
  • 数据一致性要求:如果对数据一致性要求较高,不建议开启二级缓存。
  • 内存资源:如果内存资源有限,需要谨慎使用二级缓存。

130、内存泄漏和内存溢出的区别

内存泄漏:程序在申请内存后,未能释放已经不再使用的内存,导致这部分内存无法被再次利用,从而逐渐消耗系统的可用内存资源。

内存溢出:程序在运行过程中,所需的内存超过了系统或虚拟机(如 JVM)所能提供的最大内存限制,导致程序无法继续执行,抛出 OutOfMemoryError 异常。

131、如何对SQL语句进行优化

1.使用合适的索引

2.避免使用 SELECT *:只选择需要的列,减少数据传输量和内存消耗

3.尽量避免使用子查询

4.避免使用or使用 UNION 代替 OR:多个 OR 语句可能导致索引失效。

根据业务场景建立复合索引只查询业务需要的字段,如果这些字段被索引覆盖,将极大的提高查询效率.

多表连接的字段上需要建立索引,这样可以极大提高表连接的效率.

where 条件字段上需要建立索引, 但 Where 条件上不要使用运算函数,以免索引失效.

排序字段上, 因为排序效率低, 添加索引能提高查询效率.

优化 order by 语句: 在使用 order by 语句时, 不要使用 select *, select 后面要查有索引的列, 如果一条 sql 语句中对多个
列进行排序, 在业务允许情况下, 尽量同时用升序或同时用降序. 

优化 group by 语句: 在我们对某一个字段进行分组的时候, Mysql 默认就进行了排序,但是排序并不是我们业务所需的, 额外的排序会降低效率. 所以在用的时候可以禁止排序, 使用 order by null 禁用. 

select age, count(*) from emp group by age order by null

尽量避免子查询, 可以将子查询优化为 join 多表连接查询.

132、你知道的传输协议有哪些

1、 TCP(传输控制协议)

2、 UDP(用户数据报协议)

3、HTTP(超文本传输协议)

4、HTTPS(安全超文本传输协议)

5、FTP(文件传输协议)

6、MQTT(Message Queuing Telemetry Transport)是一种轻量级的消息传输协议

133、JVM垃圾回收器分类

PS(年轻代)/PO(老年代)

G1:既可以处理年轻代也可以处理老年代,把真实的物理内存抽象成一块一块的,可以设置处理时间,分批次处理,可以控制STW时间

parnew(年轻代)和CMS(老年代)

134、为什么元空间初始化大小设置为128,那两个非标的为什么设置一样都是128

为了避免频繁地fullgc垃圾回收

135、谈一下你对高并发的看法以及处理

①分库分表 ②Spring Cloud Sentinel [哨兵] ③K8S 部署应用 [可以动态扩展副本] ④用nginx作负载均衡 ⑤Redis作缓存层 ⑥mysql数据库用集群 (读写分离)  7. MQ异步 8. 前端静态页面 9. CDN

136、你们项目中哪里用到了延迟队列

下单的时候实现订单关闭的时候,订单下单成功以后,会把订单放入一个延迟队列中,如果在十分钟内用户支付成功了,就把                                                                                 

137、mybatis底层接口实现原理

AOP、动态代理两方面:

MyBatis使用Java的动态代理技术来实现接口的自动绑定。当你配置了一个Mapper接口并声明其对应的XML映射文件后,MyBatis会在运行时为这个接口生成一个代理对象。这个代理对象会拦截所有对Mapper接口方法的调用,根据方法名和参数映射到对应的SQL语句上执行,然后将结果转换为Java对象返回。

精简版:MyBatis 使用动态代理技术为 Mapper 接口生成代理对象,代理对象拦截对接口的方法调用,根据方法名和参数映射到对应的 SQL 语句执行,并返回结果。

AOP:通过 AOP 可以在方法调用前后添加额外的逻辑,如事务管理和日志记录

138、问到反射就把mybatis拦截器,JWT、TOKEN、threadlocal等一些系列说出来

139、spring实现代理的方式

静态代理:

  • 定义接口:定义一个接口,声明需要实现的方法。
  • 实现被代理类:创建一个实现该接口的类,这个类就是被代理类。
  • 实现代理类:创建另一个实现同一接口的类,这个类就是代理类。在代理类中,通过持有被代理类的实例,调用被代理类的方法,并在调用前后添加额外的逻辑。
  • 编译时确定:代理类和被代理类的关系在编译时就已经确定,代理类需要手动编写

动态代理:

JDK 动态代理:只能代理实现了接口的类,通过 java.lang.reflect.Proxy 类生成代理对象。

CGLIB 动态代理:可以代理没有实现接口的类,通过字节码技术生成目标类的子类,实现方法的拦截和增强。

140、如何防止SQL注入

1、预编译SQL语句

2、使用ORM框架:ORM 框架(如 Hibernate、MyBatis、Django ORM)通常会自动处理 SQL 注入问题。

3、输入验证和转义:对用户输入进行严格的验证和转义,确保输入符合预期的格式。

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

1、new

2、反射

3、克隆

4、工厂代理

5、反序列化

142、SKU与SPU(电商里用得到)

 SPU(Standard Product Unit):SPU 是标准产品单元,代表一个标准化的产品。SPU 描述的是商品的基本信息,不包括具体的规格和属性;例如:iPhone 13

 SKU(Stock Keeping Unit):SKU 是库存保有单位,代表一个具体的商品规格。SKU 用于区分同一 SPU 下的不同规格、颜色、尺寸等属性的商品。例如:iPhone 13 128GB 蓝色

实际上我们没有实现SKU,解决方法就是直接上架多个商品,比如上架苹果13玫瑰粉、苹果13远峰蓝

关系:一个 SPU 可以对应多个 SKU,一个SKU必定对应一个SPU

143、List、Set、Map的区别

list:值可以重复,有序。

set :不能重复,无序,唯一

Map:key不能重复,但有重复value。存储双列数据不一定有序。 以键值对的形式存储

这三个接口在存取元素时有什么特点?

List接口:以特定索引存取元素,有序。可以有重复元素。继承collection,可以插入多个null,实现类有ArraryList、LinkList

Set接口:不可以存放重复元素(使用equals方法区分是否重复)。继承collection,只能存放一个null,实现类有HashSet、LinkedHashSet、TreeSet

Map接口:保存的是键值对(key-value-pair)映射,映射关系可以是一对一或者多对一(key唯一)。键与值是否为空需要看对应的map类型(

HashMap 和 LinkedHashMap:允许存储一个 null 键和任意数量的 null 值、

TreeMap:允许存储 null 值,但不允许存储 null 键、

Hashtable 和 ConcurrentHashMap:不允许存储 null 键和 null 值

),

实现类有HashMap、TreeMap、Hashtable、LinkedHashMap、ConcurrentHashMap

144、哪些集合类是线程安全的?

map:

ConcurrentHashMap:线程安全的哈希表实现,允许多个读取操作同时进行,但写操作会加锁

Hashtable:线程安全的哈希表实现,所有方法都是同步的。性能相对较差,因为每次操作都会加锁

list:

CopyOnWriteArrayList:适用于读多写少的场景

Collections.synchronizedList:将现有 List 包装成线程安全的 List

set:

CopyOnWriteArraySet:适用于读多写少的场景

Collections.synchronizedSet:将现有 Set 包装成线程安全的 Set

145、常见的Java算法

1.冒泡排序它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。

2.二分查找法

  • 计算集合的中间元素。

  • 将关键项与中间元素进行比较。

  • 第一次判断如果key = middle(中间)元素,则返回找到的键的中间索引位置。

  • 否则,如果键>中间元素,则键位于集合的右半部分。因此,在集合的下半部分(右)重复步骤1到3。

  • 其他键<中间元素,则键在集合的上半部分。因此,您需要在上半部分重复二分查找。

146、对于Spring IOC和Spring AOP的理解

1、IoC(Inverse of Control:控制反转)

Spring IOC是一个管理对象之间依赖关系的容器,将对象之间的依赖关系交由容器来管理Bean对象,它的核心在于控制反转,使原本手动创建对象交给容器来实现,另外一个核心是依赖注入实现方式有构造器注入、Setter方法注入、属性注入等;实现服务之间的松耦合,提高代码可读性和可维护性;

2、Spring AOP(Aspect-Oriented Programming:面向切面编程)

手写的starter、@translation声明式事务、

147、undo_log和redo_log表的区别

undo_log:

记录了事务提交前数据库的数据,以便在事务回滚时可以撤销这些更改,确保事务的原子性

redo_log 记录了事务对数据库的所有修改操作,以便在系统崩溃后可以重新执行这些更改,确保事务的持久性

147、HTTP 中重定向和请求转发的区别

重定向 是服务器告诉客户端(通常是浏览器)去访问另一个 URL,客户端收到响应后会发起一个新的请求到新的 URL

重定向 特点是:两次请求,客户端有感知,url发生变化,可以跨服务器

请求转发 是服务器内部将请求转发到另一个资源(如另一个 Servlet、JSP 页面等),客户端不知道这个过程,仍然认为请求是在同一个 URL 上处理的。

请求转发 特点是:一次请求,客户端无感知,url不变,只能在同一服务器内

148、http和https的区别

http协议和https协议的区别主要是:传输信息安全性不同、连接方式不同、端口不同、证书申请方式不同

https更加安全;

http基于tcp协议,https在http的基础上加入了SSL/TLS加密协议;

http端口为80,https端口为443;

http不需要证书,https需要证书

149、锁的升级过程

无锁--偏向锁--轻量级锁(自旋锁)--重量级锁

150、Java中锁的分类

151、Java成员变量和局部变量的区别

成员变量:在类中声明,但在方法、构造器或块之外,存储在堆中,作为对象的一部分

局部变量:在方法、构造器或块内部声明的变量,存储在栈中

153、JDK和JRE区别

JDK:Java Development Kit 的简称,Java 开发工具包,提供了 Java 的开发环境和运行环境。

JRE:Java Runtime Environment 的简称,Java 运行环境,为 Java 的运行提供了所需环境。

JVM:Java虚拟机

154、“==”与equals的区别

“==”:对于基本数据类型,比较的是值是否相等,对于引用数据类型,比较的是引用地址值是否相等

equals:equals 默认情况下是引用比较,只是很多类重写了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等

155、基本数据类型与引用数据类型

基本数据类型

数据类型描述占用字节数
byte8 位有符号整数1 字节
short16 位有符号整数2 字节
int32 位有符号整数4 字节
long64 位有符号整数8 字节
float32 位单精度浮点数4 字节
double64 位双精度浮点数8 字节
char16 位 Unicode 字符2 字节
boolean布尔值(true 或 false1 字节(实际占用可能因 JVM 实现而异)

引用数据类型

数据类型描述占用字节数(典型值)
Object任何对象的引用4 字节(32位系统) / 8 字节(64位系统)
String字符串对象的引用4 字节(32位系统) / 8 字节(64位系统)
Array数组对象的引用4 字节(32位系统) / 8 字节(64位系统)
Class类对象的引用4 字节(32位系统) / 8 字节(64位系统)
Interface接口对象的引用4 字节(32位系统) / 8 字节(64位系统)

156、final的作用

  • inal 修饰的类叫最终类,该类不能被继承。

  • final 修饰的方法不能被重写。

  • final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。

157、Java 中操作字符串都有哪些类以及他们的区别

String、StringBuffer、StringBuilder

1.在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。

2.StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer

158、currenthashmap和hashmap区别

线程安全ConcurrentHashMap线程安全,HashMap 线程不安全

性能

单线程下,HashMap 的性能通常优于 ConcurrentHashMap

多线程下,多线程环境下,ConcurrentHashMap 的性能通常优于 HashMap

内部实现:HashMap 是数组 + 链表/红黑树,ConcurrentHashMap是数组 + 链表/红黑树,在此基础上使用了CAS算法以及分段锁

ConcurrentHashMap新特点:分段锁机制、CAS 算法、扩容机制

159、产生死锁的条件以及如何避免

当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁

死锁的产生的一些特定条件:四个条件都满足才会造成死锁

Java 中,死锁产生的一些特定条件通常包括以 下四个方面:

1. 互斥条件:一个资源一次只能被一个线程持有,如果其他线程想要获取该资源,就必须等待该线程释放该资源。

2. 保持条件:一个线程请求资源时,如果已经持有了其他资源,就可以保持对这些资源的控制,直到满足所有资源的要求才释放。

3. 不剥夺条件:已经分配的资源不能被其他线程剥夺,只能由持有资源的线程释放。

4. 环路等待条件:多个线程形成一种循环等待的关系,每个线程都在等待其他线程所持有的资源,从而导致死锁的产生。

如何避免死锁

要避免线程死锁,可以采取以下几种方法:

1. 尽量避免使用多个锁,尽量使用一个锁或者使用更加高级的锁,例如读写锁或者 ReentrantLock。

2.减少锁的粒度, 确保同步代码块的执行时间尽可能短,这样可以减少线程等待时间,从而避免死锁的产生。

3. 使用尝试锁,通过 ReentrantLock.tryLock() 方法可以尝试获取锁,如果在规定时间内获取不到锁,则放弃锁。

4. 避免嵌套锁,如果需要使用多个锁,请确保它们的获取顺序是一致的,这样可以避免死锁。

160、ConcurrentHashMap新特点(*********)

1. 分段锁机制:ConcurrentHashMap 通过将数

据分成多个 segment 来实现锁的粒度更细,从而减小锁的竞争范围,提高并发性能。如果CAS算法不行了才会升级为分段锁,只修改我的那一部分(segment)

2. CAS 算法:ConcurrentHashMap 在对数据进行更新时,采用 CAS(Compare And Swap)算法来保证更新的原子性,避免了锁的颗粒度过大而带来的性能问题。采用的乐观锁机制,先修改,再去判断那个版本号正不正确,如果版本号是我们的就不管,不是我们的就更新;

3. 扩容机制:ConcurrentHashMap 在扩容时,只需要对需要扩容的 Segment 进行扩容,而不是对整个 Map 进行扩容,这样可以减少扩容对并发性能的影响。

161、基本数据类型不能用equals比较,因为他们不具备equals的方法

162、Java中的集合类有哪些

  • 接口/类实现类特点是否线程安全是否允许重复是否有序
    Collection
    ListArrayList基于动态数组,支持随机访问,插入和删除操作较慢
    LinkedList基于双向链表,插入和删除操作较快,不支持高效的随机访问
    Vector类似于 ArrayList,方法是同步的,线程安全
    Stack继承自 Vector,实现了一个后进先出(LIFO)的栈
    SetHashSet基于哈希表,不保证元素的顺序
    LinkedHashSet基于哈希表和链表,保证元素的插入顺序
    TreeSet基于红黑树,元素自然排序或通过比较器排序
    MapHashMap基于哈希表,不保证键值对的顺序
    LinkedHashMap基于哈希表和链表,保证键值对的插入顺序
    TreeMap基于红黑树,键值对按键的自然顺序或通过比较器排序
    Hashtable类似于 HashMap,方法是同步的,线程安全
    ConcurrentHashMap线程安全的哈希表实现,性能优于 Hashtable

Collection 和 Collections 有什么区别

  • Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,所有集合都是它的子类,比如 List、Set 等。

  • Collections 是一个包装类,包含了很多静态方法,不能被实例化,就像一个工具类,比如提供的排序方法:Collections. sort(list)。

163、常见的RPC框架有?

远程过程调用,是一种通过网络从远程计算机程序上请求服务,而无需了解底层网络技术的协议。简化了分布式系统的调用。

gRPC(谷歌的)、dubbo 、Spring Clould openfeign等

164、stream类可以被继承吗?可以

165、集群模式

主从,哨兵,Redis Cluster模式(官方的)

redis数据类型应用场景:

166、redis的压缩机制

当对同一条数据进行两次修改的时候,我们保存最新的一条

167、进程和线程区别

j进程是程序运行和资源分配的基本单位,一个程序至少有一个1进程,一个进程至少有一个线 程,但一个进程一般有多个线程。

进程在运行过程中,需要拥有独立的内存单元,否则如果申请不到,就会挂起。而多个线程能共享内存资源,这样就能降低运行的门槛,从而效率更高。

线程是是cpu调度和分派的基本单位,在实际开发过程中,一般是考虑多线程并发。

168、实例化变量方式

new、克隆、反射、反序列化

克隆属于object类,用的浅拷贝

169、如何写一个类

1、类名、属性、方法等见名知意

2、使用注释和文档

3、满足单一职责,一个类应该只做一件事,并且做好这件事

4、开闭原则:主要分为对扩展开发,对修改关闭

5、写单元测试

170、nacos配置方式

一种根据服务名

一种根据分组

171、有过代码优化经验吗

SQL优化

我们手写的starter

尽量避免使用嵌套循环

部分变量可以写为静态变量

JVM参数优化

172、你们公司几台服务器

我们现在是基于k8s发布的,k8s是对服务器的抽象,内存不够会自动伸缩;一台k8s服务器

174、xxl-job的应用场景(重点*********)

你在那里用到了xxl-job?
每天将MySQL的慢日志抓取到技术中心
商品中心的商品信息全量更新到es中
购物车中用户加入了购物车却没有支付的商品,存储在redis中,每晚需要定时推送到交换机上
使用XXl_Job监控消息队列的消息数量是否超出阈值(比如监控死信队列,并进行预警)
定时查询订单系统和库存系统订单对比,判断销售量是否一致(保证最终一致性)
可以用来定时删除redis黑名单
为解决缓存雪崩,使用xxl-job进行缓存预热

15天以后订单自动签收完成

175、为什么要用Redis分布式锁

1. 数据一致性

2. 高性能

3. 可靠性

4.锁的公平性

5.锁的超时机制

176、timer、spring task xxl-job之间的区别

为什么不使用springtask而用xxl-job:xxl-job天生支持分布式

177、为什么阿里巴巴不让使用JDK自带的线程池

阿里巴巴在《阿里巴巴Java开发手册》中建议开发者不要直接使用 java.util.concurrent.Executors中的静态工厂方法来创建线程池,而是推荐直接使用 ThreadPoolExecutor 类来实例化线程池

原因:

1、资源耗尽的风险,最终引发OOM

2、线程池参数优化:优化不方便;但是ThreadPoolExecutor 允许自定义更多的参数

3、拒绝策略不明确

4、监控和调试困难

178、线程池的状态

1、RUNNING:这是线程池的初始状态。线程池可以接受新的任务提交,并且可以处理队列中的任务

2、SHUTDOWN:线程池不再接受新的任务提交,但会继续处理已经提交到队列中的任务

3、STOP:线程池不再接受新的任务提交,也不再处理队列中的任务,会中断正在执行的任务

4、TIDYING:当所有任务都已终止(无论是正常完成还是被中断),并且 terminated() 方法即将被调用时,线程池进入 TIDYING 状态。

5、TERMINATED:terminated() 方法已经被调用,线程池的所有资源已经释放,线程池完全终止。

179、GET和POST请求的区别

1、用途:

GET: 用于请求服务器上的资源,通常是获取数据

POST: 用于向服务器提交数据,通常是提交表单数据或上传文件等

2、数据传输方式:

GET: 数据通过 URL 参数传递,附在 URL 后面

POST: 数据通常包含在请求体中,而不是直接暴露在 URL 中。

3、缓存:

GET: 可以被浏览器缓存。

POST: 通常不会被缓存,不会被保存在浏览器历史记录中

4、安全性:

GET: 不安全。

POST:相对安全

5、幂等性

GET: 天生支持幂等性。

POST:不幂等

180、同步与异步的区别以及BIO/NIO/AIO

同步: 同步就是发起一个调用后,被调用者未处理完请求之前,调用不返回。

异步: 异步就是发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时我们可以处理其他的请求,被调用者通常依靠事件,回调等

阻塞和非阻塞

  • 阻塞: 阻塞就是发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。

  • 非阻塞: 非阻塞就是发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。

BIO/NIO/AIO的区别

  • BIO同步阻塞I/O模型,它使用一个线程来处理一个请求,如果这个请求没有被处理完,这个线程就会一直等待,直到请求处理完成。这种模型适用于连接数较小的情况。

  • NIO同步非阻塞I/O模型,它使用单个线程来处理多个请求,,它通过轮询的方式来处理I/O请求,如果有I/O请求就处理,没有就继续轮询。这种模型适用于连接数较多但是连接时间短的情况

  • AIO异步非阻塞I/O模型,这种模型不需要为每个请求创建一个线程,由操作系统内部的线程来处理I/O请求,同时它也具有非阻塞的特性,I/O请求不会一直等待,而是异步执行。这种模型适用于连接数很多的情况

  • 我们用的就是异步AIO

181、普通类和抽象类有哪些区别?

  • 普通类不能包含抽象方法,抽象类可以包含抽象方法,注意抽象类不是必须要有抽象方法的。

  • 抽象类不能直接实例化,普通类可以直接实例化。

182、Synchronized与Lock 区别以及各自特点

SynchronizedLock 是 Java 中两种常用的同步机制,用于控制多线程环境下的资源共享和互斥访问

Synchronized 关键字(自动释放锁):使用时只需要在方法或代码块前加上 synchronized 关键字即可

1.互斥性:Synchronized 保证同一时刻只有一个线程可以获取锁,并且只有该线程可以执行同步代码块中的代码。

2. 可重入性:同一个线程可以多次获取同步锁而不会被阻塞,这样可以避免死锁的发生。

3. 独占性: 如果一个线程获得了对象的锁,则其他线程必须等待该线程释放锁之后才能获取锁。

4.非公平锁 ,当锁被释放后,任何一个线程都有机会竞争得到锁,这样做的目的是提高效率,但缺点是可能产生线程饥饿现象。

不可中断性:一旦一个线程获得了锁,其他线程只能等待,即使等待的线程被中断也不会提前释放锁。

自动放锁

Lock 接口(手动释放锁):

互斥性Lock 是一个互斥锁,即同一时间只有一个线程可以持有锁。

可重入性:支持可重入,即同一个线程可以多次获取同一个锁而不会导致死锁。

公平性:可以通过构造函数指定是否为公平锁。如果是公平锁,那么等待时间最长的线程优先获取锁。

可中断性:支持可中断锁,即等待获取锁的线程可以被中断

手动放锁

183、什么是分库、分表

分库分表是指,将数据库与表进行不同维度的切分,以减少服务器与数据库负担,缩短数据查询时间。无论是分库还是分表,都是从水平与垂直两个维度进行切分。

垂直切分

垂直分库:按照业务将表进行分类,分布到不同的数据库上面,专库专用,比如电商库可拆分为用户库和商品库

垂直分表:按照字段访问频次,将一张表的字段拆分到多张表当中,每张表仅存储其中一部分字段,拆分后数据的行数保持不变

水平切分

水平分库:将表水平切分后 按某个字段的某种规则分到不同的数据库,使得每个库具有相同的表,表中的数据不相同,水平分库一般是伴随水平分表;各个库保存的表结构是一致的,但是表中内容不一样

水平分表:整张表在数据结构不变的情况下,将大表按一定规则拆分成多个小表,表结构一样但数据不一样

总结:垂直结构不一样,数据行数一样(数据不一定一样),水平结构一样,数据不一样

184、final,finally,finalize

final:修饰类、方法、变量,被修饰的类不可被继承,被修饰的方法不能重写,修饰的变量一旦初始化不可被修改

finally:在 try-catch 语句块中定义一个总是被执行的代码块

finalize:在对象被垃圾回收之前进行资源清理工作

185、Set是怎么去重的?为什么要重写equals   

Set接口的实现类(如HashSet, TreeSet, LinkedHashSet等)都保证了集合中的元素是唯一的,即不允许重复的元素。去重的过程依赖于元素的equals()和hashCode()方法。

hashSet,底层使用的是hashmap,判断两个对象是否相等? 
     1:获取对象的hashcode,找到对应的桶位,如果没有数据,直接存,
     2:如果有数据,调用equals方法,判断是否是同一个对象,是:丢弃  不是:链表 

为什么重写equals要重写 hashcode?

重写equals:用于确定两个对象是否逻辑上相等。如果两个对象的 equals() 方法返回 true,那么它们在 Set 中被视为同一个元素

hashCode() 方法:用于快速定位哈希表中的位置。如果两个对象的 hashCode() 值不同,那么它们在哈希表中的位置也会不同;如果 hashCode() 值相同,但 equals() 返回 false,则它们仍被认为是不同的元素。


如果重写了equals,两个对象equals返回true,java规范中约定 hashcode就必须一致。所以要重写 hashcode

186、Java异常

1. 检查异常(Checked Exception):在程序中必须处理,否则编译器将不允许代码通过编译。例如: IOException 、 SQLException 等。

2. 运行时异常(Runtime Exception):在程序中可以不进行显示处理的异常。例如: NullPointerException 、 ArrayIndexOutOfBoundsException 等。

3. 错误(Error):表示系统级别的错误,程序无法处理,一旦出现此类错 误,程序将立即停止运行。例如: OutOfMemoryError 、 StackOverflowError 等。

需要注意的是, Error 与 Exception 不同。 Error 表示系统级别的错误或资源耗尽问题,对于这类错误,程序无法处理,只能选择终止程序的运行。而 Exception 表示由程序员编写的代码出现的异常情况,可以通过代码进行处理。

187、Spring Cloud(openfeign) 和 Dubbo的区别

通信方式

配置方式

适用场景

188、方法重载和重写的区别

重载:方法重载是指在同一个类中,可以有多个同名的方法,但这些方法的参数列表必须不同

重写:方法重写是指在子类中重新定义父类中已有的方法。重写的方法必须具有相同的名称、参数列表和返回类型。重写方法的目的是为了在子类中提供特定的实现。

189、springboot相较于SSM区别

约定大于配置、开箱即用、内置tomcat、支持微服务、生态系统丰富

190、vue中的watch和computed的区别

watch它监听它里面某一个函数的变化,computed监听的是这一组里面所有函数的变化,所以watch性能高一些,computed性能低一些

191、你们项目的支付怎么做的

使用聚合支付平台的接口

192、springcloudalibaba你最熟悉的是哪些

193、JVM调优做过吗

用Jconsole、VisulVm、阿里的阿尔萨斯;用

194、你们JDK用的什么版本,垃圾回收器呢

JDK1.8,垃圾回收器用的G1

195、你说一下双亲委派

是一种类加载机制,

类加载器分类:启动类、应用程序,扩展类、自定义

196、你说一下线程池的核心参数

核心线程数、最大线程数、最大空闲等待时间、阻塞队列、拒绝策略

197、线程的拒绝策略

  • AbortPolicy抛出 RejectedExecutionException 异常,拒绝任务
  • CallerRunsPolicy:把任务交给调用者去执行
  • DiscardPolicy:静默丢弃被拒绝的任务不抛出任何异常
  • DiscardOldestPolicy丢弃最早进入队列任务,然后重新尝试提交被拒绝的任务

198、JUC接口Lock实现类有哪些(****************)

ReentrantLock

ReentrantLock 是一个可重入的互斥锁,类似于 synchronized 关键字,支持公平锁和非公平锁,提供tryLock 方法

为什么有了synchronized 还要使用ReentrantLock:

锁的粒度synchronized 关键字作用于整个方法或代码块,锁的范围较大,而ReentrantLock 允许更细粒度的锁控制,可以精确地控制哪些部分需要加锁,哪些部分不需要

公平性:synchronized 非公平,ReentrantLock可设置公平或非公平

锁的升级synchronized 本身可以自升级锁,

获取锁synchronized 会阻塞直到获取到锁,ReentrantLock 提供了 tryLock() 方法,允许线程尝试获取锁,如果锁不可用则立即返回 false,而不是阻塞等待

ReentrantReadWriteLock

ReentrantReadWriteLock 是一个读写锁,允许多个读线程同时访问,但写线程独占锁,读锁和写锁分离

StampedLock

StampedLock 是一个读写锁,提供了更高的性能和更细粒度的锁控制,支持乐观读锁、读锁和写锁

AbstractQueuedSynchronizer :

AbstractQueuedSynchronizer 是一个抽象类,用于构建锁和其他同步器的基础框架

199、你们的人脸识别

物业的门禁管理,通过人脸识别来开门

商场或者小区的人脸识别,业主人脸数据的采集、生成临时的一次行吗,帮助外卖员进入、热力图什么时间段人流量大,分析人流量中的性别、

200、mysql的undolog、redolog和seata的区别

201、MySQL的日志

1、慢日志

2、binlog:三种格式的文件,一种记录SQL语句,一种记录数据

MySQL 提供了三种 binlog 格式,分别是:

  1. Statement:(记录SQL语句)

    • 每个SQL语句都会被记录到 binlog 中。这种方式的优点是日志量较小,但缺点是在某些情况下可能会导致主从数据不一致(例如,包含随机数的SQL语句)。
  2. Row:(记录数据)

    • 每一行数据的变更都会被记录到 binlog 中。这种方式的优点是数据一致性更高,但缺点是日志量较大。
  3. Mixed:(混合的)

    • 结合了 Statement 和 Row 的特点,MySQL 会根据具体情况选择合适的格式来记录 binlog。这种方式既保证了数据的一致性,又尽量减少了日志量。

3、undolog

4、redolog

202、内部类和静态内部类区别

内部类:是定义在另一个类(外部类)中的非静态成员类。它可以访问外部类的所有成员(包括私有成员)

静态内部类:是定义在另一个类(外部类)中的静态成员类。它可以独立于外部类的实例存在

1. 静态内容的支持:
    普通内部类:不能定义静态成员。
    静态内部类:可以定义静态成员,可以直接通过 外部类类名.内部类类名.静态内容 的方式访问。
2. 访问外部类成员:
    普通内部类:可以访问外部类的所有成员。
    静态内部类:只能访问外部类的静态成员,不能访问普通成员。
3. 对象创建方式:
    普通内部类:必须基于外部类对象来创建。
    静态内部类:基于外部类类名即可创建。
 

203、你们用的IO模型(AIO/BIO/NIO)

BIO同步阻塞

NIO同步非阻塞

我们用的AIO异步非阻塞

204、Java中的锁

205、Files的常用方法有哪些

Files.exists():检测文件路径是否存在。
Files.createFile():创建文件。
Files.createDirectory():创建文件夹。
Files.delete():删除一个文件或目录。
Files.copy():复制文件。
Files.move():移动文件。
Files.size():查看文件个数。
Files.read():读取文件。
Files.write():写入文件

206、数据库三范式第二范式解决行冗余,第三范式解决列

207、lua脚本为什么要使用多条命令

比如我们的出库,有破损出库、调拨出库、销售出库等,根据不同场景执行不同的命令,保证我们库存扣减不出错

208、count(*)和count(1)的区别

1、count(*):明确表示统计所有行,包括 NULL 值

2、count(字段)过滤null值,效率比count(*)差

3、count(*)做了优化,它会找最合适的索引遍历,性能高于count(1)

count(*)走索引

209、redis为什么使用单线程

1、简化设计:避免数据竞争:由于 Redis 是内存数据库,所有的数据都存储在内存中,单线程模型避免了多线程环境下常见的数据竞争和死锁等问题,从而保证了数据的一致性和安全性。

2、易于实现事务:单线程环境下处理事务更加简单,因为事务中的所有操作都是顺序执行的,不需要担心并发操作带来的复杂性。

3、性能考虑:尽管 Redis 使用单线程模型处理客户端请求,但其内部采用了非阻塞的事件驱动架构(Event Loop)来处理网络通信。这意味着 Redis 可以有效地利用现代操作系统提供的 I/O 多路复用机制(如 Linux 的 epoll),即使是在单个线程中也能高效处理大量的并发连接。

4、可预测性:单线程环境下,Redis 的行为更加可预测,因为不会有来自不同线程的不可预期的交互影响。

5、资源消耗

209、封装的好处

提高代码复用性

提高代码可维护性

有利于高内聚低耦合

210、有参构造调用无参构造怎么做

调用本类无参构造用this() 关键字用于调用同一个类中的另一个构造方法。需要注意的是,this() 必须是构造方法中的第一行代码

调用父类无参构造用surper(),这一句最好别说

211、synchronized的静态方法和非静态方法的区别

非静态方法锁的是this当前对象

静态方法锁的是当前类,类就是元空间的类对象

212、Seata的三大角色

在 Seata 的架构中,一共有三个角色:

TC (Transaction Coordinator) - 事务协调者(带头大哥

维护全局和分支事务的状态,驱动全局事务提交或回滚。

TM (Transaction Manager) - 事务管理器 (鸡妈妈

定义全局事务的范围:开始全局事务、提交或回滚全局事务。

RM (Resource Manager) - 资源管理器

管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

其中,TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端。

三张表:

global_table 就是存储的全局事务信息(长事务)

branch_table 存储事务参与者(短事务)

lock_table 行锁,当多个事务同时尝试修改同一行数据时,lock_table 用于确保只有一个事务能够成功获取锁,其他事务需要等待锁释放后才能继续执行。

                                                                                                                       

分布式事务中redolog日志,一般用于恢复已确认但未写入数据库的数据,记录的是数据修改后的值undolog一般用于事务的取消与回滚,记录的是数据修改前的值

213、堆和栈的区别

栈是线程私有的,堆是线程共享的

栈自动分配和释放资源,堆手动分配和释放

存储内容:栈存储变量、函数;堆存放动态数据,new的对象

类的信息存储在元空间(类信息:类名,包名,字段,创建类对象的方法),类的内容(类里面的变量,方法)存储在堆中

214、Java常见运行时异常以及受检异常

运行时异常:是那些不需要强制处理的异常,即编译器不会强制要求捕获或声明这些异常

NullPointerException(空指针)

ArrayIndexOutOfBoundsException(数组索引越界)

IllegalArgumentException(传参不合法)

ClassCastException(类型转换失败)

ArithmeticException(算数异常)

受检异常:是那些需要强制处理的异常,即编译器会强制要求捕获或声明这些异常

IOException:输入输出操作中发生的异常

FileNotFoundException:打开的文件不存在

SQLException:数据库中发生异常

InterruptedException:线程被中断

215、中断线程的方式(interrupt,他不会立马中断,中断时间由系统自己决定)

线程阻塞:死锁、sleep、join,year是会让步

216、为什么造成了OOM,因为没有用remove清理

217、单例模式实现方式

懒汉式、双重检锁式(线程安全版懒汉式)、饿汉式、静态内部类、枚举类

218、JWT优点:

无状态:无状态意味着服务器端不需要存储任何关于会话的状态信息。当客户端发送带有JWT的请求时,服务器可以从JWT中解析出所需的所有信息,而不需要查询数据库或其他存储来验证用户的身份

防止CSRF:防止CSRF攻击(跨站请求伪造攻击)

单点登录:同家公司的产品用户只需要在一个地方登录,其他产品就不需要登录了,比如在淘宝登录然后天猫就不需要登录了

允许跨域:跨域指的是从一个域名的网页去请求另一个域名的资源,导致跨域的原因是同源策略

多语言支持:

219、为什么使用axios而不用ajax

axios是对ajax的封装,axios提供了请求和响应拦截器,axios有简洁的API、跨平台支持

220、索引的数据结构

B树:叶子节点和非叶子节点都有数据和索引

B+树:非叶子节点只存索引,而叶子结点有数据也有索引,并且所有叶子节点通过指针相互连接,形成一个有序链表,便于范围查询

哈希

位图

221、JDK1.8新增了哪些方法以及他们的区别

接口允许有方法和静态方法

IO流:输入输出

文件:输入输出

222、varchar和char的区别

char的性能好一些

varchar空间利用率好一些,char差一些

varchar动态

223、你觉得什么是微服务微服务的好处

好处:

降低耦合度

快速更新迭代,快速响应

可以跨语言

发布的时候根据应用自动伸缩

224、你们是如何进行redis持久化的(RDB/AOF)

获取线程参数

获取核心线程数:getcollpoolsize

获取最大线程数:getcollmaxpoolsize

获取线程状态:get status

synchorized在

225、事务传播属性:

226、为什么上传excel里用到countloack,保证原子性,防止高并发下、

227、采购单有几张表:

master、item

228、你对多态的理解:父类引用指向子类实例,子类实例的状态变了就会引发父类引用状态发生变化

229、你们项目中的异常处理机制:

try catch   、throw

230、说一下你们用过的工作流:

我实际项目中没有用过,可以使用第三方flowable

231、你们项目怎么开发的

需求分析发布会、讨论技术栈的选型、构建接口、分配任务具体实现、开发完以后开始联调测试、联调没问题就上线

你对管理有兴趣吗?

232、JDK自带的查看JVM运行参数的命令

233、你们项目绩效怎么算的:考勤、项目完成度、代码质量

234、加索引的原则:

短索引、尽量使用复合索引减少回表次数,不要加太多索引,索引遵循最左匹配原则

235、setnx锁缺点:

SETNX 是 Redis 中的一个命令,意思是如果不存在则设置,这个命令可以在分布式系统中用来实现分布式锁。基本思想是,当多个客户端试图获取同一资源时,它们都会尝试使用 SETNX 命令设置一个相同的键。只有第一个成功设置该键的客户端才能获取到锁,其他客户端获取不到锁

不能控制TTL、可能导致死锁、锁的公平性问题:因为setnx基于先到先得,可能造成其他线程饥饿

线程池的优势

线程池复用:线程池中的线程可以被复用,避免了频繁创建和销毁线程的开销,提高了应用程序的性能

提高响应速度:线程池中的线程通常是预先创建好的,当任务提交时,可以直接从线程池中获取线程来执行任务,减少了任务的响应时间。

简化编程模型:使用线程池可以简化多线程编程的复杂性

控制系统的资源使用:线程池可以设置最大线程数

支持异步执行

236、redission是什么

Redisson 是一个用于 Redis 的 Java 客户端,它不仅提供了对 Redis 命令的支持,还实现了许多高级功能,如分布式锁、分布式集合、分布式对象等

特点:分布式锁、消息队列、任务调度、丰富数据结构支持、连接池管理:自动管理连接池,提高性能和稳定性、

3

补一下双token、mybatis拦截器、手写分页、redis、MQ

237、为什么上传Excel时用到锁

可能会出现数据冲突或损坏的情况。为了避免这些问题,可以使用锁机制来控制对文件的访问

多线程或多进程环境中,用于确保对共享资源(如 Excel 文件)的安全访问,防止数据冲突

238、单例模式里的懒加载的DCL中volatile的作用是什么(保证线程的可见性但是不保证原子性)

双重检查锁定(Double-Checked Locking,简称 DCL)是一种在多线程环境下确保单例模式线程安全的技术。在 DCL 中,volatile 关键字起着至关重要的作用,确保了单例对象的正确性和可见性

239、IO流

主要分为字节流和字符流

字节流:

分为输入输出流

字节流用于处理二进制数据,如图片、音频文件等

字节输入流(InputStream)、字节输出流(OutputStream

常见的字节流

文件:

  • FileInputStream:从文件中读取字节。
  • FileOutputStream:向文件中写入字节。

字节数组:

  • ByteArrayInputStream:从字节数组中读取字节。
  • ByteArrayOutputStream:将字节写入到字节数组中。

字符流:

字符流用于处理文本数据,以字符(char)为单位进行读写操作

也分输入输出流:

  • Reader:抽象类,所有字符输入流的基类。
  • Writer:抽象类,所有字符输出流的基类

常见字符流

  • FileReader:从文件中读取字符。
  • FileWriter:向文件中写入字符。
  • CharArrayReader:从字符数组中读取字符。
  • CharArrayWriter:将字符写入到字符数组中。

240、Thread.join()Thread.yield()

Thread.join() 方法:其实就说join方法将挂起调用线程的执行,直到被调用的对象完成它的执行。。调用join()的线程会阻塞,会等待被调用join()的线程执行完

Thread.yield() 方法使当前线程让出 CPU 时间片,允许其他线程运行,他不会立马让当前线程停止,只是提示一下,由系统自动选择让步时间

Thread.wait():wait不会进入阻塞状态,会进入waiting状态

Thread.sleep():会进入time-waiting状态,等待他自动苏醒

241、为什么nginx性能高

C编写的

inpoll模型,天生支持高并发

工作模式是一个master进程多个工作进程

242、排查问题

skywalking、ELK

开发或者测试环境:Jconsole,VisualVM,线上环境:阿尔萨斯

243、你们基础镜像是定制的还是用的第三方的

定制的,openjdk放到私仓,skywalking客户端的aopjar包放到私仓,把阿尔萨斯放到私仓

244、怎么实现单点登录

单点登录:整个公司的所有体系跟应用从一个地方登录,比如阿里的所有产品,你阿里的所有产品在使用的时候,不管从哪登录都会跳到同一个域名进行登录,然后一次登录以后其他产品都可以不用登录

255、什么时候开启多线程?

调用多个后端业务应用,用countdownlatch开启线程池

发短信,邮件,消息通知,开启线程池,但是我们项目中用的MQ

提高消费者速度,不要求消息强有序性,可以开启线程池

被动使用tomcat的时候,默认开启十个线程池

256、线程池的生命周期

XXL-JOB还能做什么

257、黑名单太大了怎么办

黑名单太大:找到商品id最小的和最大的这个范围内的保留,范围外的删除并结合给黑名单商品设置24小时TTL

落库:订单相关的五张表,跟客户相关的有三张表,其余两个mq'异步处理的,这样做提高并发效率,抛出一个orderingok,用到分布式事务

绑定orderingok的队列:

1、会员中心扣除会员积分的队列

2、调库存,加销量

3、es中让消费者看到的那个销量要加

4、延迟队列,延迟十分,十分钟不支付自动取消订单然后去掉用取消订单接口,支付了就推到仓储系统

5、插入order_note

6、插入order_sale表

取消订单:传递一些订单的参数,还积分,还库存,还钱,订单状态改了改为已取消;保证幂等性

1保证接口幂等性,通过id查看订单状态是否取消过了

2还库存,调库存组的换库存接口;订单还积分可用会员中心的还积分,也要保证幂等性,避免重复还积分

3如果在线支付了调orderpay原路返钱

4插入ordernote表记录信息

5修改订单状态

258、怎样解决跨域问题?


使用@CrossOrigin注解:在资源上添加@CrossOrigin注解,设置单个资源的访问限制
使用全局跨域配置:实现WebMvcConfigurer接口,重写addCorsMappings方法,进行全局跨域配置
使用CorsFilter跨域:创建cros过滤器,设置响应头的配置(实现filter接口)
使用Nginx反向代理:设置响应头来支持跨域请求,并通过反向代理将请求转发到后端服务器

259、域名解析怎么实现的

DNS解析,每个机房都有自己的ip,但是ip我们不好记,为了有辨识度,通过DNS将发送的域名发送给DNS服务器,然后DNS服务器去自己的缓存中找对应的ip,先去对应区域找,找不到再向上一层找,比如某个区找不到就去某个市的DNS服务器,最后不行就到北京的服务器找了,浏览器根据DNS返回的ip去访问我们对应的ip

260、为什么用xxl-job提前预热,而不用synchorized加层锁来实现(两种方案都可以)

因为我们的项目现在都是分布式的,synchorized锁(双重检查)的是当前进程,只能锁单体应用,我们最多同时十几个应用访问数据库,每个数据库访问数据库单开一个线程,即便这样数据库访问压力也不大,等于说限制了我们访问数据库的次数,变向解决的缓存穿透击穿,当然两种方式都可以实现,最优解是xxl-job。

261、项目里哪里用到延迟队列,为什么用?

下单成功以后,我们通过延迟队列将订单放入延迟队列中,如果十分钟内支付了,我们就把订单推送到库存那边,如果没有支付就调用取消订单接口去还库存;

目的是:

给客户有足够时间支付

下单的时候我们冻结库存了,要及时还

262、nginx的优势

C语言编写

主从模式,主节点负责分发任务给从节点执行,而且只要主节点不停止,从节点即便关闭了也可以自动拉起

天生支持高并发

263、元空间的内存可以无限大,取决于物理机

264、订单下单成功推送到消息队列的过程,但是在这个过程中发生了网络波动,没有投递到队列中,怎么办?

没办法解决,这是MQ要做的事情,不是我们要做的

注意死信队列只能作用于消息到队列中,消费者迟迟不消费,我们可以把消息放入死信队列

另外保证消息可靠性生产者要持久化加上确认机制(发布者确认、发布者退回),消费者采用消息确认和死信队列

265、视图跟表的区别

视图是虚拟出来的表,而表是真实存在的。

前 言 1 1 概 述 2 1.1 选题背景 2 1.2 组织结构 2 2 所用相关技术和方法 3 2.1 工作流 3 2.1.1 什么叫工作流 3 2.1.2 工作流发展 3 2.1.3 工作流的优点 3 2.2 MVC工作模式 4 2.2.1 MVC设计思想 4 2.2.2 MVC的具体实现 5 2.2.3 MVC的不足 6 2.3 JSP技术介绍 6 2.3.1 JSP的运行原理 7 2.3.2 JSP的生命周期 8 2.3.3 Servlet和JavaBean技术介绍 8 2.3.4 Java 虚拟机 9 2.3.5 JSP访问SQL Server 2000数据库 9 2.4 数据库后台环境配置 10 2.5 系统开发工具简介 10 2.5.1 Dreamweaver 10 2.5.2 MyEclipse 10 2.5.3 Tomcat 11 2.5.4 SQL Server2000 11 2.5.5 chs_sql2ksp3 12 3 系统需求分析 13 3.1 系统功能分析 13 3.2 系统性能分析 13 3.3 系统方案的确定和评价 13 4 系统总体设计 15 4.1 系统层次模块图 15 4.1.1 营业厅模块 15 4.1.2 收费管理模块 16 4.2 系统数据流程图 16 4.3 数据表设计 18 5 详细设计及编码 21 5.1 编写JAVABEAN 21 5.2 营业厅实现函数 21 5.3 收费厅主要的实现函数 22 5.4 JAVABEAN主要实现模块 22 5.4.1 中文字符格式的转换模块(Stringto.java) 22 5.4.2 自动生成验证码(Ran.java) 22 5.4.3 数据库的连接(ConnectionFactory.java) 23 5.4.4 数据库连接的关闭(DatabaseUtils.java)--只提供接口 23 5.4.5 密码修改模块(Common_fuction.java) 24 5.4.6 时间格式转换(timeBean.java) 24 5.4.7 数据统计(counthander.java) 25 5.4.8 营业厅的接口(luruaction.java) 27 5.4.9 营业厅的主要函数实现(luruhander.java) 28 5.4.10 收费厅的主要函数接口 32 5.5 管理员登陆模块 33 5.5.1 管理员登录 33 5.6 营业厅管理模块 36 5.6.1 Left.jsp页面 36 5.6.2 Work.jsp 40 5.6.3 customerlistinfo.jsp 41 5.6.4 allinfo.jsp 41 5.7 收费厅管理模块 42 5.7.1 Left.jsp 42 5.7.2 Work.jsp 43 5.7.3 Customerlistinfo.jsp 43 5.7.4 gongdan.jsp 43 6 系统测试与维护 45 6.1 测试目的 45 6.2 测试环境 45 6.3 系统测试 45 6.4 系统维护 45 7 开发难点与技术 46 7.1 主要程序实现的代码描述 46 7.1.1 验证码的自动生成 46 7.1.2 生成WORD工单 46 7.1.3 以一定的时间刷新页面 47 7.1.4 JSP中文问题的解决 47 7.2 在程序编码过程遇到的主要问题: 48 7.3 代码编写风格 49 7.4 我的不足: 49 结束语 50 致 谢 50
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值