异常
错误的情况下,java中会阻止当前方法或作用域的情况,称之为异常
分类
Throwable 是可抛出异常的接口,一般由虚拟机抛出,或者运行的时候由持续抛出
Error: 持续无法处理的错误,一般是虚拟机的错误,通常有Virtual MachineError(虚拟机运行错误),当jvm耗完出现OutOfMemoryError
Exception: 持续本身可捕获并处理的异常
运行时异常(不受检异常): RuntimeException 白哦是JVM运行期间可能出现的错误,编译器不会检查此类异常,并且不要求处理异常,不如控制对象的引用NullPointException非运行时异常(受检异常): 编译器会检查此类异常,如果程序中出现此类异常,如IOException,必须对异常处理,要么使用try-catch要么使用throws抛出,否则编译不通过
异常处理
抛出异常: throw throws
捕获异常: try catch finally
抛出异常
throw new 异常类名(参数);
public class DemoThrow {
public static void main(String[] args) {
a();
}
public static int a(){
return b();
}
public static int b(){
return c();
}
public static int c(){
return div(4,0);
}
public static int div(int a, int b){
if( b== 0){
throw new ArithmeticException("异常信息,除数不能为0");
}
return a/b;
}
}
堆栈信息最上面是栈顶(抛出异常的地方) 最下面是栈底,最开始调用的地方
Exception in thread "main" java.lang.ArithmeticException: 异常信息,除数不能为0
at com.kalpa.exceptiontest.DemoThrow.div(DemoThrow.java:25)
at com.kalpa.exceptiontest.DemoThrow.c(DemoThrow.java:20)
at com.kalpa.exceptiontest.DemoThrow.b(DemoThrow.java:17)
at com.kalpa.exceptiontest.DemoThrow.a(DemoThrow.java:14)
at com.kalpa.exceptiontest.DemoThrow.main(DemoThrow.java:11)
Process finished with exit code 1
-
声明抛出异常 throws
在函数上声明此函数会抛出异常,让调用者来处理
public class DemoThrows { public static void main(String[] args) throws FileNotFoundException{ readFile(); } public static void readFile() throws FileNotFoundException { InputStream is = new FileInputStream("E:/iodemo/ch01.txt"); } }
-
try代码块
try {
… //监视代码执行过程,一旦返现异常则直接跳转至catch,
// 如果没有异常则直接跳转至finally
} catch (SomeException e) {
… //可选执行的代码块,如果没有任何异常发生则不会执行;
//如果发现异常则进行处理或向上抛出。
} finally {
… //必选执行的代码块,不管是否有异常发生,
// 即使发生内存溢出异常也会执行,通常用于处理善后清理工作。
}public class DemoTryCatch { public static void main(String[] args) { //捕获异常 try { //可能产生异常的代码 readFile(); } catch (FileNotFoundException e) { //异常的处理逻辑,将异常记录日志,异常封装后显示 System.out.println("系统找不到指定的路径"); } System.out.println("后续代码"); } public static void readFile() throws FileNotFoundException { InputStream is = new FileInputStream("E:/iodemo/ch01.txt"); } }
-
自定义异常
根据业务情况自定义异常类
-
可以提前预测错误情况,对错误处理更加友好
-
可以继承Exception RuntimeException
public class UserNotExistsException extends RuntimeException{ public UserNotExistsException() { super(); } public UserNotExistsException(String message) { super(message); } }
-
try,catch,finally用法总结
不管有没有异常,finally中的代码都会执行
当try,catch中有return时,finally中的代码依然会运行
finally是在return后面的表达式计算之后执行的(会先保存起来),之后执行finally,不管finally干了干了什么,都不会影响之前保存的值
如果是基本类型,在栈中,必然不会改变
如果是引用类型,在堆中,其只是一个地址,对象改变了,会影响,因为只是地址不变
除非finally里面有return语句,则执行finally的时候就直接返回了,没机会回到try,catch里面返回之前保存的值了
finally代码最好不包含return,程序会提前退出
-
执行顺序
public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(test()); } public static int test(){ int i = 1; try{ i++; System.out.println("try block, i = "+i); }catch(Exception e){ i ++; System.out.println("catch block i = "+i); }finally{ i = 10; System.out.println("finally block i = "+i); } return i; }
-
return在try catch中查看finally
public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(test()); } public static int test(){ int i = 1; try{ i++; System.out.println("try block, i = "+i); }catch(Exception e){ i ++; System.out.println("catch block i = "+i); }finally{ i = 10; System.out.println("finally block i = "+i); } return i; }
-
如果操作类型不是基本类型
public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(test().mmp("fuck")); } public static Person test(){ Person person = new Person(); try{ person.setShengao("172cm"+"---try block"); System.out.println("try block"); return person; }catch(Exception e){ person.setTizhong("100kg"); System.out.println("catch block"); return person; }finally{ person.setXingbie("女"); System.out.println("finally block "); } } public class Person { public String shengao; public String tizhong; public String xingbie; // getter,setter方法 public String mmp(String fuck){ System.out.println("person : mmp"); System.out.println("shengao:"+this.shengao); System.out.println("tizhong:"+this.tizhong); System.out.println("xingbie:"+this.xingbie); return fuck; } }
try block
finally block
person : mmp
shengao:172cm—try block
tizhong:null
xingbie:女
fuck
常见的异常类
- RuntimeException子类
序号 | 异常名称 | 异常描述 |
---|---|---|
1 | ArithmeticException | 算术异常,比如整数除0等 |
2 | SecurityException | 安全性异常 |
3 | IllegalArgumentException | 非法参数异常 |
4 | ArrayStoreException | 数组中包含不兼容的值抛出的异常 |
5 | ArrayIndexOutOfBoundsException | 数组索引越界异常 |
6 | NullPointerException | 空指针异常 |
- IOException
序号 | 异常名称 | 异常描述 |
---|---|---|
1 | IOException | 操作输入流和输出流可能出现的异常 |
2 | EOFException | 文件已结束异常 |
3 | FileNotFoundException | 文件未找到异常 |
- 其他
序号 | 异常名称 | 异常描述1 |
---|---|---|
1 | ClassCastException | 类型转换异常类 |
2 | ArrayStoreException | 数组中包含不兼容的值抛出的异常 |
3 | SQLException | 操作数据库异常类 |
4 | NoSuchFieldException | 字段未找到异常 |
5 | NoSuchMethodException | 方法未找到异常 |
6 | NumberFormatException | 字符串转换为数字抛出的异常 |
7 | StringIndexOutOfBoundsException | 字符串索引超出范围抛出的异常 |
8 | IllegalAccessException | 不允许访问某类异常 |
9 | InstantiationException | 当程序视图使用Class类中的newInstancce()方法创建一个类的示例,而制定类实例无法被实例化的时候 |
10 | ClassNotFoundException | 找不到异常当应用试图根据字符串形式的类名构造类,但是遍历照后找不到对应的class文件时候,抛出异常 |
-
为什么创建自己的异常
需要根据业务来更加清楚的说明异常的情况
-
应该抛出异常还是捕获异常
捕获并处理知道如何处理的异常,抛出不知道如何处理的异常
多线程中的异常
参考博客: https://blog.csdn.net/zl1zl2zl3/article/details/106988927
当利用多线程的时候,父线程不能捕获到子线程的异常,多个线程之间可以通过异常处理器
UncaughtExceptionHandler来处理那些未捕获的的异常
public class ExceptionInChildThread implements Runnable {
@Override
public void run() {
throw new RuntimeException("子线程发生了异常...");
}
/**
* 模拟子线程发生异常
*
* @throws InterruptedException
*/
private static void exceptionThread() throws InterruptedException {
new Thread(new ExceptionInChildThread()).start();
TimeUnit.MILLISECONDS.sleep(200L);
new Thread(new ExceptionInChildThread()).start();
TimeUnit.MILLISECONDS.sleep(200L);
new Thread(new ExceptionInChildThread()).start();
TimeUnit.MILLISECONDS.sleep(200L);
new Thread(new ExceptionInChildThread()).start();
TimeUnit.MILLISECONDS.sleep(200L);
}
/**
* 在主线程尝试通过try catch捕获异常
*/
private static void catchInMain() {
try {
exceptionThread();
} catch (Exception e) {
//无法捕获发生在其他线程中的异常
log.error("捕获到了异常?", e);
}
}
public static void main(String[] args) throws InterruptedException {
ExceptionInChildThread.catchInMain();
}
结果没有复活岛异常,不符合我们的预期
即: 无法在一个线程中通过try catch捕获另一个线程的异常
解决方案
-
在每个线程内部run()方法内部通过try catchc捕获当前线程的异常
每个线程都需要编写重复的try catch代码
-
使用线程异常处理器 UncaughExceptionHandler
-
给所有线程设置统一的异常处理器
- 通过Thread.setDefaultUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler()); 来设置一个全局默认的异常处理器
-
给每个线程设置特定的异常处理器
-
线程内部有一个属性
private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
-
-
给线程组设置异常处理器
-
给线程池设置异常处理器
- 因为线程也是通过new Thread()创建的线程,思想和上面两种一致
- 注意execute()和submit()方法对异常处理的不同
-
自定义线程异常处理类
public class CustomThreadUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomThreadUncaughtExceptionHandler.class);
@Override
public void uncaughtException(Thread t, Throwable e) {
LOGGER.error("捕获到线程发生的异常,线程信息:[{}]", JSON.toJSONString(t), e);
}
}
- 全局设置
/**
* @author 喜欢天文的pony站长
* Created on 2020/6/16.
*/
@Slf4j
public class ExceptionInChildThread implements Runnable {
@Override
public void run() {
throw new RuntimeException("子线程发生了异常...");
}
/**
* 模拟子线程发生异常
*
* @throws InterruptedException
*/
private static void exceptionThread() throws InterruptedException {
new Thread(new ExceptionInChildThread()).start();
TimeUnit.MILLISECONDS.sleep(200L);
new Thread(new ExceptionInChildThread()).start();
TimeUnit.MILLISECONDS.sleep(200L);
new Thread(new ExceptionInChildThread()).start();
TimeUnit.MILLISECONDS.sleep(200L);
new Thread(new ExceptionInChildThread()).start();
TimeUnit.MILLISECONDS.sleep(200L);
}
public static void main(String[] args) throws InterruptedException {
//设置全局的线程异常处理器
Thread.setDefaultUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler());
exceptionThread();
}
}
- 指定线程设置特定异常处理器
/**
* @author 喜欢天文的pony站长
* Created on 2020/6/16.
*/
@Slf4j
public class ExceptionInChildThread implements Runnable {
@Override
public void run() {
throw new RuntimeException("子线程发生了异常...");
}
/**
* 模拟子线程发生异常
*
* @throws InterruptedException
*/
private static void exceptionThread() throws InterruptedException {
Thread thread1 = new Thread(new ExceptionInChildThread());
//为指定线程设置特定的异常处理器
thread1.setUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler());
thread1.start();
TimeUnit.MILLISECONDS.sleep(200L);
new Thread(new ExceptionInChildThread()).start();
TimeUnit.MILLISECONDS.sleep(200L);
new Thread(new ExceptionInChildThread()).start();
TimeUnit.MILLISECONDS.sleep(200L);
new Thread(new ExceptionInChildThread()).start();
TimeUnit.MILLISECONDS.sleep(200L);
}
public static void main(String[] args) throws InterruptedException {
exceptionThread();
}
}
- 给线程组设置
/**
* @author futao
* @date 2020/6/20
*/
@Slf4j
public class ExceptionInThreadGroup implements Runnable {
@Override
public void run() {
throw new RuntimeException("线程任务发生了异常");
}
public static void main(String[] args) throws InterruptedException {
ThreadGroup threadGroup = new ThreadGroup("只知道抛出异常的线程组...") {
@Override
public void uncaughtException(Thread t, Throwable e) {
super.uncaughtException(t, e);
log.error("线程组内捕获到线程[{},{}]异常", t.getId(), t.getName(), e);
}
};
ExceptionInThreadGroup exceptionInThreadGroup = new ExceptionInThreadGroup();
new Thread(threadGroup, exceptionInThreadGroup, "线程1").start();
TimeUnit.MILLISECONDS.sleep(300L);
//优先获取绑定在thread对象上的异常处理器
Thread thread = new Thread(threadGroup, exceptionInThreadGroup, "线程2");
thread.setUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler());
thread.start();
TimeUnit.MILLISECONDS.sleep(300L);
new Thread(threadGroup, exceptionInThreadGroup, "线程3").start();
}
}
- 线程池
/**
* @author futao
* @date 2020/6/17
*/
public class CatchThreadPoolException {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2,
4,
1L,
TimeUnit.MINUTES,
new LinkedBlockingDeque<>(1024),
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
//设置线程异常处理器
thread.setUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler());
return thread;
}
}
);
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
throw new RuntimeException("execute()发生异常");
}
}
);
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
throw new RuntimeException("submit.run()发生异常");
}
});
threadPoolExecutor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
throw new RuntimeException("submit.call()发生异常");
}
});
threadPoolExecutor.shutdown();
}
}
并不符合,只捕获到了execute()提交的,没有捕获到submit()提交的异常
示例
外卖小哥送外卖
你作为外卖小哥,今天同时接到了10个订单,因为是晚上了,每个订单有20%概率配送失败,看看能不能成功
public class Demo8 {
public static void main(String[] args) {
for(int i = 0; i < 10 ; i++){
distrubution(i);
}
System.out.println("恭喜你,成功了送完四个外卖");
}
public static void distrubution(int i){
Random random = new Random();
if( Math.random() < 0.2){
throw new MyException("在第" + i + "次配送中,因为你的原因,配送失败了,扣钱!!!");
}
}
}
class MyException extends RuntimeException{
public MyException() {
}
public MyException(String message) {
super(message);
}
}
老师和学生
你是一个老师(未捕获异常处理器),有很多学生,学生们随时都可以捣乱(抛出异常),捕获异常,并教育他们
public class Demo9 {
public static void main(String[] args) throws InterruptedException {
ThreadGroup threadGroup = new ThreadGroup("学生们"){
@Override
public void uncaughtException(Thread t, Throwable e) {
// 如果有这个,还是会往上抛出,不让他继续
// super.uncaughtException(t, e);
System.out.println("线程:" + t.toString() + "发出错误信息" + e.getMessage());
}
};
Demo9 demo9 = new Demo9();
Student student = new Student();
new Thread(threadGroup, student, "线程1").start();
// 想让同学们随时捣乱,就不要延时让他们按顺序了
// TimeUnit.MILLISECONDS.sleep(300L);
Thread thread = new Thread(threadGroup, student, "线程2");
thread.setUncaughtExceptionHandler(new Teacher());
thread.start();
// TimeUnit.MILLISECONDS.sleep(300L);
for(int i =3;i<50;i++){
new Thread(threadGroup, student, "线程"+i).start();
}
}
}
class Teacher implements Thread.UncaughtExceptionHandler{
// private static final Logger log = LoggerFactory.getLogger()
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("线程:" + t.toString() + "发出错误信息" + e.getMessage());
}
}
class Student implements Runnable{
@Override
public void run() {
// 一节课40分钟,熊孩子们,每分钟都有20%概率出错, 而且被教训之后会好好听话,不会继续捣乱了
for (int i = 1; i < 40 ; i++ ){
if(Math.random() < 0.2){
String rel = Thread.currentThread().getName() + ",在第" + i + "分钟,开始捣乱了";
throw new StudentException(rel);
}
}
}
}
class StudentException extends RuntimeException{
public StudentException() {
super();
}
public StudentException(String message) {
super(message);
}
}