news 2026/4/15 10:22:09

20260414 java 面试题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
20260414 java 面试题

1、最近项目使用的技术架构;


2、多线程的参数有哪些;

一、线程池关键参数详解
Java线程池的核心实现是ThreadPoolExecutor,其构造函数包含7个关键参数:

public ThreadPoolExecutor( int corePoolSize, // 核心线程数 int maximumPoolSize, // 最大线程数 long keepAliveTime, // 空闲线程存活时间 TimeUnit unit, // 时间单位 BlockingQueue<Runnable> workQueue, // 任务队列 ThreadFactory threadFactory, // 线程工厂 RejectedExecutionHandler handler // 拒绝策略 );


1. 线程数控制参数

corePoolSize(核心线程数)

  • 线程池初始化时默认不会创建线程,直到有任务提交
  • 当线程数 < corePoolSize时,即使有空闲线程也会创建新线程执行任务
  • 可通过prestartAllCoreThreads()预创建所有核心线程

maximumPoolSize(最大线程数)

  • 线程池允许的最大线程数量
  • 当队列满且线程数 < maximumPoolSize时,会创建新线程执行任务


keepAliveTime & unit(空闲线程存活时间)

  • 非核心线程空闲超过此时间会被销毁
  • 通过allowCoreThreadTimeOut(true)可使核心线程同样受此参数影响

2. 任务队列(BlockingQueue)

SynchronousQueue

  • 不存储元素的队列,每个插入操作必须等待另一个线程的移除操作
  • Executors.newCachedThreadPool()默认使用此队列
  • 优点:吞吐量极高;缺点:可能创建大量线程导致OOM

LinkedBlockingQueue

  • 基于链表实现的无界队列(默认容量为Integer.MAX_VALUE)
  • Executors.newFixedThreadPool()默认使用此队列
  • 风险:任务堆积时可能导致OOM

ArrayBlockingQueue

  • 基于数组实现的有界队列,必须指定容量
  • 通过合理设置容量和拒绝策略,可有效防止资源耗尽

PriorityBlockingQueue

  • 支持优先级排序的无界队列,需任务实现Comparable接口

3. 拒绝策略(RejectedExecutionHandler)

当队列满且线程数达到maximumPoolSize时触发:

  • AbortPolicy(默认)

直接抛出RejectedExecutionException,阻止系统正常运行

  • CallerRunsPolicy

由调用线程(提交任务的线程)直接执行该任务,降低提交速率

  • DiscardPolicy

默默丢弃任务,不抛出任何异常

  • DiscardOldestPolicy

丢弃队列中最老的任务,尝试重新提交当前任务

二、线程池性能优化策略

1. 参数配置优化
CPU密集型任务
线程数 ≈ CPU核心数 + 1

int corePoolSize = Runtime.getRuntime().availableProcessors() + 1; new ThreadPoolExecutor( corePoolSize, corePoolSize, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100) );



IO密集型任务
线程数 ≈ CPU核心数 × (1 + 平均等待时间/平均处理时间)

int corePoolSize = Runtime.getRuntime().availableProcessors() * 2; new ThreadPoolExecutor( corePoolSize, corePoolSize * 2, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000) );


混合型任务
将任务拆分为CPU密集型和IO密集型,分别使用不同线程池处理

2. 队列选择策略
任务执行时间短、吞吐量高 → SynchronousQueue
任务执行时间长、需要限制线程数 → ArrayBlockingQueue
任务量稳定、避免任务丢弃 → LinkedBlockingQueue(需控制容量)

3. 拒绝策略选择
关键任务 → CallerRunsPolicy(避免数据丢失)
非关键任务 → DiscardPolicy或DiscardOldestPolicy

4. 线程工厂定制
设置线程名称前缀,便于问题定位
设置守护线程,避免影响JVM退出

ThreadFactory factory = new ThreadFactoryBuilder() .setNameFormat("custom-pool-%d") .setDaemon(true) .build();


5. 动态调整参数
通过JMX或配置中心动态调整线程池参数:

// 增加核心线程数 executor.setCorePoolSize(newCoreSize); // 调整队列容量(需自定义可调整容量的队列) customQueue.setCapacity(newCapacity);

3、java 多线程需要返回值,有哪些方式

在Java中,实现多线程并返回结果有多种方式,每种方式有其适用场景。以下是几种常见的方法:

1. 使用CallableFuture

Callable接口类似于Runnable,但它可以返回一个结果并且可以抛出异常。你可以使用ExecutorService来执行Callable任务,并通过Future获取结果。

import java.util.concurrent.*; public class CallableExample { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(2); Callable<String> task = () -> { // 模拟耗时操作 Thread.sleep(1000); return "Task result"; }; Future<String> future = executor.submit(task); System.out.println("Task result: " + future.get()); // 获取结果 executor.shutdown(); } }

2. 使用CompletableFuture

CompletableFuture提供了更灵活的异步编程方式,支持链式调用和组合多个异步操作。

import java.util.concurrent.*; public class CompletableFutureExample { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(2); CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(1000); // 模拟耗时操作 } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 重新设置中断状态 } return "Task result"; }, executor); System.out.println("Task result: " + future.get()); // 获取结果 executor.shutdown(); } }

3. 使用AtomicReference或自定义容器类在Runnable中共享结果

如果你不想使用CallableFuture,可以通过共享变量(如AtomicReference)在多个线程之间传递结果。但这种方式通常不推荐,因为它违背了多线程设计的最佳实践(即避免共享可变状态)。更好的方式是使用上述的CallableFuture

4. 使用线程局部变量(ThreadLocal)传递结果(不推荐)

虽然可以使用ThreadLocal在每个线程中存储结果,但这同样不是最佳实践,因为它限制了结果的共享和并发性。通常,这种方法只在特定场景下使用,例如在每个线程需要独立处理但又需要访问某些共享数据时。

总结:

推荐使用CallableFuture或者CompletableFuture来实现多线程并返回结果,因为这些方法提供了更好的错误处理、灵活性以及与Java并发工具库的集成。尽量避免使用共享可变状态的方法,比如通过共享变量或线程局部变量传递结果。这些方法虽然技术上可行,但会使代码更难理解和维护。


4、GC机制有哪些;

Java 的垃圾回收(GC)机制是 JVM 自动管理内存的核心功能,主要目标是回收不再使用的对象,防止内存泄漏和溢出。根据当前权威公开资料(截至 2026 年 4 月),Java GC 机制主要包括以下几方面:


一、判断对象是否可回收的算法

  • 引用计数法

    • 每个对象维护一个引用计数器,被引用时 +1,引用失效时 -1。
    • 缺点‌:无法解决‌循环引用‌问题,因此 ‌Java 虚拟机未采用此算法‌ ‌12。
  • 可达性分析算法(主流)

    • 以 ‌GC Roots‌ 为起点,向下搜索引用链。若对象与 GC Roots 无任何引用链相连,则视为不可达,可被回收。
    • GC Roots 包括‌:
      1. 虚拟机栈(栈帧中的本地变量)引用的对象
      2. 方法区中类静态属性引用的对象
      3. 方法区中常量引用的对象
      4. 本地方法栈中 JNI(Native 方法)引用的对象

二、Java 中的四种引用类型(强度递减)

  • 强引用(Strong Reference)

    • Object obj = new Object(),只要强引用存在,GC ‌永远不会回收‌该对象。
  • 软引用(Soft Reference)

    • 描述“有用但非必需”的对象。
    • 内存不足前会保留,发生内存溢出异常前才会被回收。适用于缓存。
  • 弱引用(Weak Reference)

    • 非必需对象,‌仅存活到下一次 GC 之前‌,无论内存是否充足。
  • 虚引用(Phantom Reference)

    • 最弱引用,‌无法通过它获取对象实例‌。
    • 唯一作用是在对象被回收时收到系统通知,必须配合ReferenceQueue使用。

三、对象回收的“两次标记”流程

  1. 第一次标记‌:可达性分析后发现无 GC Roots 引用链,进行筛选——判断是否有必要执行finalize()方法。
  2. 第二次标记‌:若finalize()未被调用过且被重写,则放入 ‌F-Queue 队列‌,由低优先级 Finalizer 线程执行。
    • 若在finalize()中重新与 GC Roots 建立关联(如this赋值给静态变量),可“自救”;
    • finalize()仅执行一次‌,第二次回收时直接清除 ‌。

⚠️ 注意:finalize()方法已被标记为 ‌deprecated‌(JDK 9 起),不推荐使用,应改用try-finallyCleaner机制。


四、常见的垃圾回收算法

  • 标记-清除(Mark-Sweep)

    • 标记所有存活对象,清除未标记的。
    • 缺点‌:效率低,产生‌内存碎片‌ ‌。
  • 复制(Copying)

    • 将内存分为两块,存活对象复制到另一块,清空原块。
    • 优点‌:无碎片;‌缺点‌:内存利用率仅 50%(HotSpot 优化为 Eden:Survivor = 8:1:1)‌。
  • 标记-整理(Mark-Compact)

    • 标记后将所有存活对象向一端移动,清理边界外内存。
    • 优点‌:避免碎片,适合老年代 。
  • 分代收集(Generational Collection)

    • 新生代‌:对象朝生夕死,采用‌复制算法
    • 老年代‌:对象存活率高,采用‌标记-清除‌或‌标记-整理‌ ‌

五、主要垃圾收集器(HotSpot 虚拟机)

收集器区域算法特点
Serial新生代复制单线程,Client 模式默认
ParNew新生代复制Serial 多线程版,可与 CMS 配合
Parallel Scavenge新生代复制关注‌吞吐量‌,可控制停顿时间
Parallel Old老年代标记-整理Parallel Scavenge 的老年代搭档
CMS老年代标记-清除低停顿‌,已废弃(JDK 9+)
G1整堆分区 + 复制+整理支持 Region 划分,可预测停顿,‌Java 9+ 默认‌ ‌511

G1 是目前主流生产环境推荐的收集器,适用于大堆(>4GB)和低延迟场景 ‌511。


六、内存分配策略

  • 对象优先在 ‌Eden 区‌ 分配
  • 大对象直接进入 ‌老年代‌(-XX:PretenureSizeThreshold
  • 长期存活对象晋升至老年代(年龄阈值默认 15,可调-XX:MaxTenuringThreshold
  • 动态年龄判断:Survivor 中相同年龄对象总和 > 一半时,直接晋升老年代
  • 空间分配担保‌:Minor GC 前检查老年代剩余空间是否足够容纳新生代所有存活对象


5、spring事务@Transactional什么情况下会失效;

在使用Spring框架进行事务管理时,@Transactional注解确实非常强大,但它也有一些情况下可能不会按预期工作。以下是一些常见的导致@Transactional注解失效的情况:

  1. 代理方式不正确‌:

    • 基于接口的代理‌:确保你的类是基于接口的。只有基于接口的代理才会通过Spring AOP代理事务。如果你的类是一个类(没有实现接口),你需要使用类代理方式,这通常通过配置来实现,比如在XML配置中设置<tx:annotation-driven proxy-target-class="true"/>
    • 类代理‌:如果你使用的是类而不是接口,确保你在@EnableTransactionManagement注解中或者在你的配置类中设置了proxyTargetClass=true
  2. 方法必须是public‌:

    • @Transactional注解的方法必须是public的。如果你在一个public方法中调用一个非public方法(即使是同一个类中的),事务将不会应用到那个非public方法上。
  3. 内部调用问题‌:

    • 如果你在一个被@Transactional注解的方法内部调用另一个方法,而那个方法没有被@Transactional注解,事务将不会应用到那个内部调用的方法上。例如,在同一个类中调用另一个方法时,除非那个方法也是事务性的,否则事务不会传播。
  4. 异常类型‌:

    • 如果你抛出的异常没有被@Transactional注解的rollbackFor或rollbackForClassName属性捕获,事务可能不会回滚。例如,如果方法抛出了RuntimeException之外的异常,而你只配置了rollbackFor = Exception.class,那么只有Exception及其子类的异常才会导致回滚。
  5. 事务管理器配置‌:

    • 确保你的应用配置了正确的事务管理器,并且@Transactional注解指向了正确的事务管理器。在Spring配置中,你需要确保<tx:annotation-driven/>指向了正确的事务管理器。
  6. 数据访问层框架支持‌:

    • 确保你使用的数据访问层框架(如JPA, Hibernate, JdbcTemplate等)被Spring正确支持。例如,对于JPA和Hibernate,你需要确保EntityManager或Session被正确地注入和使用。
  7. 配置问题‌:

    • 确保你的Spring配置(无论是XML配置还是Java配置)正确设置了事务管理器和AOP代理。

解决方法示例:

  • 确保使用基于接口的代理‌:

@Configuration @EnableTransactionManagement(proxyTargetClass = true) public class AppConfig { // 配置数据源、事务管理器等 }
  • 确保方法为public‌:

public class MyService { @Transactional public void publicMethod() { // 方法内容 } private void privateMethod() { // 不会被事务管理的方法 } }
  • 正确配置事务回滚‌:

@Transactional(rollbackFor = Exception.class) public void myMethod() { // 方法内容,抛出Exception将导致回滚 }

通过检查和解决以上问题,通常可以解决@Transactional注解失效的情况。

6、vue 前端项目父子间怎么传值

在Vue.js中,父子组件间的传值可以通过多种方式进行。以下是几种常见的方法:

1. 使用props传递数据

父组件向子组件传值‌:

  1. 在子组件中定义props
  2. 在父组件的模板中,通过属性绑定将数据传递给子组件。

子组件(ChildComponent.vue):

<template> <div> <p>{{ message }}</p> </div> </template> <script> export default { props: ['message'] } </script>

父组件(ParentComponent.vue):

<template> <ChildComponent :message="parentMessage" /> </template> <script> import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent }, data() { return { parentMessage: 'Hello from parent!' } } } </script>

2. 使用事件传递数据(子组件向父组件)

子组件向父组件传值‌:

  1. 在子组件中,使用$emit触发一个事件。
  2. 在父组件中监听这个事件并接收数据。

子组件(ChildComponent.vue):

<template> <button @click="sendToParent">Send to Parent</button> </template> <script> export default { methods: { sendToParent() { this.$emit('updateMessage', 'Hello from child!'); } } } </script>

父组件(ParentComponent.vue):

<template> <ChildComponent @updateMessage="handleMessage" /> </template> <script> import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent }, methods: { handleMessage(message) { console.log(message); // 处理从子组件接收的数据 } } } </script>

3. 使用Vuex进行状态管理(适用于复杂状态管理)

如果你有多个组件需要共享状态,或者父子组件层级较深,使用Vuex是一个好选择。

  1. 安装并设置Vuex。
  2. 在Vuex中定义state、mutations和actions。
  3. 在需要数据的组件中,通过this.$store.state.someProperty或通过mapState辅助函数来访问状态。在需要修改状态时,通过this.$store.commit('someMutation')this.$store.dispatch('someAction')

安装Vuex‌:

npm install vuex@next --save # Vue 3使用此命令,Vue 2使用`vuex`而不是`vuex@next`。

设置Vuex‌:

// store.js 或 store/index.js import { createStore } from 'vuex'; export default createStore({ state: { message: '' }, mutations: { setMessage(state, message) { state.message = message; } }, actions: { updateMessage({ commit }, message) { commit('setMessage', message); } } });

在Vue组件中使用Vuex‌:

// 在组件中引用store并使用它。例如,在ParentComponent或ChildComponent中。 import { useStore } from 'vuex'; // Vue 3使用此导入方式,Vue 2使用`import store from './store'`。 并在组件中使用`this.$store`。 确保在根实例中提供了store。 例如,在main.js或main.ts中:`createApp(App).use(store).mount('#app');`。 然后在任何子组件中通过`this.$store`访问。 如果你使用的是组合式API(Composition API),可以使用`const store = useStore();`。 然后在setup函数中使用它。例如:`store.commit('setMessage', 'Hello from child!');`。 对于选项式API(Options API),仍然使用`this.$store`。例如:`this.$store.commit('setMessage', 'Hello from child!');`。 确保在setup函数外部或在任何非setup函数中使用this.$store来访问Vuex store。对于组合

7、前端写过哪些公共组件


8、两张表一样,A表比B表多一条数据,怎么查询出来B表少的那条数据;

在MySQL中,如果你想要找出两张表(A表和B表)之间的差异,特别是找出B表缺少的那条数据,你可以使用以下几种方法:

方法1:使用EXCEPT(MySQL 8.0及以上版本)

如果你的MySQL版本是8.0或以上,可以使用EXCEPT关键字来找出B表缺少的数据。这种方法类似于SQL Server中的EXCEPT

SELECT * FROM A EXCEPT SELECT * FROM B;

方法2:使用NOT IN

如果你使用的是MySQL 8.0以下的版本,可以使用NOT IN来实现相同的效果,但这种方法在某些情况下可能效率不高,特别是在处理大量数据时。

SELECT * FROM A WHERE (A.column1, A.column2, ...) NOT IN ( SELECT B.column1, B.column2, ... FROM B );

你需要将column1,column2, ...替换为你的具体列名。

方法3:使用FULL OUTER JOIN然后过滤

使用FULL OUTER JOIN可以展示所有在A表和B表中都存在的行,以及那些只在A表或B表中存在的行。然后你可以通过选择那些在B表中没有的行来找出差异。

SELECT A.* FROM A LEFT JOIN B ON A.column1 = B.column1 AND A.column2 = B.column2 AND ... WHERE B.column1 IS NULL; -- 这里可以根据你的主键或唯一标识列来选择条件

或者反过来,如果你想要找出A表和B表共同的记录之外的记录:

SELECT A.* FROM A LEFT JOIN B ON A.column1 = B.column1 AND A.column2 = B.column2 AND ... WHERE B.column1 IS NULL UNION ALL SELECT B.* FROM B LEFT JOIN A ON A.column1 = B.column1 AND A.column2 = B.column2 AND ... WHERE A.column1 IS NULL;

这种方法可以找出所有在A或B中独有的记录。

方法4:使用MINUS(Oracle的语法)

虽然这不是MySQL的语法,但如果你熟悉Oracle,可以这样写:

SELECT * FROM A MINUS SELECT * FROM B;

在MySQL中,你可以通过上述方法实现类似的效果。

总结:

对于大多数情况,方法3(使用FULL OUTER JOIN然后过滤)是比较通用和灵活的方法。根据你的具体需求(比如是否需要找出所有独有的记录),你可以选择合适的方法。如果你使用的是MySQL 8.0或以上版本,推荐使用EXCEPT方法,因为它更直观且易于理解。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 10:18:03

YOLOv5确定性算法报错解析与CUDA环境下的调试技巧

1. 报错现象与背景分析 最近在部署YOLOv5改进模型时&#xff0c;很多开发者遇到了一个典型的CUDA环境报错。具体表现为&#xff1a;模型在CPU上运行正常&#xff0c;但切换到GPU环境时突然崩溃&#xff0c;终端抛出RuntimeError: adaptive_max_pool2d_backward_cuda does not h…

作者头像 李华
网站建设 2026/4/15 10:13:40

3分钟搞定Windows激活:KMS_VL_ALL_AIO智能激活工具完整指南

3分钟搞定Windows激活&#xff1a;KMS_VL_ALL_AIO智能激活工具完整指南 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 还在为Windows系统激活而烦恼吗&#xff1f;KMS_VL_ALL_AIO是一款开源免费…

作者头像 李华
网站建设 2026/4/15 10:13:37

从分销到动销,破解增长瓶颈的必然选择

引言&#xff1a;在快消行业竞争白热化的当下&#xff0c;曾经支撑行业增长的深度分销模式&#xff0c;如今逐渐显露瓶颈&#xff0c;而以“动销”为核心的营销数字化转型&#xff0c;正成为品牌突破增长天花板的关键。从分销到动销的转变&#xff0c;不是简单的模式替换&#…

作者头像 李华
网站建设 2026/4/15 10:10:48

多模态幻觉识别与抑制全链路指南,覆盖数据注入、推理约束、后验校验三大关键阶段

第一章&#xff1a;多模态大模型幻觉问题研究 2026奇点智能技术大会(https://ml-summit.org) 多模态大模型在融合文本、图像、音频与视频等异构信息时&#xff0c;其推理路径高度依赖跨模态对齐机制与联合表征空间的稳定性。当模态间语义映射存在偏差、训练数据分布不均衡或指…

作者头像 李华