2025年面试经历
有时候我也不知道自己做的是不是对的,现在看不出来,以后也不知道,可能某天之后恍然大悟
不要浪费太多时间在基础的复习上,要尽快过一过,在面试题里巩固这些基础
8月总结
数据库三大范式:
所有字段具有原子性不可拆分的,
主键对每一行具有唯一约束
非主键属性不对其他表非主键属性具有外键约束。面向对象和面向过程
面向过程就是一步一步去实现某个任务
面向对象就是描述清楚某个事物,原先的任务就是每一个事物的方法hashMap的存取原理
hashMap的默认长度是16,负载因子是0.75,他不是一开始就初始化的,在首次put时初始化的,put首先通过key计算hash值,除以15取得存储位置,
如果位置上没有数据,则直接存储,有数据的话,要判断hash值是否相同,不同的话存储在链表,如果相同则覆盖,当链表长度大于8,则会形成红黑树
取数据用get(key)的方式,会比较hashCode和key线程状态创建(new)
创建对象时就进入创建状态
就绪
线程使用start方法
运行
线程经过调度到达运行状态
阻塞
线程调用sleep方法,进入就绪状态,等阻塞事件接触后,重新进入就绪状态等待cpu调度执行
死亡
线程中止、或者结束,就会进入死亡状态
yield
礼让线程,让当前处于执行的线程停止,但不阻塞 ,转为就绪状态 让cpu冲洗你调度,但是不一定成功
join
join 合并线程,待此线程执行完成,才能执行其他线程,其他线程阻塞(包括主线程)
线程优先级
java 提供线程调度器来监听程序中启动后进入就绪状态的所有线程,线程调度器根据优先级决定应该调度那个线程先执行
线程优先级用数字表示,范围1~10Thread.MIN_PRIORITY =1Thread.MAX_PRIORITY =10Thread.NORM_PRIORITY =5
获取或者改变使用getPriority setPriority(int XXX)线程分为用户线程和守护线程,线程必须保证用户线程完全执行,不用等待守护线程执行完成,守护线程可以用于
后台记录操作日志、监控内存、垃圾回收等
setDaemontransient类里面不想被序列化的变量
死锁:多个线程互相占用一些共享资源,并且互相等待别的进程释放资源才能够继续运行
Lock和synchronized
Lock是显式锁 需要手动的开启或者关闭,synchronized是隐式锁,出了作用域就会自动释放
Lock效率更高
Lock只能锁代码块,而synchronized 可以锁代码块和方法
管城法
消费者和生产者通过缓冲区进行资源的更新,通过object的wait和notify方法实现
信号灯法
通过一个标志位,判断当前是否可以消费或者生产
线程池
可以避免线程频繁的创建和销毁,实现重复利用,提高响应速度、便于线程管理、降低资源的消耗。
HashSet
1、先计算hash值,然后再比较内容,如果哈希值相同,在比较内容,如果不同存,相同则去重复。
HashMap LinkedHashMap TreeMap
1、key唯一,value可重复 1、key唯一,value可重复 1、可以对key进行排序
2、无序 2、有序 2、无索引
3、无索引 3、无索引 3、key唯一
4、线程不安全 4、线程不安全 4、线程不安全
5可以存null键null值 5、可以存null建null值 5、不能存null
数据结构:哈希表 6、是hashMap的子类 数据结构是红黑树数据结构:哈希表+双向链表ArrayList linkedList
1、有序 1、有序
2、可重复 2、可重复
3、有索引 3、有索引
4、线程不安全 4、线程不安全
数据机构为数组 数据机构为双向列表HashSet LinkedHashSet TreeSet
1、无序 1、有序 1、有序
2、值唯一 2、值唯一 2、无索引
3、无索引 3、无索引 3、线程不安全
4、线程不安全 4、线程不安全 4、不能存null
数据结构为哈希表 数据结构为哈希表加双向链表 5、数据唯一数据结构:红黑树Hashtable Vector
1、key唯一 1、有序
2、线程安全 2、有索引
3、无索引 3、可重复
4、不能存null键null值 4、线程安全
5、无序 数据机构是列表
数据结构是哈希表jvm
类加载器加载顺序
1、类加载器收到加载类的请求,
2、将这个请求向上委托给父类,一直向上委托,直到根加载器
3、启动加载器检查是否能够加载当前这个类,能解在就结束,不能的话会报错,通知子类加载
4、重复3
null:java调用不到,或者不存在凡是带了native 关键字的,说明Java的作用范围达不到了,回去调C语言的库
会进入本地方法栈,调用本地方法接口 jni:扩展java的使用,融合不同的编程语言为java所用
他在内存区域开辟了一块标记区域, 本地方法栈登记native方法,执行时通过jni调用本地方法库的方法
使用场景:java程序驱动打印机、管理系统程序计数器
每个线程都有一个程序计数器,他是线程私有的,就是一个指针,在执行引擎读取吓一跳命令方法区
方法区被所有线程共享,所有的字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,
简单点说,所有的定义的方法的信息都保存在该区域,此区域为共享区间
静态变量、常量、类信息、常量池栈:
先进后出,栈内存主管程序的运行,生命周期和线程同步
线程结束、栈内存就会释放,
存的是8大基本类型、对象引用、实例的方法
堆
Heap 一个jvm只会有一个堆内存,堆的大小是可以调节的
类加载器读取了类文件后,一般会把类、方法、常量、变量、保存我们所有引用对象的真实对象新生区
类:诞生和成长的地方,甚至死亡
所有对象都是在伊甸园区new出来的
幸存者区老年区永久区
这个区域常驻内存的。用于存放JDK自身携带的Class对象。interface元数据。存储的是java运行时的
一些环境或者类信息,这个区域不存在垃圾回收,关闭VM虚拟机就会释放这个区域的内存。
什么时候堆溢出:一个启动类加载了太多的第三方jar包,Tomcat部署了太多的服务。大量动态生成的
反射类。如果新生区满了则会进行轻GC,活下来的会在幸存区来回跳转,到一定程度时会进入老年区,老年区也满的时候
会进行重GC
堆内存调优
引用计数法
给所有对象分配一个计数器,计数器本身也会有消耗,计数为0的就会被清除掉GC算法:复制算法>标记清除法>标记压缩算法
内存效率:复制算法>标记清除法>标记压缩算法
内存整齐度:复制算法=标记压缩算法>标记清除法
内存利用率:标记压缩算法=标记清除法>复制算法年轻代:存活率低 复制算法!!
老年代:区域大,存活率高 标记清除(内存碎片不是太多)+标记压缩混合 实现分代收集算法新生区频繁GC 采取复制算法老年区:标记清楚法、标记压缩法 混合使用JMM
就是定义了主内存和线程的变量同步的问题
volatile用于修饰变量,主要是解决多线程环境下的可见性,或者有序性问题,
当一个线程修改改了volatile修饰的值,就会立马同步到主线程。Spring
是一个免费的轻量级、非入侵式的开源框架,核心是IOC、AOP,它支持事务的处理、对框架整合的支持。过度利用反射、影响效率简单点说,他就是一个轻量级的IOC、面向切面开发的框架!IOC:本来对象的创建是程序来控制的,而且service->impl->dao,耦合性高,现在是由容器控制的,我们想要对象只需要在容器中获取就可以了DI依赖注入方式:
构造器注入、set方法注入、命名空间注入
作用域
单例、原型、session、request 、global session
自动装配
xml、注解@component @configurationaop实现方式
xml、@aspectjs
弱类型脚本语言,不需要编译,控制网页的行为
变量类型、方法、操作domSpringMVC是一种基于java实现MVC的轻量级Web框架
MVC是一种架构模式,Model保存业务的逻辑,保存数据的状态;View就是视图展示
controller 控制层:取得表单数据,调用Model层,然后转向指定的页面mybatis
用于sql查询的持久层框架,优化掉重复性的jdbc代码,支持动态sql,可动态生成映射关系。#{}和${}的区别
#{}为预处理,¥{}是字符串替换,可防止sql注入rowBounds
PageHelper.startPage(currPage,pageSize);用到构造模式、工厂模式、单例模式、nginx
Nginx是一个高性能的http和反向代理web服务器,
负载均衡、反向代理springboot
是一个轻量级的开发框架,为了解决企业级开发的复杂性而创建的。优点
开箱即用,内嵌了tomcat,没有冗余代码生成、不需要配置xml了!!!
@SpringbootApplication主配置类
点进去会发现他是一个组合注解
@componentscan
自动扫描并加载符合条件的组件或者bean,并加载到ioc容器中
@EnableAutoConfiguration
开启自动配置,
@SpringbootConfiguration@Import 加载文件里所有的自动配置类,加载到容器里@AutoConfigurationPackge 把所有这个注解下的配置加载到容器里springCloud
因为服务的并发量上来了,导致单个的springboot已经不能够满足我们的需求,我们把一个模块拆分成多个模块,
基于Springboot,提供了一套微服务解决方案,包括了服务的注册与发现、服务网关、负载均衡、熔断器C强一致性 A可用性 P 分区容错性zookeeper cp和eureka ap区别ribbon负载均衡
连接所有的机器,根据算法判断去调用谁
9月总结
minio的关键操作:
默认配置:minio的地址、账号、密码、默认试桶
上传:
1、首先判断文件、桶名称是否为空
2、为了防止文件名称重复导致被覆盖,可以文件名拼一个uuid
3、为了防止text中文乱码,在content-type上拼接一个字符类型等于utf-8
4、然后通过minio的上传方法,将构造上传对象传递(字段有桶名、文件名、文件大小、文本类型)
5、通过minio获取路径的方法,将桶、文件名作为参数,获取完成的下载地址下载:
1、首先判断文件、桶名称是否为空
2、通过构造请求参数,使用minioclient的下载方法,可以获取一个输入流对象
3、通过httpResopnse返回分布式锁:加锁通过redis的setnx命令,value设置成uuid,保证是当前服务解锁。配置过期时间,防止服务宕机导致锁不释放
删锁通过脚本,保证原子性操作,让防止因为过期而把别人的锁给删除掉了redisson
可重入锁:允许一个线程再已经获取锁的情况下,可以再次过去锁。Rlock lock =redisson.getLock("MY-LOCK");
1、有一个看门狗机制锁的自动续期,如果业务超长,运行期间定时任务每隔1/3(也就是10秒)会自动续期30秒,不会因为业务过长而导致锁的过期。
2、枷锁后,就算因为业务宕机,30秒后也会自动调用解锁//自动解锁时间一定要大于业务执行时间RleadWriterLock lock=redisson.getReadWriteLock("ew-lock");
Rlock rlock=lock.writeLock();
rLock.lock();
rLock.unlock();RLock rLock =lock.readLock();
rLock.unlock();
读写锁:必须等待写入后,才能读;保证一定能够读取到最新数据
读+写:有读锁。写也需要等待
读+读:不加锁
写+读:需要等待释放信号量:通过redis获取默认量, redisson.getSemaphone acquire和release tryacquire 尝试一下可不可以
闭锁:
RCountDownLatch door=redisson.getCountDownLatch("door");
door.trySetCount(5);
door.await();
door.countDown();缓存一致性: 双写、失效SpringCacheCacheManager Cache
@Cacheable
代表当前方法的结果需要缓存,如果缓存中有,方法不用调用。
如果缓存中没有,会调用方法,放入缓存。
默认行为
1)如果缓存里有,方法不用调用。
2)key默认自动生成,缓存的名字::simplekey[] (自主生成的值)
3)缓存中的value默认使用jdk序列化机制,将序列化的数据存到redis
4)默认ttl时间为-1
pms3.0技术总结:
定期巡视启动定时任务线程时,因为要考虑到后面多镜像的问题,这里使用了分布式锁,尝试tryLock 可以不可以,
可以则放到当前实例的map中。通过applicationRunner的run方法,等cron表达式后执行。开启、关闭、重启时,
然后发Kafka消息,接收到消息后,oa:
face++:步骤是先创建人脸库,可以自己传一个out_id,或者用自动生成的faceset_Token,
然后往人脸库添加人脸,添加人脸前要先检测人脸,人脸检测通过之后有一个faceToken,然后人脸库添加人脸。
人脸比对时就可以通过user_id拿到facetoken,将用于比对的照片提取出base64数据.activity工作流:
发起流程时,通过流程key,创建流程实例,获取申请节点
工作流 执行顺序
1、设计模型,流程部署后生成流程定义表、流程配置表\BPMN
2、流程配置时,操作每个节点,设置是否抄送、会签等等
3、启动流程时,创建流程信息,携带参数,然后通过任务,找下一个节点的执行人,然后把创建进候选人表。
4、查询待处理的,就是在候选人里关联,然后查到任务、关联到流程,已完成就是查看历史任务记录
5、版本升级后,原有为执行的会继续走,如果想要升级,则需要进行迁移
6、配置抄送人员后,一般会在任务完成时,把抄送人员列表添加进去
7、会签则表示要选择的所有人都认同了jdk8和jdk11的区别
8:引入了lamda表达式
11:引入了var,可以不必声明具体的类型声明,对String的判空、去除首尾空字符
17:Records记录类自动生成equals()、hashCode()、toString()和 getter:
instanceof实现类型判断 + 转换的流程,无需显式强制转换:
还可以声明一个类能被那些类继承
21:虚拟线程创建数百万级线程而不耗尽系统资源,大幅简化高并发编程:在使用 MyBatis-Plus 进行更新操作时,如果字段为 null,你希望数据库中的对应字段也被更新为 NULL,而不是被忽略。@TableField(updateStrategy = FieldStrategy.ALWAYS)mybatis-plus:global-config:db-config:update-strategy: ignored # 不建议使用,会影响所有更新操作加密分为可逆的:手机号、身份证号、银行卡号。
和不可逆的单点登录:redis实现
jwt分为三部:header用来保存加密的算法 payload 用来存储用户信息 signature:保证jwt的安全性,比如有没有被破坏窗口函数:排名函数、聚合函数、偏移函数、分布窗口函数5.7和8.0的区别。
8.0开始支持:完全要用innodb,支持窗口函数,默认使用utfmb4 可以防止乱码
5.7部分还在用myiSam,已经停止维护了dm和MySQL的区别:
dm:默认是区分大小写,有自己的数据库管理、迁移工具,一般在国企、事业单位用的比较多,在保密上做的比较好、函数上有所不同
mysql:默认是不区分大小写,通过navicat当数据量太大时,将所有数据存储在内存中的 Map 会带来几个严重问题:
内存溢出 (OutOfMemoryError):最直接的问题,导致程序崩溃。
垃圾回收 (GC) 压力大:即使没溢出,庞大的堆内存也会导致 GC 停顿时间变长,程序性能急剧下降。
查询效率下降:虽然 HashMap 的 get/put 是 O(1),但当桶变得很大时,冲突会增加,性能会退化。内存泄漏 Memory Leak 和内存溢出 OutOfMemoryError:
内存泄漏是指程序在申请内存后,无法释放已经不再使用的对象的内存空间。 数据库连接、网络连接、文件流 未关闭 静态变量的生命周期与程序一致,这些对象永远不会被回收。
内存溢出是程序申请内存时,无法提供足够的内存供其使用 内存泄漏导致、内存设置太小/需求太大
gc回收之前,要先确定哪些活着,哪些已经死了。引用技术算法:
给对象一个引用计数器,被引用+1,引用失效就减一
标记清楚法:
复制算法:把还存活的对象放到另一块//破坏双亲委派机制
通过继承ClassLoader类并重写loadClass方法,可以创建一个自定义的类加载器。在这个方法中,可以忽略双亲委派机制,直接从文件系统或者网络加载类。
使用线程上下文类加载器: 在某些情况下,可以通过设置线程的上下文类加载器来改变类加载的行为。通过设置不同的类加载器,可以加载同名但不同的类。线程安全问题:
线程安全问题指的是当多个线程同时访问(特别是修改)同一个共享资源(如变量、对象、文件、数据库条目等)时,由于执行顺序的不确定性(CPU时间片的随机调度),
导致程序的最终结果变得不可预测、不正确或与单线程环境下的预期结果不一致的情况。生线程安全问题的三个必要条件
共享资源(Shared Resource):存在多个线程都能访问到的数据或资源。
修改操作(Modification):至少有一个线程在对这个资源进行写(修改)操作。如果所有线程都只是读,不会有问题。
缺乏同步机制(Lack of Synchronization):没有使用正确的机制(如锁)来协调多个线程对共享资源的访问顺序。直接调用 run() 方法并没有创建新的线程,它只是在当前线程中执行了一个普通的方法调用。
而调用 start() 方法,才是真正地启动了一个新的线程,并由这个新线程去异步执行 run() 方法中的代码。
以上就是我这两个月背过的所有面试题了,今天是第一天入职,说实话不是很舒服,中午休息的时间太短了,好累