缺陷 & 规避

  1. NPE 空指针异常(Null Pointer Exception)

1)使用注解 @NotNull 和 @Nullable

2)用 Optional 处理链式调用

3)用 Objects.equals(a,b) 代替 a.equals(b),能够避免任意对象为 null 时的 NPE。

4)使用空对象模式,空对像模式通过一个特殊对象代替不存在的情况,代表对象不存在时的默认行为模式。例如:用 Empty List 代替 null,EmptyList 能够正常遍历:Collections.emptyList();

  1. 线程安全

JVM 的内存模型十分复杂,难以理解, <<Java 并发编程实战>>告诉我们,除非你对 JVM 的线程安全原理十分熟悉,否则应该严格遵守基本的 Java 线程安全规则,使用 Java 内置的线程安全的类及关键字。

熟练使用线程安全类 ConcurrentHashMap

java 复制代码
public class ConcurrentHashMapExample {
    private Map<String, String> map = new ConcurrentHashMap<>();

    public void append(String key, String suffix) {
        // 使用 computeIfPresent 原子操作
        map.computeIfPresent(key, (k, v) -> v + suffix);
    }
}

保证变更的原子性

使用不可变对象

**正确性优先于性能:**不要因为担心性能问题而放弃使用 synchronized,volatile 等关键字,或者采用一些非常规写法。

多线程环境中的volatile:在多线程编程中,每个线程可以拥有变量的独立拷贝,这可能导致数据不一致的问题。声明一个变量为volatile可以保证所有线程看到的都是主存中的最新版本,从而在一定程度上解决了线程间可见性的问题。尽管volatile不保证复合操作的原子性,它通过内存屏障来防止指令重排序,确保了操作的顺序性。

Java中的volatile:在Java中,volatile不仅保证了单个变量的可见性,还可以用于实现轻量级的同步,特别是在使用双重检查锁定模式(Double-Checked Locking)设计单例模式时。volatile关键字通过内存屏障来防止指令重排,这对于维持对象引用的可见性和一致性至关重要。

线程池使用不当

java 复制代码
public class ThreadPoolExample {

    // 没有任何限制的线程池, 使用起来很方便, 但当一波请求高峰到达时, 可能会创建大量线程, 导致系统崩溃
    private static Executor executor = Executors.newCachedThreadPool();

}

手动创建线程池

java 复制代码
public class ManualCreateThreadPool {

    // 手动创建资源有限的线程池
    private Executor executor = new ThreadPoolExecutor(10, 10, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<>(1000),
        new ThreadFactoryBuilder().setNameFormat("work-%d").build());
}

通过 AOP 统一异常处理

  1. 避免未知异常抛给调用方, 将未知异常转为 Result 或者通用异常类型

  2. 统一异常日志的打印和监控

处理 Checked Exception

Checked Exception 是在编译期要求必须处理的异常,也就是非 RuntimeException 类型的异常,但 Java Checked 的异常给接口的调用者造成了一定的负担,导致异常声明层层传递,如果顶层能够处理该异常,我们可以通过 lombok 的 @SneakyThrows 注解规避 Checked exception。

Try catch 线程逻辑

特殊异常的处理

InterruptedException 一般是上层调度者主动发起的中断信号,例如某个任务执行超时,那么调度者通过将线程置为 interuppted 来中断任务,对于这类异常我们不应该在 catch 之后忽略,应该向上抛出或者将当前线程置为 interuppted。

避免 catch Error

不要吞并 Error,Error 设计本身就是区别于异常,一般不应该被 catch,更不能被吞掉。举个例子,OOM 有可能发生在任意代码位置,如果吞并 Error,让程序继续运行,那么以下代码的 start 和 end 就无法保证一致性。

内存/资源泄漏

虽然 JVM 有垃圾回收机制,但并不意味着内存泄漏问题不存在,一般内存泄漏发生在在长时间持对象无法释放的场景,比如静态集合,内存中的缓存数据,运行时类生成技术等。

Spring 事务问题,注意事务注解失效的场景

当打上 @Transactional 注解的 spring bean 被注入时,spring 会用事务代理过的对象代替原对象注入。

但是如果注解方法被同一个对象中的另一个方法里面调用,则该调用无法被 Spring 干预,自然事务注解也就失效了。

java 复制代码
 @Component
 public class TransactionNotWork {
 ​
     public void doTheThing() {
         actuallyDoTheThing();
     }
 ​
     @Transactional
     public void actuallyDoTheThing() {
     }
 }
相关推荐
Theodore_102217 分钟前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
zy张起灵1 小时前
48v72v-100v转12v 10A大功率转换电源方案CSM3100SK
经验分享·嵌入式硬件·硬件工程
冰帝海岸1 小时前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象2 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了2 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
----云烟----2 小时前
QT中QString类的各种使用
开发语言·qt
lsx2024062 小时前
SQL SELECT 语句:基础与进阶应用
开发语言
小二·2 小时前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic3 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
向宇it3 小时前
【unity小技巧】unity 什么是反射?反射的作用?反射的使用场景?反射的缺点?常用的反射操作?反射常见示例
开发语言·游戏·unity·c#·游戏引擎