面试题总结5
讲一下 Java 的集合
从两方面去讲,单例集合、双列集合,单列集合顶层接口 Collection,下面的子接口 Set、List、Queue,最常用的集合实现类 ArrayList、LinkedList。这两个的对比:前者动态数组、能随机查找、时间复杂度 O1、扩容机制、创建时默认大小为 10 但不会分配内存空间第一个元素插入后才会实际分配内存空间、方法 add、set、get、remove,后者双向链表、顺序查找、时间复杂度 On、方法 add、getFirst、getLast、remove;双列集合顶层接口 Map,最常用的实现就是 HashMap,又可分为 1.7 和 1.8 来讲:1.7 使用数组+链表实现,数组存储哈希桶,链表存元素;数组大小为 2 的倍数原因内部使用取模运算也叫位运算 2 倍方便计算,方法:get、put、remove;1.8 使用数组+链表+红黑树实现,防止链过长导致的查询缓慢问题。插入元素后检查链表长度是否大于 8,是则先扩容数组,然后再讲链表树化成红黑树。方法:put、resize、get。
HashMap 的线程安全吗?怎么实现线程安全
ConcurrentHashMap 线程安全的容器。包括 1.5 到 1.7,使用的是分段锁,里面创建了大小固定为 16 的 Segment 数组 锁住每个槽实现线程安全、初始化时只创建 [0] 其余还是 null,每个段中的数组默认大小为 2 只有插入到第二个元素后才会扩容,缺点并发度低。1.8 使用 CAS 插入元素和 Synchronized 扩容实现线程安全。
异常处理讲一下,自定义异常
异常顶级接口 Throwable、两个实现异常 Exeception、错误 Error,异常通常分为两大类运行时异常和非运行时异常,运行时异常:内存溢出、数组越界、空指针,出现异常两种解决方案 1.声明式解决 throws 交给异常类去处理 2.手动解决 throw 配合 try catch 捕获异常抛出。底层是有一个异常表记录异常信息。自定义异常就是自己手动继承 Exeception 在里面重写方法。
SpringMVC 的注解有哪些
从启动类开始:启动类注解、开启配置注解、控制层、自动注入、类型注入、配置类、自定义 bean、业务层、持久层、事务、接收参数注解。
spring 事务的原理,事务传播机制
spring 事务底层利用的就是数据库事务和代理来实现的,首先对于使用了事务注解的 bena 会先创建他的一个代理对象,当调用代理对象上的方法时先判断是否有事务注解,如果有则修改数据库的 autocommit 为 false禁止事务自动提交,接着执行方法如果没有异常事务提交,出现异常则看又没有捕获,如果没有捕获则正常通过事务的传播机制去回滚事务,如果自己捕获异常了则事务提交导致失效。事务的传播行为目的是统一管理多个事务方法的嵌套调用开启事务回滚事务。
git 的操作命令
初始化仓库 git init、克隆仓库 git clone、创建分支 git branch、合并分支 git merge、提交 git commit。
Spring 的启动流程
首先从启动类开始 main 方法 、run 方法执行,创建应用上下文 ApplicationContext,管理 Bean 的生命周期、依赖注入、配置加载,组件扫描配置加载配置、定义注册 Bean、初始化 Bean、启动服务器监听端口
IOC 和 AOP 说一下,IOC 我想要多例 bean 怎么办
IOC 是 Spring 中的一种设计思想,内部利用工厂模式和反射机制实现。在原始的场景下想要使用某个类的方法就要自己 new 对象,这种实现非常不优雅后面 IOC 就取代了这个步骤,我们的组件的 bean 交给 IOC 容器去管理,当想使用的时候直接从容器中给他注入 就好了。AOP 面向切面编程,通过外部的实现在不修改原方法的情况下对方法进行增强,内部是基于动态代理实现的。AOP 内部有一些方法:切面、切入点、通知。然后 IOC 和 AOP 是没有依赖关系的,例如说没有交给 IOC 管理的 Bean 也是可以被 AOP 增强的,因为动态代理只需要拿到对象即可;想要获得多例对象可以修改 bean 的作用域 scope 为 protype,默认是单例的。
Redis 的锁讲一下,以及过期策略
Redis 是基于缓存的数据库,里面的锁的主要有两种一个是普通锁:SETNX 原理就是插入数据前先判断数据是否存在存在则不插入就代表加锁失败否则加锁成功,再给他设置一个超时时间防止 Redis 宕机导致的死锁问问题。分布式锁 Redisson:内部基于看门狗机制实现,线程获取到锁后需要想看门狗发送一个心跳,看门狗接收到心跳之后就会给锁续期一般是每 10s 续费到 30s,如果线程挂了,看门狗没有收到心跳则向锁管理器发出释放锁的命令这样防止死锁;redis 的过期策略有两种惰性删除、懒惰删除,惰性删除就是数据只有在被查询的时候才会检查他的 ttl,过期则删除否则返回值,缺点就是存在内存泄露问题也就是可能会有某些 key 长时间不被使用,这样到下一次被使用时才会删除;定期删除:就是一个定时任务去随机 抽取 key 去检查 ttl 是否过期,这样就防止某些 key 长时间不被使用的问题。
线程池讲一下,线程里面有异常怎么处理。怎么接收返回值
线程池是创建线程的进阶方案,主要有 7 个参数:核心线程数、最大线程数、线程空闲存活时间、时间单位、线程工厂、阻塞队列、拒绝策略。最重要的三个参数:核心线程数、最大线程数、阻塞队列;最开始投递任务的时候会创建核心线程,只要核心线程没有满都会创建新的线程,达到核心线程数后开始往阻塞队列里放任务,阻塞队列满了后再将任务交给拯救线程。最后才触发拯救策略。线程出现异常可以用 Future 来捕获异常,或者是再任务代码里使用 trycatch 也行。Tuture 也可以接收返回值,get 会等任务执行完成再返回结果。
项目一定要懂