Java后端
·2024-04-26很多小伙伴想要小米一面的面经,因此他来啦~
# 1. 堆和栈的区别?
- 堆 是JVM中 几乎所有对象存放的地方,会存放 创建的对象实例和数组。为什么说是所有的对象,是因为有些对象直接是在栈上分配,就是一些经过JVM逃逸分析之后,未被外部方法所引用的对象;
- 堆也是一个JVM进程中所有资源共享的区域,因此会存在线程安全问题,JVM的垃圾回收的区域也主要是在java堆中
- 栈是 每个线程运行时 所需要的内存空间,栈由多个栈帧组成,对应着每次方法调用时所占用的内存
- 栈帧里面保存着方法的返回值、局部变量、返回地址;当前执行的方法被称为活动栈帧
- 虚拟机栈是线程私有的,因此是线程安全的
# 2. 堆和栈谁的效率高?
- 申请速度快:栈是**程序运行前**就已经分配好的空间,所以运行时分配几乎不需要时间。
- 而堆是运行时动态申请的,相当于将分配内存的耗时由编译阶段转嫁到了机器运行阶段,将分配过程从编译器搬到了运行的代码中。
- 于是动态分配的速度不仅与分配算法有关,还与机器运行速度有关。(栈是编译时分配空间,而堆是动态分配(运行时分配空间),所以栈的申请速度快)
- 存储寻址速度快:**栈的物理地址空间是连续的,而堆未必**,查找堆的链表也会耗费较多时间,所以存储寻址速度慢。
- CPU硬件操作速度快:cpu有专门的寄存器(esp,ebp)来操作栈,堆是使用间接寻址的,所以栈快。
# 3. 介绍一下快速排序的原理
**快排思想:**
- 通过筛选一个基准元素,将待排序列分隔为两个子序列;
- 然后对子序列进行排序,使左子序列的元素小于基准元素,使右子序列的元素大于基准元素
- 之后继续对子序列进行排序,直到使整个序列有序
**流程:**
- 先为序列 设定一个 基准元素,一般是选择第一个元素,然后设置两个前后指针
- 先从右指针开始,如果右指针的元素 小于 基准元素,则将该元素 移动至 左指针位置,然后左指针
- 如果右指针的元素 大于 基准元素,则该元素不动,右指针--,继续判断该元素是否 小于 基准元素
- 若右指针的元素移动到 左指针,然后左指针 ,继续判断该元素是否 大于 基准元素
# 4. Java中线程有哪些状态?
**六种状态**:
1. **初始(NEW)**:新创建了一个线程对象,但**还没有调用start()方法。**
2. **运行(RUNNABLE)**:Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
3. **阻塞(BLOCKED)**:表示线程阻塞于锁。
4. **等待(WAITING)**:进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
5. **超时等待(TIMED_WAITING)**:该状态不同于WAITING,它可以在指定的时间后自行返回。
6. **终止(TERMINATED)**:表示该线程已经执行完毕。
# 5. 在Java中怎么去保证多线程运行安全?
**三种保证线程安全的方法:**
1. **synchronized** 关键字,实现一个 同步代码块 或 同步方法
2. **Lock锁机制**, 通过创建Lock对象,采用lock()加锁,unlock()解锁,来保护指定的代码块
3. **AtomicInteger原子类**,juc包下的保证原子性的操作类
# 6. 什么是AOP?
- AOP 是一种 **面向切面编程** 的思想,让我们能够在不修改源码的情况下 **给程序动态地进行功能增强**,实现功能解耦
- 可以针对于方法切面,去做一个功能的动态增强
- AOP 是基于 动态代理去实现的,因此我们可以针对 方法(切面)进行 前置、后置、环绕、异常通知
**AOP使用场景:**
- 记录日志 (调用方法后记录日志)
- **权限控制** (调用方法前校验是否有权限):OJ项目中使用了 @AuthCheck
- **监控性能** (统计方法运行时间)
- **事务管理** (调用方法前开启事务,调用方法后提交关闭事务 )
> Spring的事务中底层也是基于AOP动态代理去实现的,采用的是Cglib动态代理实现的,通过 **继承被代理类**
> 所以事务失效的原理其实和AOP息息相关,例如事务的异常在方法内部手动捕获,就会造成事务失效,需要向上抛,才能被aop捕获;
> 例如 非public方法事务失效,因为cglib代理是继承被代理类去实现的,需要重写父类需要被增强的方法,如果父类private就不能代理
>
# 7. 在SpringBoot的启动类当中,有用到哪些注解?
- ComponentScan 默认扫描该类所在包及其子包
- SpringBootConfiguration 里面的注解其实就是 Configuration 配置注解
- EnableAutoConfiguration 自动配置的核心注解
- **@AutoConfigurationPackage**
- **@Import({AutoConfigurationImportSelector.class})**
其内部就是读取了 **该项目和该项目引用Jar包** 的 classpath 路径下 `META-INF/Spring.factories` 的 配置类
然后这些配置类会根据 **条件注解所 指定的条件来**判断 是否将该类加载进 Spring容器
- Spring 2.x 自动配置文件 是在 `META-INF/Spring.factories`
- Spring 3.x 是在`META-INF/spring` 文件夹内 的一个 **autoconfigure** 中
例如 `org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration`
其中会根据 `@ConditionalOnxx` 来判断是否要加载 某个类进容器
```java
@AutoConfiguration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}
}
```
# 8. 说说事务的特性?
- 原子性:对数据库进行的一组操作视为原子的,要么全部成功,要么全部失败
- 一致性:数据库总是从一个一致性的状态转换到另一个一致性的状态
- 隔离性:一个事务所做的修改操作在提交事务之前,对于其他事务来说是不可见的
- 持久性:一旦事务提交,则其所做的修改会永久保存到数据库
# 9. MYSQL 的索引不适合用在什么场景中?
首先可以先明确,什么时候适合使用索引?
- 对于查询比较频繁的字段,改和删的操作比较少
- 经常作为查询条件的字段
- 字段的区分度比较高
因此不适合用在:
- 增、删、改操作比较频繁的字段
- 字段的区分度比较低,例如isDelete
# 10. L322 零钱兑换
给你一个整数数组 `coins` ,表示不同面额的硬币;以及一个整数 `amount` ,表示总金额。
计算并返回可以凑成总金额所需的 **最少的硬币个数** 。如果没有任何一种硬币组合能组成总金额,返回 `-1` 。
你可以认为每种硬币的数量是无限的。
**示例 1:**
**输入:coins = `[1, 2, 5]`, amount = `11`
输出:**`3`
**解释:11 = 5 5 1
**示例 2:**
**输入:coins = `[2]`, amount = `3`
输出:**-1
**示例 3:**
**输入:coins = [1], amount = 0
输出:0
```java
public int coinChange(int[] coins, int amount)
{
int[] dp = new int[amount 1];
Arrays.fill(dp, amount 1);
dp[0] = 0;
for (int i = 1; i <= amount; i )
{
for (int j = 0; j < coins.length; j )
{
if (i >= coins[j])
{
dp[i] = Integer.min(dp[i], dp[i - coins[j]] 1);
}
}
}
return dp[amount] == amount 1 ? -1 : dp[amount];
}
```
#求职# #面试# #实习#
22
1
分享
操作
评论
问答助学
相关内容
0个评论
全部评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
