Java面试题

场景设计

设计一个下单接口需要考虑哪些场景

下单接口是电商系统的核心,也是最复杂的接口之一。它不仅仅是插入一条订单数据,而是涉及用户、营销、商品、仓储、资金、等多个系统的协同工作

1、核心业务流程与数据一致性(基本功能)

参数校验、商品信息校验、价格计算、数据一致性:下单过程涉及多个数据源的写操作(订单表、库存表、优惠券表、积分表等)

2、性能与高并发(保证系统不挂)

流量削峰(可以使用消息队列作为缓冲池,将瞬时的大量请求先入队)、服务拆分与隔离、异步化(非核心的业务,如生成电子发票、发送短信/App通知)、缓存策略(例如,商品信息、用户信息、优惠券信息都可以缓存起来,减少查询)

3、安全与风控(保护公司和用户资产)

防重复提交、防刷:识别恶意刷单行为(如限流:同一用户/IP在一段时间内下单次数限制)、信息泄露(敏感信息(如金额)脱敏)。

4、可观测性与可靠性(快速定位和解决问题)

日志记录(需要集成唯一的链路追踪ID)、监控与告警(对接口的 QPS、响应时间、错误率等关键指标进行监控)、幂等性(客户端可能会重试请求)

总结:保证业务正确和数据一致的基础上,通过架构设计来应对高并发,通过风控手段保障安全,并通过完善的监控体系来保证系统的稳定可靠

Mysql

交易表统计商户交易笔数大于1的商户号?

1、Group by merchant_id
2、COUNT(*) OVER (PARTITION BY merchant_id) as transaction_count

3、子查询

java 复制代码
SELECT DISTINCT t1.merchant_id
FROM transactions t1
WHERE EXISTS (
    SELECT 1 
    FROM transactions t2 
    WHERE t2.merchant_id = t1.merchant_id 
      AND t2.status = 'success'
      AND t2.id != t1.id  -- 确保是另一条记录
)
AND t1.status = 'success';

Mysql关联查询有哪些

java 复制代码
1、INNER JOIN	内连接,返回匹配的行(很好理解,左右能匹配上的取数据)
2、LEFT JOIN	左外连接,返回左表所有行+右表匹配行
3、RIGHT JOIN	右外连接,返回右表所有行+左表匹配行
4、FULL JOIN	全外连接,返回左右表所有行(实际没有这个需要通过2个表UNION来实现,且2个表输出的列要保持一致)
5、CROSS JOIN	交叉连接,笛卡尔积(行数=左行*右行)
6、SELF JOIN	自连接,表与自身连接(自己连接自己)

介绍下索引下推

java 复制代码
SELECT * FROM users WHERE age > 18 AND city LIKE '杭州%';
//age和city都是联合索引

1、没有索引下推的情况:

java 复制代码
1、存储引擎根据索引 idx_age_city找到所有 age > 18的记录。
2、将这些记录对应的主键ID(这里是 id)全部返回给Server层。
3、Server层根据这些主键ID回表(通过主键索引)读取完整的行数据。
4、Server层再对完整的行数据应用 city LIKE '杭州%'条件进行过滤。

2、有索引下推的情况:

java 复制代码
1、存储引擎根据索引idx_age_city找到所有age > 18的记录。
2、在存储引擎层,直接利用索引中的 city列信息,应用 city LIKE '杭州%'条件进行过滤。
3、只将满足age > 18 AND city LIKE '杭州%'条件的记录的主键ID返回给Server层。
4、Server层再对这些大大减少的主键ID进行回表操作。

核心受益:虽然依然需要回表,但回表的次数显著减少,因为无效数据在存储引擎层就被过滤掉了。

间隙锁和临键锁

间隙锁和临键锁都是 InnoDB 引擎在可重复读隔离级别下为了解决幻读问题而引入的锁机制。它们的核心区别在于锁定的范围不同

间隙锁(Gap Lock):

锁定范围:锁定一个索引区间,但不包含记录本身。例如,锁定 (5, 10)这个开区间,会阻止其他事务在这个区间内插入新的记录(比如插入 6, 7, 8),但不会阻止其他事务修改或删除 id=5或 id=10这两条已存在的记录。

特点:间隙锁的唯一目的就是防止其他事务在间隙中插入数据,从而防止幻读。间隙锁之间是兼容的,即一个事务加了间隙锁,不会阻止别的事务对同一个间隙加间隙锁。它们共享同一个"不让你插入"的目标。

临键锁(Next-Key Lock):

锁定范围:它是 记录锁(行锁) 和 间隙锁的结合。即锁定一个索引区间并且锁定记录本身。例如,锁定 (5, 10]这个左开右闭区间。它既会阻止其他事务修改 id=10这条记录(记录锁),也会阻止在 (5, 10)这个区间插入新记录(间隙锁)。

特点:临键锁是 InnoDB 默认的行锁模式。它通过"锁定当前记录和之前的间隙"来实现,既能防止脏写(行锁),也能防止幻读(间隙锁)。

假设我们有一张表 user,其主键 id 的索引上有记录 5, 10, 15。

场景一:触发间隙锁

查询语句:SELECT * FROM user WHERE id > 10 AND id < 15 FOR UPDATE;

锁定范围:这里满足条件的记录只有 id=15吗?不,为了防止幻读,InnoDB 会锁定索引区间 (10, 15)。这是一个间隙锁。因为 id=15本身(15)并不满足 id < 15的查询条件,所以不会锁住 id=15这条记录。

效果:其他事务无法在 10和 15之间插入 id=11, 12, 13, 14的记录,但可以修改或删除 id=15的记录。

场景二:触发临键锁(最常见的情况)

查询语句:SELECT * FROM user WHERE id >= 10 AND id <= 15 FOR UPDATE;

锁定范围:满足条件的记录是 id=10和 id=15。InnoDB 会加两个临键锁:

(5, 10]:锁住 id=10这条记录,并锁住 (5, 10)这个间隙。

(10, 15]:锁住 id=15这条记录,并锁住 (10, 15)这个间隙。

效果:其他事务不能修改 id=10或 id=15的记录,也不能在 (5,10)或 (10,15)的区间内插入任何新记录。

场景三:等值查询的特殊情况

查询语句:SELECT * FROM user WHERE id = 12 FOR UPDATE;

情况分析:数据库中并不存在 id=12的记录。

锁定范围:为了防止幻读(即防止别的事务插入一条 id=12的记录),InnoDB 会锁定 (10, 15)这个间隙(一个间隙锁)。如果 id=12存在,那么默认加的就是 (10, 15]这个临键锁。

总结:临键锁是 InnoDB 的默认加锁单元,可以看作是"加强版的间隙锁"。理解它们的区别和适用场景,对于设计高并发、数据一致性要求高的应用至关重要。

Spring

一个接口多个实现类时,如何注入Map

方式1:

java 复制代码
@Autowired
private Map<String, MyService> myServiceMap;

方式2:

java 复制代码
@Resource
private Map<String, MyService> myServiceMap;

方式3

java 复制代码
@Component
public class MyComponent implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public void someMethod() {
        Map<String, MyService> myServiceMap = applicationContext.getBeansOfType(MyService.class);
    }
}

方式4:

java 复制代码
@Configuration
public class ServiceConfig {
	@Bean
    public Map<String, MyService> myServiceMap(ApplicationContext context) {
    //通过ApplicationContext在结和Type
    	context.getBeansOfName(xxx);

    	Map<String,MyServices> map = new HashMap<String,MyServices>();

    	map.put(xx,MyServices);
        return map;
    }
}

ByType的实现原理,可以发现findBeansByType返回的就是一个Map结构。

java 复制代码
private Map<String, Object> findBeansByType(Class<?> interfaceType) {
    .....
    Map<String, Object> result = new LinkedHashMap<>();
    
    // 1. 遍历容器中所有的Bean
    for (Map.Entry<String, Object> entry : beanFactory.entrySet()) {
        String beanName = entry.getKey();
        Object bean = entry.getValue();
        Class<?> beanClass = bean.getClass();
        
        // 2. 检查Bean是否实现了目标接口
        if (interfaceType.isAssignableFrom(beanClass)) {
            result.put(beanName, bean);
        }
        
        // 3. 检查Bean的接口(包括父接口)
        ....
        result.put(beanName, bean);
    }
    
    return result;
}

ByName的实现原理

java 复制代码
public Object getResource(Resource element, String requestingBeanName) {
    // 关键方法:根据目标类型决定注入策略,如果类型是Map,就会通过resolveMapResource方法来操作。
    if (Map.class.equals(element.lookupType)) {
        return resolveMapResource(element, requestingBeanName);
    }
    // 其他类型处理...
}

//获取字段属性,拿到属性的类型,通过getByType来获取。
private Object resolveMapResource(Resource resource, String beanName) {
    // 获取Map的泛型信息
    ResolvableType mapType = ResolvableType.forField(resource.getField());  
    Class<?> valueType = mapType.getGeneric(1).resolve();
    
    // 按类型获取所有Bean
    Map<String, ?> beans = beanFactory.getBeansOfType(valueType);
    return beans;
}

Java

线程池使用场景

为了高效地管理线程生命周期、控制并发资源消耗,从而提升系统性能和响应能力。具体来说,主要应用于需要处理大量、短期、异步任务的场景。

高并发请求处理(Web服务器)

在瞬间会接收到成千上万个HTTP请求。如果每个请求都创建一个新线程来处理,线程的频繁创建和销毁会造成巨大开销,甚至可能导致系统资源耗尽而崩溃。

异步处理任务(提升响应速度):

用户执行一个操作后,有些步骤并不需要立即完成或告知用户结果。例如,用户注册成功后,需要发送欢迎邮件、初始化用户数据、记录日志等。如果这些操作都在主线程中同步执行,用户需要等待很长时间才能看到"注册成功"的提示。

周期性或定时任务

许多系统需要定时执行一些任务,比如每天凌晨统计报表、每隔5分钟同步一次数据、定期清理临时文件等。

总结就是:降低资源消耗、提高响应速度、提高线程的可管理性

ConcurrentHashMap线程安全吗

如果是线程安全,又是怎么保障线程安全的。如果只回到synchronized,那么面试官还会问

单利模式下的懒汉式就算使用了Synchronized其实也不安全,还需要在变量修饰一个volatile,那么HashMap是怎么做到的?

看下源码

java 复制代码
Node<K,V> node = table[i];//桶中某一个元素。
synchronized (node) {}
//看下table是如何被定义的。
volatile Node<K,V> next;//也是加了volatile。
那么Node上的链表呢?也是一样用了volatile。
相关推荐
闫有尽意无琼1 小时前
银河麒麟v11 arm编译Qt creator8.0.2报错
开发语言·qt
小此方2 小时前
从零开始手搓堆:核心操作实现 + 堆排序 + TopK 算法+ 向上调整 vs 向下调整建堆的时间复杂度严密证明!
开发语言·数据结构·算法
_OP_CHEN2 小时前
从零开始的Qt开发指南:(五)Qt 常用控件之 QWidget(上):解锁 Qt 界面开发的核心基石
开发语言·c++·qt·前端开发·qwidget·gui开发·qt常用控件
wjs20242 小时前
SQLite 视图
开发语言
q***2512 小时前
Spring容器的开启与关闭
java·后端·spring
q***44812 小时前
java进阶--多线程学习
java·开发语言·学习
0***m8222 小时前
Maven Spring框架依赖包
java·spring·maven
艾斯比的日常2 小时前
Neo4j 完全指南:从核心特性到 Java 实战(附企业级应用场景)
java·开发语言·neo4j
K***43062 小时前
三大框架-Spring
java·spring·rpc