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 该如何理解?

SecurityException 是 Java 中的一个运行时异常(RuntimeException),表示:

你试图执行一个违反安全策略的操作,而 Java 的安全管理器(SecurityManager)阻止了你。


🔒 背后机制:SecurityManager

Java 提供了一个 SecurityManager 类,它允许系统管理员或平台(比如浏览器、沙箱)控制 Java 程序能不能访问某些系统资源,例如:

  • 文件系统

  • 网络连接

  • 线程管理

  • 运行外部程序(如 Runtime.exec()

  • 系统属性读取(如 System.getProperty()

如果你执行了某个被禁止的操作,SecurityManager 会抛出 SecurityException 来阻止它。


🔍 常见触发场景

1. 访问受限的文件或目录

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:

UnsupportedOperationException 是 Java 中的一个常见运行时异常(RuntimeException),表示:

你调用了某个操作,但该对象不支持这个操作。


🧠 通俗解释

这是 Java 在告诉你:

“这个方法是合法的,但我这个对象 就是不支持你要干的事儿,你不能这么用我!”


🔥 常见触发场景

✅ 场景 1:使用 Arrays.asList() 得到的列表不能增删元素

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 的区分,该如何理解?

throwthrows 虽然长得像,但用途完全不同。下面我用简明扼要的方式给你讲清楚两者的区别和用法,并配上代码示例。


🔍 一句话区分

关键词用途出现位置意思
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 后写异常类。

 

posted @ 2025-07-20 13:03  AlphaGeek  阅读(36)  评论(0)    收藏  举报