Java 核心技术与框架实战十八问
1、java基本数据类型以及长度 (语言类)
整数类型
用于存储整数数值,以二进制补码形式表示。
类型 | 长度(字节) | 取值范围 | 默认值 |
---|---|---|---|
byte | 1 | -128 至 127 | 0 |
short | 2 | -32768 至 32767 | 0 |
int | 4 | -2^31 至 2^31-1(约 - 21 亿至 21 亿) | 0 |
long | 8 | -2^63 至 2^63-1(约 ±9.2×10^18) | 0L |
浮点数类型
用于存储带小数的数值,遵循 IEEE 754 标准。
类型 | 长度(字节) | 精度 | 默认值 |
---|---|---|---|
float | 4 | 单精度(约 6~7 位有效数字) | 0.0f |
double | 8 | 双精度(约 15~17 位有效数字) | 0.0d |
字符类型
用于存储单个字符,采用 Unicode 编码(16 位无符号)。
类型 | 长度(字节) | 取值范围 | 默认值 |
---|---|---|---|
char | 2 | 0 至 65535('\u0000' 至 '\uffff') | '\u0000' |
布尔类型
用于表示逻辑值(真 / 假)。
类型 | 长度(字节) | 取值范围 | 默认值 |
---|---|---|---|
boolean | 1(规范未明确规定,实际取决于 JVM 实现) | true 或 false | false |
2、ArrayList存储原理以及长度和扩容规则 (语言类)
ArrayList基于动态数组实现的;长度是实际存储的元素个数,通过size()方法获取;初始默认容量是10(jdk8+);当数组长度大于容量时触发扩容,扩容为原来的1.5倍。
3、HashMap存储原理以及扩容规则 (语言类)
HashMap基于哈希表(数组 + 链表 / 红黑树)实现,JDK 8+ 引入红黑树优化链表过长问题(当链表长度超过 8 且数组长度超过 64 时,链表转换为红黑树(时间复杂度从 O(n)
优化到 O(log n)
));HashMap初始默认容量为16,负载因子为0.75,阈值=初始默认容量*负载因子,当元素数量超过阈值时触发扩容,扩容为原来的两倍。
4、SpringBoot常见的注解 (框架类)
核心注解
@SpringBootApplication
作用:标记主类,等价于 @Configuration + @EnableAutoConfiguration + @ComponentScan@RestController
作用:标记控制器,等价于 @Controller + @ResponseBody,返回 JSON/XML 格式数据@Autowired
作用:自动注入依赖(基于类型)
请求映射注解
@RequestMapping:通用请求映射
@GetMapping:处理 GET 请求(等价于 @RequestMapping(method = RequestMethod.GET))
@PostMapping:处理 POST 请求
@PutMapping:处理 PUT 请求
@DeleteMapping:处理 DELETE 请求
组件扫描注解
@Component:通用组件标记
@Service:标记业务层组件
@Repository:标记数据访问层组件(自动处理数据访问异常)
@Controller:标记控制器组件
配置注解
@Configuration:标记配置类,等价于 XML 配置文件
@Bean:声明 Bean,相当于 XML 中的 <bean> 标签
@PropertySource:加载外部配置文件(如 .properties)
@Value:注入配置值(如 @Value("${app.name}"))
条件注解
@ConditionalOnClass:当类路径下存在指定类时生效
@ConditionalOnMissingBean:当容器中不存在指定 Bean 时生效
@Profile:根据环境(如 dev、prod)激活配置
测试注解
@SpringBootTest:集成测试注解,启动完整 Spring 应用上下文
@WebMvcTest:仅测试 Web 层组件
@MockBean:模拟 Bean(如 Mock 服务层)
5、ArrayList,HashSet,HashMap是否线程安全,如何线程安全 (语言类)
- ArrayList:非线程安全。多个线程同时操作(如添加、删除元素)可能导致数据不一致或抛出
ConcurrentModificationException
。 - HashSet:非线程安全。基于
HashMap
实现,多线程操作会有线程安全问题。 - HashMap:非线程安全。多线程下可能出现死循环(JDK 7 及以前)或数据丢失。
线程安全的替代方法:
1.使用同步包装类
List list = Collections.synchronizedList(new ArrayList<>());
Set set = Collections.synchronizedSet(new HashSet<>());
Map map = Collections.synchronizedMap(new HashMap<>());
- 原理:通过
synchronized
关键字对所有方法加锁,保证线程安全。 - 缺点:性能较低,所有操作串行执行。
2.使用并发集合类
import java.util.concurrent.*;List list = new CopyOnWriteArrayList<>();
Set set = new CopyOnWriteArraySet<>(); // 基于CopyOnWriteArrayList
Map map = new ConcurrentHashMap<>();
CopyOnWriteArrayList/Set
:- 读写分离,写操作(如
add
)时复制数组,读操作无需加锁。 - 适用于读多写少的场景,写操作多时性能较差。
- 读写分离,写操作(如
ConcurrentHashMap
:- JDK 7:分段锁(Segment),不同段可并行读写。
- JDK 8:CAS + synchronized,锁粒度更小(锁槽位而非整个表)。
- 推荐使用,性能优于
synchronizedMap
。
总结:
- 线程安全:优先使用
ConcurrentHashMap
和CopyOnWriteArrayList
,避免synchronized
包装类的性能损耗。
6、多线程的4种创建方式 (语言类)
1.继承thread类
public class MyThread extends Thread {@Overridepublic void run() {System.out.println("Thread running");}public static void main(String[] args) {MyThread thread = new MyThread();thread.start(); // 启动新线程}
}
- 缺点:Java 单继承,继承
Thread
后无法继承其他类。
2.实现Runnable接口
public class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("Runnable running");}public static void main(String[] args) {Thread thread = new Thread(new MyRunnable());thread.start();}
}
- 优点:避免单继承限制,更灵活。
3.实现Callable接口(带返回值)
import java.util.concurrent.*;public class MyCallable implements Callable<String> {@Overridepublic String call() throws Exception {return "Result from Callable";}public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService executor = Executors.newSingleThreadExecutor();Future<String> future = executor.submit(new MyCallable());String result = future.get(); // 获取返回值(阻塞)System.out.println(result);executor.shutdown();}
}
- 特点:
call()
方法可返回结果,通过Future
获取。- 支持抛出异常。
4. 使用线程池(推荐)
import java.util.concurrent.*;public class ThreadPoolExample {public static void main(String[] args) {// 创建固定大小的线程池ExecutorService executor = Executors.newFixedThreadPool(5);// 提交任务for (int i = 0; i < 10; i++) {final int taskId = i;executor.submit(() -> {System.out.println("Task " + taskId + " executed by " + Thread.currentThread().getName());});}// 关闭线程池executor.shutdown();}
}
-
线程池优势:
- 复用线程,减少创建 / 销毁开销。
- 控制最大并发数,避免资源耗尽。
- 提供任务管理机制(如定时任务、拒绝策略)。
常见线程池工厂方法:
Executors.newFixedThreadPool(5); // 固定大小线程池
Executors.newCachedThreadPool(); // 缓存线程池(按需创建)
Executors.newSingleThreadExecutor(); // 单线程池
Executors.newScheduledThreadPool(5); // 定时任务线程池
手动创建线程池(推荐):
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, // 核心线程数5, // 最大线程数60, // 空闲线程超时时间TimeUnit.SECONDS,new LinkedBlockingQueue<>(10), // 任务队列Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
总结:多线程创建推荐使用线程池(如Executors
或手动创建ThreadPoolExecutor
),提升性能和可管理性。
7、JVM中的5大组成部分以及每一部分的作用 (语言类)
-
类加载器(ClassLoader)
-
作用:加载
.class
文件到内存,生成对应的Class
对象。 -
流程:加载 → 验证 → 准备 → 解析 → 初始化。
-
分类:
-
启动类加载器(Bootstrap ClassLoader):加载 JRE/lib 目录下的核心类(如
java.lang.*
)。 -
扩展类加载器(Extension ClassLoader):加载 JRE/lib/ext 目录下的扩展类。
-
应用类加载器(Application ClassLoader):加载用户路径(classpath)下的类。
-
自定义类加载器:继承
ClassLoader
实现特殊加载逻辑。
-
-
-
运行时数据区(Runtime Data Area)
-
方法区(Method Area):
存储类信息、常量、静态变量等,是所有线程共享的内存区域。
JDK 8 后,方法区被元空间(Metaspace)取代,使用本地内存而非堆内存。 -
堆(Heap):
所有对象实例和数组分配的内存区域,垃圾回收的主要区域。
分为新生代(Eden、Survivor)和老年代。 -
虚拟机栈(VM Stack):
每个线程私有的内存区域,存储局部变量表、操作数栈、动态链接等。
每个方法执行时创建一个栈帧,方法结束后栈帧弹出。 -
本地方法栈(Native Method Stack):
与虚拟机栈类似,但为本地方法(如native
修饰的方法)服务。 -
程序计数器(Program Counter Register):
记录当前线程执行的字节码行号,线程私有。
-
-
执行引擎(Execution Engine)
-
解释器(Interpreter):逐条解释字节码为机器码执行。
-
即时编译器(JIT Compiler):将热点代码(频繁执行的代码)编译为本地机器码,提升执行效率。
-
垃圾回收器(Garbage Collector):回收不再使用的对象,释放内存。
-
-
本地方法接口(Native Interface)
-
与本地方法库(如 C/C++ 库)交互的接口,通过
native
关键字调用。
-
-
本地方法库(Native Libraries)
-
提供本地方法实现的库,如
java.lang.System
类中的arraycopy()
方法。
-
8、常见的垃圾回收器有哪些? (语言类)
-
新生代回收器
- Serial:单线程,STW(Stop The World),适合小型应用。
- ParNew:Serial 的多线程版本,配合 CMS 使用。
- Parallel Scavenge:多线程,关注吞吐量(运行用户代码时间 / 总时间)。
-
老年代回收器
- Serial Old:单线程,适合客户端模式。
- Parallel Old:Parallel Scavenge 的老年代版本,多线程。
- CMS(Concurrent Mark Sweep):
以获取最短回收停顿时间为目标,分初始标记、并发标记、重新标记、并发清除四个阶段。
缺点:产生内存碎片,并发阶段占用 CPU 资源。
-
整堆回收器
- G1(Garbage-First):
分代收集,将堆划分为多个 Region,优先回收垃圾最多的区域。
适合大内存、多 CPU 的服务器。 - ZGC(Z Garbage Collector):
超低延迟,支持 TB 级内存,使用染色指针和读屏障技术。
JDK 11 引入,JDK 15+ 正式支持。
- G1(Garbage-First):
-
最新回收器
- Shenandoah:与 G1 类似,但并发阶段更多,延迟更低。
- Epsilon:实验性回收器,仅分配内存,不执行垃圾回收(用于性能测试)。
9、什么是双亲委派策略,有何作用 (语言类)
双亲委派机制:当类加载器需要加载类时,先委托给父类加载器尝试加载,直到启动类加载器。只有父类无法加载时,才由当前类加载器自己加载。
作用:
- 避免类重复加载:确保同一个类只被加载一次。
- 保证安全性:防止恶意替换核心类(如
java.lang.Object
)。
若用户自定义一个java.lang.Object
,通过双亲委派机制会优先加载 JDK 中的Object
,避免风险。
10、什么是新生代,老年代,永久存储区? (语言类)
JVM 内存分代模型
-
新生代(Young Generation)
- 作用:存储新创建的对象。
- 分区:
- Eden 区:对象初始分配的区域。
- Survivor 区(S0、S1):Eden 区满时,存活的对象被复制到 Survivor 区。
- Minor GC:新生代垃圾回收,频繁触发,速度快。
- 对象晋升:多次 GC 后仍存活的对象会被移至老年代。
-
老年代(Old Generation)
- 作用:存储长期存活的对象(如静态变量、单例对象)。
- Major GC/Full GC:老年代垃圾回收,耗时较长,可能触发 STW。
-
永久代(Permanent Generation)与元空间(Metaspace)
- 永久代(JDK 7 及以前):
存储类元数据、常量池等,属于堆内存的一部分,大小通过-XX:MaxPermSize
限制。 - 元空间(JDK 8+):
取代永久代,使用本地内存(Native Memory)存储类元数据,避免永久代 OOM。
大小通过-XX:MetaspaceSize
和-XX:MaxMetaspaceSize
控制。
- 永久代(JDK 7 及以前):
11、ThreadLocal 以及底层实现原理 (语言类)
- 作用:为每个使用该变量的线程都提供一个独立的变量副本,每个线程都可以独立地改变自己的副本,而不会影响其他线程。
- 底层原理:
- ThreadLocalMap:每个
Thread
对象内部都维护一个ThreadLocalMap
,其键为ThreadLocal
实例(弱引用),值为线程变量副本。 - 弱引用机制:若外部无强引用指向
ThreadLocal
,GC 时会回收键,但值仍可能存在(强引用),需手动调用remove()
避免内存泄漏。
- ThreadLocalMap:每个
12、HashMap和Hashtable的区别 (语言类)
对比项 | HashMap | Hashtable |
---|---|---|
线程安全 | 非线程安全 | 线程安全(所有方法synchronized ) |
null 支持 | 允许一个null 键和多个null 值 | 不允许null 键 / 值(抛 NPE) |
初始容量 | 16 | 11 |
扩容机制 | 2 倍 | 2*oldCapacity+1 |
数据结构 | 数组 + 链表 + 红黑树(JDK8+) | 数组 + 链表 |
迭代器 | Iterator (支持 fail-fast) | Enumeration (不支持) |
13、Spring 的 IOC 和 AOP (框架类)
- IOC(控制反转):
- 将对象的创建和依赖关系管理交给 Spring 容器,而非手动 new 对象。
- 实现方式:依赖注入(DI,如构造器注入、Setter 注入)。
- AOP(面向切面编程):
- 将横切关注点(如日志、事务)与业务逻辑分离,通过动态代理增强代码。
- 核心概念:切面(Aspect)、通知(Advice)、切入点(Pointcut)。
14、添加索引的规则,索引的数据结构(B+树)(数据库类)
- 索引添加规则:
- 在
WHERE
、JOIN
、ORDER BY
频繁出现的字段上建索引。 - 选择高区分度字段(如用户 ID,避免性别字段)。
- 避免冗余索引(如已有复合索引
(a,b)
,无需单独为a
建索引)。
- 在
- B + 树结构:
- 所有数据存储在叶子节点,非叶子节点仅存索引键和指针。
- 叶子节点通过指针相连,支持范围查询,高度平衡(通常 3-4 层)。
15、mysql 优化 (数据库类)
- 索引优化:
- 为高频查询字段添加复合索引。
- 避免在索引字段上使用函数或表达式(如
WHERE YEAR(create_time)=2023
)。
- 查询优化:
- 避免
SELECT *
,仅查询需要的字段。 - 用
JOIN
替代子查询,优化分页(如WHERE id >= (SELECT id FROM t LIMIT 100000,1)
)。
- 避免
- 配置优化:
- 调整
innodb_buffer_pool_size
(建议物理内存的 50%-75%)。 - 优化
innodb_log_file_size
(日志文件大小)。
- 调整
16、explain 关键字 分析 慢查询 (数据库类)
- 作用:分析 SQL 执行计划,查看索引使用、扫描行数、连接类型等。
- 关键字段:
type
:访问类型(system
>const
>ref
>range
>index
>all
,all
表示全表扫描)。rows
:预估扫描行数,值越大性能越差。Extra
:额外信息(如Using filesort
、Using temporary
表示需优化)。
17、mybatis 的一级缓存和二级缓存 (框架类)
- 一级缓存:
- 作用域:
SqlSession
级别,默认开启。 - 生命周期:会话结束时清除,同一会话内重复查询相同 SQL 会复用缓存。
- 作用域:
- 二级缓存:
- 作用域:
namespace
级别(全局缓存),需手动配置。 - 特点:多个
SqlSession
共享,可跨会话复用缓存。
- 作用域:
18、mybatis 中的一对一,一对多,多对对如何处理 (框架类)
- 一对一:
- 方式:
<association>
标签,嵌套查询或结果映射。 - 示例:用户(User)关联账户(Account)。
- 方式:
- 一对多:
- 方式:
<collection>
标签,嵌套结果或嵌套查询。 - 示例:订单(Order)包含多个商品(Product)。
- 方式:
- 多对多:
- 实现:通过中间表,使用
<collection>
+ 联合查询。 - 示例:学生(Student)与课程(Course)通过选课表关联。
- 实现:通过中间表,使用