一、常用工具与集合操作
1.1 Arrays.asList()
- 作用:将数组转换为
List。 注意:返回的
List是固定大小的,不支持add/remove操作。List<String> list = Arrays.asList("a", "b", "c");
1.2 CollUtil.toMap()(Hutool 工具)
- 把一个
List转换成Map。 - 参数1:要转换的列表。
- 参数2:
key的生成方式(如Stock::getSkuId)。 参数3:
value的生成方式,Function.identity()表示用对象本身作为value。Map<Long, Stock> map = CollUtil.toMap(stocks, Stock::getSkuId, Function.identity());
1.3 IdentityHashMap
- 比较冷门,判断 key 是否相同依据 对象的引用地址(
==),而不是equals()/hashCode()。
二、数据查询策略
2.1 联表查询 vs 分开查 + Map 组装
| 方式 | 适用场景 |
|---|---|
| 联表查询 | 数据量不大、关联简单、不需要对查出的用户数据做额外处理 |
| 分开查 + Map 组装 | 数据分布在不同的服务器、微服务架构,或者需要灵活组装 |
三、Java 集合框架
3.1 集合体系概览
Collection是除Map外所有集合类的根接口。java.util包主要提供三种类型的集合:List、Set、Map。- 统一使用
Iterator遍历集合。
3.2 List
ArrayList:基于动态数组,内存连续。LinkedList:基于双向链表,节点有前后指针。List允许添加null。使用
List.of()创建不可变 List(不支持null元素)。List<Integer> list = List.of(1, 2, 5);Iterator遍历:boolean hasNext()判断是否有下一个元素。E next()返回下一个元素。
boolean contains(Object o)判断是否包含某元素。int indexOf(Object o)返回元素索引,不存在返回-1。重要:使用
contains()、indexOf()等方法时,元素类必须正确覆写equals()。String、Integer等标准库类已经正确实现了equals(),所以可以直接使用。
3.3 编写 equals() 方法
- 理论原则:自反性、对称性、传递性、一致性。
实践写法(直接套用):
- 先确定实例相等的逻辑(哪些字段相等就算相等)。
- 用
instanceof判断类型,类型匹配后再比较字段。 使用
Objects.equals()比较引用类型字段,避免空指针。public boolean equals(Object o) { if (o instanceof Person p) { return Objects.equals(this.name, p.name) && this.age == p.age; } return false; }
3.4 Map
- Map 遍历是无序的。
- 底层是哈希表,通过哈希函数将 key 转换成数组下标,直接定位,查找速度极快 O(1)。
- 作为 key 的对象必须正确覆写
equals()和hashCode(),相等的 key 调用equals必须返回true。
3.5 Queue 队列
- 实现先进先出(FIFO)的数据结构。
常用方法:
add()/offer():添加元素到队尾。remove()/poll():从队首获取并删除元素。element()/peek():从队首获取但不删除。
- 避免将
null添加到队列。
四、编码与安全
4.1 URL 编码
- 使用
java.net.URLEncoder对任意字符串进行 URL 编码。
4.2 哈希算法
- 目的:验证原始数据是否被篡改。
MessageDigest常见用途:- 计算文件校验和
- 实现文件秒传
- 存储用户密码
- 防止数据重复
- 生成 API 签名
4.3 AES 加密
- 最可靠的 AES 加密模式:AES-GCM。
五、多线程与并发
5.1 线程基础
守护线程(Daemon)
- JVM 退出时不等待守护线程结束,直接强制终止。
- 非守护线程(用户线程)结束时 JVM 才会退出。
- 设置方法:在
start()前调用setDaemon(true)。 - 核心业务线程不能设置为守护线程。
- 典型用途:垃圾回收、日志监控、心跳检测。
t.join()- 让当前线程等待
t线程执行结束。 - 生产环境禁用,容易造成阻塞,推荐用
Future等异步方式。
- 让当前线程等待
Thread.sleep(long millis)- 静态方法,哪个线程调用,哪个线程睡觉。
线程中断
- 线程结束即
run()方法执行完毕。 - 目标线程通过检查
isInterrupted()标志判断自身是否已被中断。
- 线程结束即
volatile与内存可见性- 变量的值保存在主内存,线程访问时会拷贝到工作内存。
- 修改后的值何时回写主内存不确定,因此会出现可见性问题。
synchronized不能修饰变量,要保证变量跨线程可见性,使用volatile。- 注意:
volatile只能保证可见性和禁止指令重排,不保证原子性。
5.2 同步机制
synchronized
- Java 语言层面语法,不需要手动释放锁(退出同步块自动释放)。
- 修饰方法时,锁对象是
this(实例方法)或类对象(静态方法)。 - 配合
wait()/notifyAll()实现线程等待/唤醒(了解即可,实际多用Lock体系)。
ReentrantLock
- Java 代码实现的锁,必须在
finally中手动释放。 标准用法:
private final Lock lock = new ReentrantLock(); lock.lock(); try { // 同步代码 } finally { lock.unlock(); }- 配合
Condition的await()/signalAll()实现等待/通知。
- Java 代码实现的锁,必须在
ReadWriteLock
- 提升读多写少场景的并发性能。
规则:
- 读-读 可共存
- 写-写 互斥
- 读-写 互斥(要么全是读,要么只有一个写)
实现:
private final ReadWriteLock rwlock = new ReentrantReadWriteLock(); private final Lock rlock = rwlock.readLock(); private final Lock wlock = rwlock.writeLock(); // 写操作 wlock.lock(); try { ... } finally { wlock.unlock(); } // 读操作 rlock.lock(); try { ... } finally { rlock.unlock(); }- 注意:读锁是悲观锁,频繁读可能导致写线程饥饿。
StampedLock
- 三种模式:写锁、悲观读锁、乐观读。
- 乐观读
tryOptimisticRead()完全不阻塞写线程,先获取版本号再读数据,读完后验证版本号有无变化,变化则重试或升级为悲观读。 - 不可重入(一个线程持有写锁再尝试获取读锁会死锁)。
Semaphore
- 信号量,限制同时访问某个资源的线程数量。
用法:
final Semaphore semaphore = new Semaphore(3); // 最多3个许可 public String access() throws Exception { semaphore.acquire(); // 获取许可,可能阻塞 try { // 业务逻辑 } finally { semaphore.release(); // 释放许可 } }tryAcquire(long timeout, TimeUnit unit)可指定等待时间。
5.3 线程安全集合
| 接口 | 非线程安全 | 线程安全替代 |
|---|---|---|
| List | ArrayList | CopyOnWriteArrayList |
| Map | HashMap | ConcurrentHashMap |
| Set | HashSet / TreeSet | CopyOnWriteArraySet |
| Queue | ArrayDeque / LinkedList | ArrayBlockingQueue / LinkedBlockingQueue |
| Deque | ArrayDeque / LinkedList | LinkedBlockingDeque |
5.4 线程池
ExecutorService
经典用法:
ExecutorService executor = Executors.newFixedThreadPool(3); executor.submit(task1); ... executor.shutdown(); // 等待任务完成后关闭
常用线程池及底层原理:
FixedThreadPool:核心线程数 = 最大线程数 + 无界队列CachedThreadPool:核心线程数 0,最大线程数无限 + 同步移交队列SingleThreadExecutor:核心和最大线程数均为 1 + 无界队列ScheduledThreadPool:ScheduledThreadPoolExecutor,支持定时调度
生产推荐:直接使用
ThreadPoolExecutor定制参数,队列必须有界。public ThreadPoolExecutor( int corePoolSize, // CPU密集:n+1 IO密集:2n int maximumPoolSize, // ≥ corePoolSize long keepAliveTime, // 非核心线程空闲存活时间,常用30-60s TimeUnit unit, // 时间单位 BlockingQueue<Runnable> workQueue, // 有界队列,如 ArrayBlockingQueue ThreadFactory threadFactory, // 线程工厂 RejectedExecutionHandler handler // 拒绝策略 )拒绝策略
策略 行为 AbortPolicy(默认) 丢弃任务,抛 RejectedExecutionExceptionCallerRunsPolicy 由提交任务的线程自行执行,可减缓提交速度(推荐) DiscardPolicy 静默丢弃任务,不抛异常 DiscardOldestPolicy 丢弃队列最老的任务,重新提交当前任务 - 推荐配置:
CallerRunsPolicy+ 监控告警。 注意事项
- 创建线程只有一种方式:
new Thread().start()。 - 生产环境只用线程池提交任务,禁止手动
new Thread()。 for循环中调用t.start()会立即返回,不会等待线程run()执行完。
- 创建线程只有一种方式:
5.5 Future 与异步编程
Callable与Runnable的区别:Callable有返回值,可抛出异常。提交
Callable返回Future<V>。get():获取结果(可能阻塞)get(long timeout, TimeUnit unit):限时获取cancel(boolean mayInterruptIfRunning):取消任务isDone():判断任务是否完成
CompletableFuture
- 实现
Future和CompletionStage,支持函数式异步编程。 - 方法引用示例:
Main::fetchPrice等价于() -> fetchPrice(),常用在CompletableFuture.supplyAsync(Main::fetchPrice)中。
- 实现
六、JDBC 简介
- 各数据库厂商使用统一接口,Java 代码无需针对不同数据库分别开发。
Maven 依赖:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> <scope>runtime</scope> <!-- 运行时才需要此包 --> </dependency>- 建立连接:
DriverManager.getConnection()(自动扫描 classpath 中的驱动)。 - 重要提示:数据库自身就是并发专家,不要用 Java 层锁去干涉数据库操作,除非操作对象是内存数据。
评论