java中的异常(Exception)

异常

错误的情况下,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用法总结

  1. 不管有没有异常,finally中的代码都会执行

  2. 当try,catch中有return时,finally中的代码依然会运行

  3. finally是在return后面的表达式计算之后执行的(会先保存起来),之后执行finally,不管finally干了干了什么,都不会影响之前保存的值

    • 如果是基本类型,在栈中,必然不会改变

    • 如果是引用类型,在堆中,其只是一个地址,对象改变了,会影响,因为只是地址不变

    • 除非finally里面有return语句,则执行finally的时候就直接返回了,没机会回到try,catch里面返回之前保存的值了

  4. 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子类
序号异常名称异常描述
1ArithmeticException算术异常,比如整数除0等
2SecurityException安全性异常
3IllegalArgumentException非法参数异常
4ArrayStoreException数组中包含不兼容的值抛出的异常
5ArrayIndexOutOfBoundsException数组索引越界异常
6NullPointerException空指针异常
  • IOException
序号异常名称异常描述
1IOException操作输入流和输出流可能出现的异常
2EOFException文件已结束异常
3FileNotFoundException文件未找到异常
  • 其他
序号异常名称异常描述1
1ClassCastException类型转换异常类
2ArrayStoreException数组中包含不兼容的值抛出的异常
3SQLException操作数据库异常类
4NoSuchFieldException字段未找到异常
5NoSuchMethodException方法未找到异常
6NumberFormatException字符串转换为数字抛出的异常
7StringIndexOutOfBoundsException字符串索引超出范围抛出的异常
8IllegalAccessException不允许访问某类异常
9InstantiationException当程序视图使用Class类中的newInstancce()方法创建一个类的示例,而制定类实例无法被实例化的时候
10ClassNotFoundException找不到异常当应用试图根据字符串形式的类名构造类,但是遍历照后找不到对应的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捕获另一个线程的异常

解决方案

  1. 在每个线程内部run()方法内部通过try catchc捕获当前线程的异常

    每个线程都需要编写重复的try catch代码

  2. 使用线程异常处理器 UncaughExceptionHandler

    1. 给所有线程设置统一的异常处理器

      • 通过Thread.setDefaultUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler()); 来设置一个全局默认的异常处理器
    2. 给每个线程设置特定的异常处理器

      • 线程内部有一个属性

        private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;

    3. 给线程组设置异常处理器

    4. 给线程池设置异常处理器

      1. 因为线程也是通过new Thread()创建的线程,思想和上面两种一致
      2. 注意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);
    }
}
  1. 全局设置
/**
 * @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();
    }
}
  1. 指定线程设置特定异常处理器
/**
 * @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();
    }
}
  1. 给线程组设置
/**
 * @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();
    }
}
  1. 线程池
/**
 * @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);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值