秒杀流程

1、提前预热

2、不走购物车

3、xxljob(调用商品接口、提前预热、调用库存接口)

介绍一下IOC和AOP

ioc具有依赖注入的容器,负责管理对象的实例化,通常需要主动去new对象,现在将new对象的过程权限交给ioc容器来处理.这就实现了控制反转

(将对象的生命周期将给bean容器管理,实现代码的解耦和灵活性,通过依赖注入实现) 

aop 业务交叉,在不改变原来代码的基础上实现对业务的增强(面向切面编程)

谈一下对高并发的理解,平时怎么处理高并发问题

高并发是指系统在同一时间内处理大量请求。

1、使用缓存技术 redis

2、数据库优化

3、负载均衡,使用负载均衡技术分发到不同的服务器,避免单个服务器过载

4、限流与熔断

@Autowired @Resource的区别

1. @Autowired:是Spring框架提供的注解,通过类型匹配的方式进行依赖注入。它可以用于属性、构造方法、Setter方法和其他自定义方法上。当有多个匹配的依赖项时,可使@Qualifier注解指定具体的依赖项。@Autowired注解默认是按照byType方式进行注入,如果找不到匹配的依赖项,会抛出异常。

2. @Resource:是Java EE提供的注解,通过名称匹配的方式进行依赖注入。它可以用于属性、Setter方法和其他自定义方法上。@Resource注解默认是按照byName方式进行注入,如果找不到匹配的依赖项,会尝试按照byType方式进行注入。可以通过@Resource(name = "beanName")注解指定具体的依赖项。

什么是java反射

Java反射机制是指在运行时,通过获取类的信息并操作类或对象的属性、方法、构造函数等。它允许程序在运行时动态地加载和使用,而不需要在编译时就确定这些类的具体信息。(可能会带来性能上的开销)

反射机制运用的场景

我们在实际开发中很少用到反射,但spring使用反射机制来实现依赖注入、自动装配、AOP(面向切面编程)动态代理等功能。当然了还有数据库jdbc的连接

  1. 框架和库开发:很多框架和库使用反射来实现插件系统,使用户能够动态加载和扩展功能。例如,Spring框架使用反射来管理和初始化Bean对象

  2. 序列化和反序列化:对象的序列化和反序列化需要访问对象的私有字段和方法,反射可用于实现这些操作。Java中的ObjectOutputStreamObjectInputStream就使用了反射

  3. 注解处理器:许多注解处理工具使用反射来扫描和分析源代码中的注解,然后生成相关的代码。例如,Java的注解处理器可用于生成代码,从而减少样板代码的编写

  4. 单元测试和调试工具:测试框架和调试工具可以使用反射来检查和修改类的状态,以便进行单元测试或诊断问题。

  5. JavaBean 操作:在图形用户界面(GUI)开发中,反射可用于自动地设置和获取JavaBean的属性,使用户界面与数据模型之间的绑定更加灵活。

  6. 反射也可以用于实现一些高级的编程技巧,如动态代理、AOP(面向切面编程)和依赖注入,用于实现日志记录、性能监控、事务管理等功能

Mybatis的一二级缓存

1. 一级缓存:一级缓存是MyBatis默认开启的缓存,它是指在同一个SqlSession中,对于相同的查询语句和参数,第一次查询会将结果缓存到内存中,后续相同的查询会直接从缓存中获取结果,避免了重复查询数据库。一级缓存的作用域SqlSession级别的,当SqlSession关闭或清除缓存时,一级缓存也会被清除。

2. 二级缓存:二级缓存是在SqlSessionFactory级别上的缓存,它可以被多个SqlSession共享。当多个SqlSession执行相同的查询时,查询结果会被缓存到二级缓存中。当其他SqlSession执行相同的查询时,会先从二级缓存中获取结果,而不是直接查询数据库。二级缓存的作用范围Mapper级别的,可以通过配置开启或关闭二级缓存。

为什么要用动态sql

为了满足实际开发中,能够根据用户随时更改的业务需求,灵活地对数据进行增删改操作,尽可能减少对代码的更改,及时完成要求。

Spring Boot 自动装载原理

首先SpringBoot要想启动,就必须要加一个复合注解@SpringBootApplication,

在这个注解里面有三个注解,而自动装配就是通过@EnableAutoConfiguration注解里的EnableAutoConfigurationImportSelector.class来实现的,

并给你指定路径要去METAINF下的spring.factories文件找动配置类

java是值传递还是引用传递?

基本类型作为参数被传递时肯定是值传递;

引用类型作为参数被传递时也是值传递,

只不过这个值实际上是对象内存地址的引用

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

常用的sql优化

1、避免使用select *

2、分页优化limit 避免一次性返回过多数据,可进行分页,对于一百万数据取最后20条,我们可查用 select id,name,age from user where id>1000000 limit 20

3、使用union all 代理 union union会进行去重

4、避免使用or 使用in

5、使用join优化 避免使用子查询

6、优化like语句

如何防止sql注入

#占位符可以防止SQL注入攻击,因为参数值会被预编译,不会将参数值直接拼接到SQL语句中。

$占位符不会对参数进行预编译,参数值会直接拼接到SQL语句中,所以需要注意SQL注入攻击的风险。

Hsah碰撞是什么?如何解决

hash碰撞指的是,两个不同的值(比如张三、李四的学号)经过hash计算后,得到的hash值相同,导致冲突。

解决方式:

1、开放地址法 如果hash值相同,回向右边继续寻找,找到null位置,然后放进去

2、拉链法 通过hash算法如果有值,则将对象放到该链的链表上

3、再哈希法 通过hash值算法,如果有值则再进行hash算法算一次,直到能存放为止

4、公共溢出区 如果发生碰撞,则将它放在单独的区域,查找的时候没找到就去单独区找

Spring是如何解决循环依赖的

Spring框架使用一种特殊的机制来解决循环依赖问题,这个机制被称为"提前暴露(early exposure)"或"三级缓存"。

1.Spring首先从一级缓存singletonObjects中获取。

2.如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取。

3.如果还是获取不到且允许singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()(三级缓存)获取.

4.如果从三级缓存中获取到就从singletonFactories中移除,并放入earlySingletonObjects中。其实也就是从三级缓存移动到了二级缓存。

这机制使得Spring能够在发生循环依赖时仍然成功实例化bean,并确保它们在正确的时间点初始化。下面是Spring解决循环依赖的基本原理:

  1. Bean定义阶段:当Spring容器初始化时,它首先解析所有bean的定义,包括它们的依赖关系。

  2. 实例化Bean对象:在实例化bean时,Spring会创建一个包装对象(proxy)作为bean的代理,而不是直接创建实际的bean对象。这个代理对象用来处理循环依赖。

  3. 注册Bean对象:Spring容器会将正在创建的bean对象放入三级缓存中,分为三个阶段:未暴露、已暴露和已完成。一开始,bean对象被标记为"未暴露"。

  4. 依赖注入:Spring继续实例化其他bean,并进行依赖注入。当遇到循环依赖的情况时,代理对象会返回一个尚未初始化的bean代理,以满足依赖关系。

  5. 初始化Bean:在后续的阶段,Spring容器会递归地初始化依赖的bean。当初始化完成后,bean会被标记为"已完成"。

  6. 暴露Bean:在合适的时机,Spring会将bean标记为"已暴露",这表示该bean已准备好供其他bean引用。

通过这个机制,Spring能够成功处理循环依赖,确保每个bean在被使用之前都已经初始化完成。这减少了循环依赖导致的问题,同时也提高了性能,因为不需要频繁地创建和销毁bean对象。

Spring解决循环依赖的方法有点像"你帮我,我帮你"的情况。当两个类彼此互相需要并创建对方的实例时,Spring采用以下步骤来解决这个问题:

  1. 首先,Spring创建两个类的代理(类似于替身)对象,而不是直接创建它们的实际对象。这些代理对象充当了缓冲的角色。

  2. 接着,Spring会先创建一个类的代理对象,并将它注入到另一个类中,以满足它的依赖。

  3. 这时,第一个类的代理对象已经被创建,但它并没有真正的初始化。Spring会继续创建第二个类的代理对象,并将它注入到第一个类中。

  4. 现在,第二个类的代理对象也已经被创建,但同样尚未真正初始化。这时,Spring会开始初始化这两个代理对象,确保它们能够正常工作。

通过这种方式,Spring在互相依赖的类之间建立了一个"先创建代理,后初始化"的机制,避免了循环依赖问题。最终,这两个类都能够正常工作,而不会陷入无限循环的初始化中。

List Map Set

List适用于需要有序、允许重复元素的场景;

Map适用于需要通过键快速查找值的场景,键唯一

Set适用于不允许重复元素的场景。

将Bean放入Spring容器中的方式

1、@Configuration+ @Bean 在配置类中引入第三方bean

2、使用注解 @Compoent 然后在注解类上@ComponentScan进行路径配置扫描

3、@Import直接导入类

4、通过xml的配置

如果客户端禁用cookie, session还能用吗

如果浏览器禁用了 cookie,浏览器请求服务器无法携带 sessionid,服务器无法识别请求中的用户身份,session失效。

解决方案:

方案1.通过url重写,把 sessionid 作为参数追加的原 url 中,后续的浏览器与服务器交互中携带 sessionid 参数。

方案2.服务器的返回数据中包含 sessionid,浏览器发送请求时,携带 sessionid 参数。

方案3.通过 Http 协议其他 header 字段,服务器每次返回时设置该 header 字段信息,浏览器中 js 读取该 header 字段,请求服务器时,js设置携带该 header 字段。

Redis数据结构及应用场景

1、字符串(String):存储字符串、整数或浮点数等类型的值。计数器

2、列表(List):按照插入顺序存储一组字符串

3、集合(Set):无序存储一组字符串,支持集合运算。交集并集

4、有序集合(ZSet):有序存储一组字符串,每个字符串关联一个分数,支持按照分数范围查询和排名查询。

5、哈希表(Hash):存储键值对,其中健和值都是字符串类型。

应用场景:

1、缓存:Redis可以作为缓存服务器,将常用的数据缓存在内存中,提高访问速度。

2、计数器:使用Redis的原子操作实现计数器功能,如网站访问量统计、商品销量统计等。

3、分布式锁:使用Redis的setnx命令实现分布式锁功能,在分环境下保证同一时刻只有一个客户端能够执行某个操作。

4、发布/订阅系统:Redis支持发布/订阅模式,在消息发布者和消息订阅者之间建立通信渠道,实现实时消息推送功能。MQ

5、排行榜系统:使用Redis的有序集合实现排行榜功能,根据分数排序并返回排名前N的数据。

6、地理位置服务:使用Redis的地理位置功能,存储地理位置信息,并支持查询附近的位置信息。

Java 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()方法可以用于计算一组元素的平均值。

Stream流中的方法

Stream提供了大量的方法进行聚集操作,这些方法既可以是“中间的”,也可以是“末端的”。

中间方法:中间操作允许流保持打开状态,并允许直接调用后续方法。上面程序中的map()方法就是中间方法。中间方法的返回值是另外一个流。

末端方法:末端方法是对流的最终操作。当对某个Stream执行末端方法后,该流将会被“消耗”且不再可用。上面程序中的sum()、count()、average()等方法都是末端方法。

除此之外,关于流的方法还有如下两个特征:

有状态的方法:这种方法会给流增加一些新的属性,比如元素的唯一性、元素的最大数量、保证元素以排序的方式被处理等。有状态的方法往往需要更大的性能开销。

短路方法:短路方法可以尽早结束对流的操作,不必检查所有的元素。

下面简单介绍一下Stream常用的中间方法:

filter(Predicate predicate):过滤Stream中所有不符合predicate的元素。

mapToXxx(ToXxxFunction mapper):使用ToXxxFunction对流中的元素执行一对一的转换,该方法返回的新流中包含了ToXxxFunction转换生成的所有元素。

peek(Consumer action):依次对每个元素执行一些操作,该方法返回的流与原有流包含相同的元素。该方法主要用于调试。

distinct():该方法用于排序流中所有重复的元素(判断元素重复的标准是使用equals()比较返回true)。这是一个有状态的方法。

sorted():该方法用于保证流中的元素在后续的访问中处于有序状态。这是一个有状态的方法。

limit(long maxSize):该方法用于保证对该流的后续访问中最大允许访问的元素个数。这是一个有状态的、短路方法。

下面简单介绍一下Stream常用的末端方法:

forEach(Consumer action):遍历流中所有元素,对每个元素执行action。

toArray():将流中所有元素转换为一个数组。

reduce():该方法有三个重载的版本,都用于通过某种操作来合并流中的元素。

min():返回流中所有元素的最小值。

max():返回流中所有元素的最大值。

count():返回流中所有元素的数量。

anyMatch(Predicate predicate):判断流中是否至少包含一个元素符合Predicate条件。

noneMatch(Predicate predicate):判断流中是否所有元素都不符合Predicate条件。

findFirst():返回流中的第一个元素。

findAny():返回流中的任意一个元素。

除此之外,Java 8允许使用流式API来操作集合,Collection接口提供了一个stream()默认方法,该方法可返回该集合对应的流,接下来即可通过流式API来操作集合元素。由于Stream可以对集合元素进行整体的聚集操作,因此Stream极大地丰富了集合的功能。

Java 8新增了Stream、IntStream、LongStream、DoubleStream等流式API,这些API代表多个支持串行和并行聚集操作的元素。上面4个接口中,Stream是一个通用的流接口,而IntStream、LongStream、DoubleStream则代表元素类型为int、long、double的流。

Java 8还为上面每个流式API提供了对应的Builder,例如Stream.Builder、IntStream.Builder、LongStream.Builder、DoubleStream.Builder,开发者可以通过这些Builder来创建对应的流。

独立使用Stream的步骤如下:

1、使用Stream或XxxStream的builder()类方法创建该Stream对应的Builder。

2、重复调用Builder的add()方法向该流中添加多个元素。

3、调用Builder的build()方法获取对应的Stream。

4、调用Stream的聚集方法。

在上面4个步骤中,第4步可以根据具体需求来调用不同的方法,Stream提供了大量的聚集方法供用户调用,具体可参考Stream或XxxStream的API文档。对于大部分聚集方法而言,每个Stream只能执行一次。

类加载的过程

1. 加载:通过类加载器(ClassLoader)查找并加载类的字节码文件。类加载器可以是系统类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)或应用类加载器(Application ClassLoader)等。类加载器根据类的全限定名(包名+类名)定位类的字节码文件,并读取到内存中。

2. 验证:对加载的字节码文件进行验证,确保其符合Java虚拟机规范。验证的内容包括语法验证、语义验证、字节码验证等。

3. 准备:为类的静态变量分配内存,并设置默认初始值。这些变量会在类的初始化阶段赋予正确的初始值。

4. 解析:将类中的符号引用(如方法、字段的符号引用)解析为直接引用(如方法、字段在内存中的地址)。解析过程可以在加载阶段之后进行,也可以在初始化阶段之后进行。

5. 初始化:对类进行初始化,即执行类的初始化方法(<clinit>)。类的初始化过程包括静态变量的赋值、静态代码块的执行等。初始化是类加载的最后一步,类加载过程中的其他步骤都是为了保证类的正确加载和初始化。

红黑树的时间复杂度为O(log n)

数组的时间复杂度为O(1)。

对于单向链表,插入和删除操作的时间复杂度为O(1),搜索操作,需要遍历链表,时间复杂度为O(n)

对于双向链表,插入和删除操作的时间复杂度同样为O(1),但搜索操作可以通过双向遍历进行优化,平均时间复杂度为O(n/2)

Logo

GitCode AI社区是一款由 GitCode 团队打造的智能助手,AI大模型社区、提供国内外头部大模型及数据集服务。

更多推荐