跳到主要内容

常见 Java 问题与排错思路

理解常见问题和排错方法是解决实际开发问题的关键。本章将介绍常见 Java 问题与排错思路。

NullPointerException / ClassCastException

NullPointerException

空指针异常是最常见的异常。

// 问题代码
String str = null;
int length = str.length(); // NullPointerException

// 解决方法
// 1. 检查 null
if (str != null) {
int length = str.length();
}

// 2. 使用 Optional
Optional<String> optional = Optional.ofNullable(str);
optional.ifPresent(s -> System.out.println(s.length()));

// 3. 使用 Objects.requireNonNull
String str2 = Objects.requireNonNull(str, "字符串不能为null");

ClassCastException

类型转换异常

// 问题代码
Object obj = "Hello";
Integer num = (Integer) obj; // ClassCastException

// 解决方法
// 1. 使用 instanceof 检查
if (obj instanceof Integer) {
Integer num = (Integer) obj;
}

// 2. 使用泛型
List<String> list = new ArrayList<>();
// 编译时类型安全

并发问题与死锁

并发问题

// 问题:线程不安全
public class Counter {
private int count = 0;

public void increment() {
count++; // 非原子操作
}
}

// 解决方法
// 1. 使用 synchronized
public synchronized void increment() {
count++;
}

// 2. 使用原子类
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}

死锁

// 问题:死锁
Thread thread1 = new Thread(() -> {
synchronized (lock1) {
synchronized (lock2) { }
}
});

Thread thread2 = new Thread(() -> {
synchronized (lock2) {
synchronized (lock1) { } // 可能死锁
}
});

// 解决方法:按相同顺序获取锁
Thread thread2 = new Thread(() -> {
synchronized (lock1) { // 相同顺序
synchronized (lock2) { }
}
});

调试与日志分析

使用调试器

// 设置断点
public void processData(String data) {
int length = data.length(); // 断点
String processed = process(data);
System.out.println(processed);
}

日志分析

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DebugExample {
private static final Logger logger = LoggerFactory.getLogger(DebugExample.class);

public void processData(String data) {
logger.debug("开始处理数据:{}", data);
try {
String result = process(data);
logger.info("处理成功:{}", result);
} catch (Exception e) {
logger.error("处理失败:数据={}", data, e);
}
}
}

性能瓶颈排查

使用性能分析工具

# 使用 jstack 查看线程
jstack <pid>

# 使用 jmap 查看内存
jmap -heap <pid>

# 使用 jstat 查看 GC
jstat -gc <pid> 1000

代码分析

// 使用 System.currentTimeMillis() 测量时间
long start = System.currentTimeMillis();
processData();
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + "ms");

// 使用 System.nanoTime() 更精确
long start = System.nanoTime();
processData();
long end = System.nanoTime();
System.out.println("耗时:" + (end - start) / 1_000_000 + "ms");

小结

常见 Java 问题与排错要点:

  • NullPointerException:检查 null,使用 Optional
  • ClassCastException:使用 instanceof 检查
  • 并发问题:使用同步机制,使用原子类
  • 死锁:按相同顺序获取锁
  • 调试:使用调试器,分析日志
  • 性能:使用性能分析工具,测量代码执行时间

关键要点

  • 预防空指针异常
  • 正确处理类型转换
  • 注意并发安全
  • 避免死锁
  • 使用调试工具
  • 分析性能瓶颈

理解了问题排错,你就能快速解决开发中的问题。恭喜你完成了第十一部分的学习!