Java中常见的异常类的作用该如何理解?一文详解!
1、Java中IllegalMonitorStateException的作用该如何理解?
IllegalMonitorStateException 是 Java 中的一个运行时异常(RuntimeException),
它的意思是:线程试图在没有拥有“监视器锁”的情况下就去操作某个对象的监视器(monitor),
比如调用了 wait()、notify() 或 notifyAll() 方法。
✅ 什么是监视器锁(monitor)?
Java 中每一个对象都有一个监视器锁(monitor lock),通常和 synchronized 关键字一起使用。当一个线程想要调用 wait()、notify() 或 notifyAll() 方法时,它必须已经持有这个对象的监视器锁,否则就会抛出 IllegalMonitorStateException。
❌ 举个错误示例(会抛异常):
public class Example { public static void main(String[] args) { Object lock = new Object(); // 没有在 synchronized 块里调用 wait(),会抛出异常 try { lock.wait(); // ❌ IllegalMonitorStateException } catch (InterruptedException e) { e.printStackTrace(); } } }
错误点:线程没有获得 lock 对象的锁,就调用了 wait()。
✅ 正确写法:
public class Example { public static void main(String[] args) { Object lock = new Object(); synchronized (lock) { try { lock.wait(); // ✅ 现在我们已经持有 lock 的监视器锁 } catch (InterruptedException e) { e.printStackTrace(); } } } }
在 synchronized(lock) 块中调用 lock.wait() 就是正确的,因为这时线程已经拥有了 lock 的锁。
同理,如果你写的是 lock.notify() 或 lock.notifyAll(),也必须在同步块中:
synchronized (lock) { lock.notify(); // 必须在拥有锁的前提下 }
总结一句话:
如果你在没有
synchronized的上下文中调用了wait()、notify()或notifyAll(),就会抛出IllegalMonitorStateException。
2、IllegalStateException 这个该如何理解呢?
IllegalStateException 是 Java 中的一个常见运行时异常,意思是:
“对象当前的状态不适合执行这个操作。”
它的英文解释非常直白:The Java environment or Java application is not in an appropriate state for the requested operation.
🧠 通俗理解
某个对象处于“不合适的状态”,而你强行调用了某个方法或执行了某种行为,Java 就抛出这个异常来提醒你:“现在不能这么干。”
🔍 常见触发场景
1. 集合迭代器中调用 remove() 错误
List<String> list = new ArrayList<>(); list.add("A"); list.add("B"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String item = iterator.next(); list.remove(item); // ❌ IllegalStateException }
原因:应该使用 iterator.remove(),而不是直接修改集合。
✅ 正确写法:
Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String item = iterator.next(); iterator.remove(); // OK }
2. 线程池已关闭,再提交任务
ExecutorService executor = Executors.newFixedThreadPool(2); executor.shutdown(); executor.submit(() -> System.out.println("任务")); // ❌ IllegalStateException
原因:线程池关闭后不能再提交任务。
3. Java Stream 已经消费后再次使用
Stream<String> stream = Stream.of("a", "b", "c");
stream.forEach(System.out::println);
stream.count(); // ❌ IllegalStateException: stream has already been operated upon or closed
流(Stream)只能消费一次。
4. ViewPager 或 RecyclerView 状态不对时操作(Android 开发中)
viewPager.setCurrentItem(-1); // ❌ 非法页码
✅ 如何避免
-
在调用方法前,检查对象状态是否有效。
-
阅读方法文档(IDE 会提示),有些方法明确要求在某些条件下调用。
-
结合 if 判断、日志输出、调试工具提前发现状态异常。
🧠 小总结一句话:
IllegalStateException就是告诉你:“虽然语法是对的,但在 当前状态 下,这个操作是 非法的、不合理的。”
3、IllegalThreadStateException该如何理解呢?
IllegalThreadStateException 是 Java 中的运行时异常(RuntimeException),意思是:
线程的当前状态不允许你执行这个操作。
🧠 通俗理解
你试图对一个线程做“当前状态下不合法”的操作,比如:
-
对一个已经启动的线程再次调用
.start(); -
或者线程状态不允许执行当前操作。
🔥 最常见的触发场景
✅ 场景 1:线程重复启动
public class MyThread extends Thread { public void run() { System.out.println("线程运行中..."); } public static void main(String[] args) { MyThread t = new MyThread(); t.start(); // 第一次启动 OK t.start(); // ❌ 第二次启动就会抛 IllegalThreadStateException } }
错误信息类似:
Exception in thread "main" java.lang.IllegalThreadStateException
at java.base/java.lang.Thread.start(Thread.java:793)
❗ Java 中的线程只能启动一次。一旦调用过 start(),就不能再启动它了。
✅ 场景 2:Thread 状态判断和操作冲突(少见)
某些使用低层 API 的时候,例如自己操作线程状态,或者用一些 Thread API,比如 Thread.setDaemon():
Thread t = new Thread(() -> { System.out.println("工作线程"); }); t.start(); t.setDaemon(true); // ❌ 线程已经启动,再设置为守护线程会抛异常
setDaemon(true) 必须在 start() 之前调用。
✅ 如何避免
-
线程对象只能
start()一次,不要重复使用同一个 Thread 实例; -
状态相关操作(如设置守护线程、优先级)必须在
start()前调用; -
可以通过
.isAlive()或自己维护状态判断是否启动过。
🧠 总结一句话:
IllegalThreadStateException是告诉你:“你对这个线程干的事不合适,它当前的状态不允许你这么做。”
3、SecurityException 该如何理解?
File file = new File("/etc/passwd"); Scanner scanner = new Scanner(file); // ❌ 可能被 SecurityManager 禁止
如果安全策略中禁止访问这个路径,就会抛出异常。
2. 读取系统属性
String secret = System.getProperty("user.home"); // ❌ 若被禁止访问则抛出 SecurityException
3. 使用反射操作受限类
Class<?> clazz = Class.forName("java.lang.System");
Field field = clazz.getDeclaredField("security"); // ❌ 试图访问核心类字段
field.setAccessible(true); // 可能触发 SecurityException
4. 启动或停止线程组中的线程
ThreadGroup group = new ThreadGroup("test"); Thread t = new Thread(group, () -> {}); t.start(); group.stop(); // ❌ 被视为危险操作
5. 沙箱环境中执行高危代码
例如 Applet、Android、某些云环境下的受限 JVM 都会启用 SecurityManager,限制你运行 exec()、写文件、开新线程等行为。
Runtime.getRuntime().exec("rm -rf /"); // ❌ 会被拦截
✅ 如何避免
-
避免在受限环境中执行危险操作;
-
检查是否有
SecurityManager安装; -
如果你控制了运行环境,可以考虑关闭
SecurityManager(Java 17+ 开始已废弃)。
❗ 注意:Java 17 起废弃了 SecurityManager
System.setSecurityManager(new SecurityManager()); // Java 17+ 不再推荐使用
现代 Java 推荐使用模块化、容器、虚拟线程等方式替代传统的 SecurityManager 安全沙箱。
🧠 总结一句话:
SecurityException是在告诉你:“这件事不安全,Java 的安全策略不让你干。”
4、UnsupportedOperationException又该如何理解呢?
ChatGPT said:
List<String> list = Arrays.asList("a", "b", "c");
list.add("d"); // ❌ UnsupportedOperationException
原因:Arrays.asList() 返回的是一个固定大小的列表,不能添加或删除元素。
✅ 正确写法(用可变的列表):
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c")); list.add("d"); // OK
✅ 场景 2:使用 List.of() / Map.of() 得到的是不可变集合(Java 9+)
List<String> list = List.of("a", "b");
list.set(0, "x"); // ❌ UnsupportedOperationException(不可变)
✅ 场景 3:你自己继承类/接口时没有实现某个方法(默认抛异常)
比如 Java 中有的抽象类默认实现是抛出 UnsupportedOperationException:
public class MyList extends AbstractList<String> { public String get(int index) { return "hello"; } public int size() { return 1; } public void add(String value) { // 没实现 add() 会默认抛 UnsupportedOperationException throw new UnsupportedOperationException(); } }
✅ 如何避免
-
明确知道你使用的集合对象是不是 只读的;
-
不要对不可变集合执行增删操作;
-
如果你继承类或接口,需要自己实现所有必要的方法,避免默认行为抛出异常;
-
IDE 有时会标注 “此方法抛 UnsupportedOperationException”,注意文档提示。
🧠 总结一句话:
UnsupportedOperationException就是你调用了一个“语法合法,但当前对象不支持”的操作,Java 不让你干。
5、throws/throw 的区分,该如何理解?
throw 和 throws 虽然长得像,但用途完全不同。下面我用简明扼要的方式给你讲清楚两者的区别和用法,并配上代码示例。
🔍 一句话区分
| 关键词 | 用途 | 出现位置 | 意思 |
|---|---|---|---|
throw |
实际抛出一个异常对象 | 方法内部 | “我要抛异常了!” |
throws |
声明这个方法可能抛出哪些异常 | 方法签名 | “我可能会抛异常,你处理一下。” |
✅ throw 示例(抛出异常)
public class Demo { public static void checkAge(int age) { if (age < 18) { throw new IllegalArgumentException("年龄必须 >= 18"); } } public static void main(String[] args) { checkAge(15); // ❌ 抛出 IllegalArgumentException } }
-
throw new IllegalArgumentException(...)是真的抛出了一个异常对象。 -
throw后面跟的是一个异常对象(必须是Throwable子类)。
✅ throws 示例(声明异常)
public class Demo { // 声明这个方法“可能”抛出异常,谁调用谁负责处理 public static void readFile() throws IOException { throw new IOException("读取文件失败"); } public static void main(String[] args) { try { readFile(); // ⚠️ 必须处理异常(try-catch 或 throws) } catch (IOException e) { System.out.println("捕获异常:" + e.getMessage()); } } }
-
throws IOException是在声明:这个方法内部可能会抛出 IOException。 -
Java 编译器会强制调用者处理它(用 try-catch 或继续 throws)。
🔥 综合例子:一起用
public class Demo { // 声明可能抛出异常 public static void divide(int a, int b) throws ArithmeticException { if (b == 0) { throw new ArithmeticException("除数不能为0"); // 实际抛出 } System.out.println(a / b); } public static void main(String[] args) { divide(10, 0); // ❌ 抛出异常 } }
✅ 总结口诀
throw 是动作,真的抛; throws 是声明,告知他,可能会抛。 throw 后接异常对象, throws 后写异常类。

浙公网安备 33010602011771号