- 如何判断一个字符串变量a为空,包括null、空字符串、一个或多个空格、一个或多个whitespace?
if (a == null || a.isBlank()) {
}
- Java新版本为什么要添加Instant?
Java 8 引入
Instant主要是为了修复旧版日期时间类(如java.util.Date)的设计缺陷,原因如下:
- 不可变性与线程安全:
Instant是不可变的(Immutable),天然线程安全;而
Date是可变的,在多线程环境下容易产生并发问题。
- 更高精度:
Instant支持纳秒级( 10\^{-9} 秒)精度;而
Date只能精确到毫秒。
- 语义清晰(机器时间):
Instant 明确表示 UTC 时间线上的瞬时点(timestamp),语义清晰,适合机器时间处理(日志、分布式时间戳、事件时间);
而 Date API 设计混乱,日期计算与格式化常依赖 Calendar、TimeZone 等类,易产生时区问题。
- 如何判断两个double类型的变量是否相等?例如
doublea=1.2;doubleb=1.1 +0.1;比较a和b是否相等。
java
public static boolean nearlyEqual(double a, double b, double epsilon) {
return Math.abs(a - b) < epsilon;
}
// 使用
double a = 1.2;
double b = 1.1 + 0.1;
boolean equal = nearlyEqual(a, b, 1e-9); // true
//epsilon 的取值取决于业务精度要求,常见取 1e-9 到 1e-6。
- 下面三条语句是否是合法的,如果不是原因是什么?
java
List<String> a = new ArrayList<String>();//1
List<Object> a = new ArrayList<String>();//2
List<?> a = new ArrayList<String>();//3
1.合法:左右类型完全匹配。
2.不合法:Java 泛型具有不变性,List 与 List 不存在父子关系。
若允许赋值,可通过 List 写入非 String 类型,破坏类型安全。
3.合法:? 是无界通配符,代表未知类型,ArrayList 是 List<?> 的子类型。
但由于类型未知,只能读取不能写入(null 除外),因此编译器允许。
- 一个字符串String,如何转换为字节数组。反过来呢?
java
//1.String → byte[]
String str = "CodeEdge_Xu";
// 显式指定编码(避免平台差异)
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
//2.byte[] → String
byte[] bytes = ...;
// 与编码时保持一致
String str = new String(bytes, StandardCharsets.UTF_8);
- Java中,如果对象有循环引用,能否被GC。比如Path类里面有个字段reversedPath反向字段。两个Path对象因为这个字段相互引用。如果不能被GC,怎么解决。
能。
Java 主流 GC(G1、ZGC、CMS)用的是可达性分析,不是引用计数。
只要这两个 Path 对象整体从 GC Roots 不可达,循环引用不影响回收。
真正导致不能 GC 的是外部强引用,比如:静态字段持有,ThreadLocal 未 remove,未注销的监听器。
解决办法:
一般不需要特殊处理。
只要外部强引用断开,对象整体从 GC Roots 不可达即可回收。
真正的问题是被静态变量、线程、缓存、监听器等长期持有。
- 写正则表达式,从类似"10,12;A1@12|12:23|p=1|f=2"字符串中抽出变量:"{x},{y};{name}@{index}|{timeStart}|{timeEnd}p={id}"。其中,除了name外其余均是数字,name中有数字、字母、中划线、下划线。
java
Pattern pattern = Pattern.compile(
"^(\\d+),(\\d+);([A-Za-z0-9_-]+)@(\\d+)\\|(\\d+:\\d+)(?:\\|(\\d+:\\d+))?\\|p=(\\d+)\\|f=(\\d+)$"
);
- 什么是大端(Big-endian),什么小端(Little-endian)?网络传输一般是大端还是小端?
大端:高位字节放前面,低位放后面。
小端:低位字节放前面,高位放后面。
网络传输一般是大端。
TCP/IP 协议规定数据包中的多字节整数必须按大端传输。
- 一个整数变量,如果只有一个线程修改它,但有多个线程读取它,有线程安全问题吗?
有线程安全问题,主要是可见性问题。
写线程修改后的值,其他线程不一定能立刻看到,可能一直读取旧值,因此通常需要使用 volatile 或同步机制保证可见性。
对于 long/double,早期 JVM 还存在非原子读写问题(现代 JVM 通常已解决)。
10 . Java的线程能被强制杀死吗?
不能安全地强制杀死。
Thread.stop() 可以强制终止,但已废弃(deprecated),因为会立即释放锁,导致对象状态损坏、资源泄漏。
11 . 如果想要一个线程安全的List,可以用List的哪些实现,或通过什么手段实现。
| 方式 | 特点 |
|---|---|
Vector |
古老类,所有方法 synchronized,已不推荐 |
Collections.synchronizedList(new ArrayList<>()) |
包装器,全表锁,读也串行 |
CopyOnWriteArrayList |
读多写少首选,写时复制新数组,读无锁 |
| 手动加锁 | synchronized 或 ReentrantReadWriteLock,灵活但代码重 |
12 . 有一个List,其中的元素是监听器对象。在多线程环境下,需要对这个List进行添加、删除和遍历三个操作,如何线程安全地进行?除了对添加、删除和遍历加锁或同步的方式------因为如果对遍历加同步,可能会阻塞很长时间,因为遍历一遍可能要很久,会阻塞添加、删除等操作,也会阻塞其他线程的遍历操作。
用
CopyOnWriteArrayList。它内部写操作(add/remove)时复制一份新数组,修改完后替换引用;
读/遍历直接读当前数组快照,完全不加锁。
所以遍历不会阻塞添加、删除,也不会阻塞其他线程遍历。
迭代器基于快照,不会抛
ConcurrentModificationException。
13 . Java核心库中创建线程池的方法是?通过核心库的线程池实现并发,比new Thread有哪些好处?
工具类工厂方法:
使用
java.util.concurrent.Executors提供的静态方法(如newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor等)。直接构造对象:
通过
new ThreadPoolExecutor(...)手动创建。推荐直接使用 ThreadPoolExecutor 手动指定核心线程数、最大线程数、队列长度和拒绝策略,而不是直接使用 Executors 工厂方法,因为部分默认实现可能导致 OOM。
比new Thread的好处:
- 降低资源消耗 :通过复用已创建的线程,减少频繁创建和销毁线程的 CPU 和内存开销。
- 提高响应速度:任务到达时,无需等待线程创建即可立即执行。
- 提高线程可管理性:能统一分配、调优和监控线程,防止无限制创建线程导致系统资源耗尽(如 OOM)。
14 . MySQL中,varchar(100)的列,能存多少个英文字母?多少个汉字?使用utf8mb4字符集。
100 个英文字母,100 个汉字。
MySQL 的
varchar(100)中 100 是字符数,不是字节数。utf8mb4 下英文字母通常 1 字节,中文常见汉字一般 3 字节,部分扩展字符可能占 4 字节,但 varchar(100) 限制的是字符数,因此最多依然存 100 个字符。
15 . MySQL中,datetime和timestamp有何不同,精度分别是毫秒还是秒,是否有时区?
datetime:
- 范围大(1000~9999 年)
- 不涉及时区
- 默认精度到秒
- 支持小数秒 datetime(fsp),最多微秒(6位)
timestamp:
- 范围小(1970~2038 年)
- 有时区语义(存 UTC,读时按 session timezone 转换)
- 默认精度到秒
- 支持 timestamp(fsp),最多微秒(6位)
16 . 写SQL:
订单表wms_order,单行表wms_order_line,一对多关系。
wms_order表有列id(单号)、create_on(创建时间)等字段,
wms_order_line有id、order_id(所属订单)、line_no(行号)、product_id (商品 ID)。
如何查询2021年12月内发生的含有A、B、C三个商品的订单。
sql
SELECT o.id AS order_id, o.create_on
FROM wms_order o
INNER JOIN wms_order_line ol ON o.id = ol.order_id
WHERE
-- 筛选2021年12月的订单
o.create_on >= '2021-12-01'
AND o.create_on < '2022-01-01'
-- 筛选包含A、B、C任意一个商品的行
AND ol.product_id IN ('A', 'B', 'C')
GROUP BY o.id, o.create_on
-- 确保订单同时包含这3个不同的商品
HAVING COUNT(DISTINCT ol.product_id) = 3;
17 . 如果有一张订单表,查询量最大的查询条件有两种:
- 根据某个创建人和创建时间(范围)查询。
- 根据某个类型和创建时间(范围)查询。
建议如何建立索引?
建立两个联合索引,并遵循"等值查询字段在前,范围查询字段在后"的原则:
- 索引一 :
(创建人, 创建时间)- 索引二 :
(类型, 创建时间)理由:
- 最左匹配原则:将等值条件(创建人/类型)放在首位,范围条件(时间)放在末位,可以使索引效率最高。
- 覆盖查询:联合索引可提高过滤效率;若查询字段被索引完全覆盖,还可减少回表。
18 . @Resource、@Autowired、@Inject的区别?
| 特性 | @Autowired | @Resource | @Inject |
|---|---|---|---|
| 来源 | Spring 框架专用 | JSR-250 | JSR-330 |
| 默认匹配方式 | 按类型 (byType) | 先 byName,再 byType | 按类型 (byType) |
| 找不到对应 Bean | 报错(可设置 required=false) |
报错 | 报错 |
| 指定特定名称 | 配合 @Qualifier |
使用 name 属性 |
配合 @Named |
| 注入位置 | 构造器、字段、Setter、方法 | 字段、Setter | 构造器、字段、Setter |
19 . @Transactional可以用于private方法吗,为什么?
private 方法不能生效,因为 Spring AOP 无法代理 private 方法;
其不能被子类覆盖,因此代理对象无法拦截。
同类内部 this.xxx() 调用也会绕过代理,因此事务同样不生效。
20 . 支付宝等接口,要求请求中对字段做签名,目的是什么?常见的签名算法有哪些?
目的:
保证请求数据不被篡改 ,并证明请求方身份(只有持有密钥的一方才能生成有效签名)。
常见签名算法:
| 算法 | 说明 |
|---|---|
RSA2 / SHA256withRSA |
主流,支付宝等常用,私钥签名、公钥验签 |
RSA / SHA1withRSA |
旧版兼容,安全性弱于 RSA2 |
| HMAC-SHA256 | 双方共享密钥,常用于微信支付等 |
| MD5 | 早期接口(如旧版支付宝),已不推荐,不安全 |
| SM2 | 国密算法,政务/金融国产化场景 |
21 . 一种常见的攻击手段是huge body,比如请求报文是JSON,但构造一个很大的JSON字符串,几百M甚至几个G,解析JSON会把CPU干爆,甚至把内存用光。在Java体系下(Java Servlet、使用Spriing或不使用),在哪一层怎么解决这个问题。
huge body 应采用分层治理,越靠前拦截越好:
- 网关 / Nginx 层(优先级最高)
限制请求体大小,例如:
client_max_body_size 10m;
超限直接返回 413,避免请求进入 Java 应用。- 容器层(Tomcat/Jetty/Undertow)
限制 HTTP 请求体大小,例如 Tomcat:
maxPostSize="10485760"
防止超大 body 被 Servlet 容器接收。- 应用层(Spring / Servlet)
避免 @RequestBody 直接全量反序列化大 JSON。
可先检查 Content-Length,
或使用 Jackson JsonParser 流式解析(Streaming API),边读边处理,避免一次性占用大量内存。- 安全治理
结合限流、超时、熔断,防止 DoS 攻击持续消耗 CPU 与内存。
22 . 把用户的密码明文存储到数据库里是不好的,那么如何存储比较好?
采用 加盐哈希 (Salted Hashing) 存储,具体做法如下:
- 使用强散列算法 :不要使用 MD5 或 SHA-1(易破解),推荐使用 Argon2 、BCrypt 或 SCrypt。
- 添加随机盐值 (Salt):为每个用户生成一个唯一的、随机的"盐",与密码拼接后再哈希。防止彩虹表攻击。
- 增加工作因子 (Work Factor):通过调整迭代次数增加计算耗时,抵御暴力破解。
- 禁止可逆加密:密码应单向加密,不可被还原。
某些高安全场景还会增加 pepper(系统级密钥),保存在服务端配置或密钥系统中,而不是数据库。