一文理解Java的异常机制是干啥的,怎么用(初级java必备)

一: try{}catch{}语句块
1  try{}catch{}    
    这种形式一般来说前者和后者不是因果关系
 
2.可以一个try跟几个catch
    在try中的异常一般是有因果关系的(类似于事务不可再分,要么全部成功要么全部失败,部分成功或失败是逻辑不通的), 这种情况不能使用第一种方式。
 
 3.其后跟finally(前两种情况
    也可以try{}finally{}(用在不想处理代码,但是却有必须要执行的代码,这种情况需要在方法的声明处抛出异常) 不加catch语句  但是finally不能单独使用!!
       
public class ExceptionTest {

    // try{}catch(){}的经典用法
    public static void test1() {
        FileInputStream fin = null;
        try {
            fin = new FileInputStream("c:\\aa.txt");
            int i = 0;
            while ((i = fin.read()) != -1) {
                // byte -> int -> char
                System.out.print((char) i); 
          //以上操作类似事务,不可再分,分则逻辑不通
            }
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
            //return  无法阻止finally代码的执行
           //System.exit(x);  异常或无条件退出Jvm是可以终止finally代码的执行的
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        finally {
            try {
                if (fin != null) {
                    // 流打开之后,必须要执行关闭的操作
                    fin.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
         test1();
    }
}

 

二:异常的分类

1.编译时异常(直接继承Exception),又叫不可查异常(unchecked exceptions)
    在方法的声明处抛出异常,要求程序员必须对其处理,要么try...catch...要么throws,直接throw +编译异常  这种情况不处理编译是不给通过的;
 
2.运行时异常(Error分支或继承RuntimeException),又叫可查异常(checked exceptions)
    在方法内部某判断语句的分支中直接throw 异常,编译期间没有影响,但是只要执行到该处就会抛出异常退出JVM,这种throw+运行时异常是可以编译通过的
    其中Error分支 是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如, Java虚拟机运行错误(Virtual MachineError), 当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
 
 
三:throw 的使用
  throw是将异常对象扔给调用者。以下是模拟JVM的栈的Demo。
        
public class Stack {
    private String[] arr;
    private int index = 0;
    public Stack(int size) {
        // 根据构造方法传递的int值,初始化数组对象.这样写比较灵活
        arr = new String[size];
    }

    /**
     * 压栈的方法 .每次压入一个数据.也就是把参数放到数组的某个位置.当栈满的时候通过异常来传递信息
     *
     * @param str
     * @throws StackException
     *             当栈满的时候通过异常来传递信息
     */
    public void push(String str) throws StackException {
        if (index < arr.length) {
            arr[index] = str;
            index++;
        } else {
            // 当栈满的时候,创建一个异常对象,然后throw出去,通过异常来传递信息
            StackException ex = new StackException("栈满了");
            throw ex;
        }
    }

    /**
     * 弹栈的方法.控制弹栈的数据是最后一个压栈的数据(也就是所谓的后进先出)
     *
     * @return .
     * @throws StackException 当栈空的时候抛出异常
     */
    public String pop() throws StackException {
        if (index > 0) {
            index--;
            String str = arr[index];
            arr[index] = null;
            return str;
        } else {
            throw new StackException("栈空了");
        }
    }
}
自定义异常类StackException(也是一般自定义异常的模板) )
 
public class StackException extends Exception {
   // 自定义异常如果继承Exception那么throw的时候编译器强制要求处理
    //如果继承RuntimeException则不会要求处理,这种异常很少用,一般是在用户的执行事务中出错来终止用户接下来的无效操作
    public StackException() {

    }
    public StackException(String msg) {
        super(msg);
    }

}
 
测试类
public class Main {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // 创建栈对象
        Stack stack = new Stack(10);

    
       
        // for (int i = 1; i <= 100; i++) {
        //      String str = "Hello-" + i;
        //      try {
        //      stack.push(str);
        //      } catch (StackException e) {
        //      e.printStackTrace();
        //      break; 
        //      }
        // }
          // 以上把try{}catch()放到了for循环中,完整的业务流程被try{}catch()打乱了.这种方式不好
      
        // 我们把for循环放到了try{}里面,这样程序的结构没有被打乱.
        // 当循环的过程中如果遇到异常,则程序跳转到catch()里面,循环被自动终断.这是最佳方式
        try {
            for (int i = 1; i <= 100; i++) {
                String str = "Hello-" + i;
                stack.push(str);
            }
        } catch (StackException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // 多次弹栈
        try {
            for (int i = 1; i <= 100; i++) {
                String str = stack.pop();
                System.out.println(str);
            }
        } catch (StackException e) {
            e.printStackTrace();
        }
    }
}
 
 
四.将编译时异常转换为运行时异常

 

     有的时候为了使用户的操作更加简单,我们会为外界调用给予更好的方式,不扔出编译时异常给调用者处理。
   
     
  private Student(int id, String name, String strBirthday, Sex sex) {

        try {
            // 把String -> Date
            this.birthday = sdf.parse(strBirthday);
        } catch (ParseException e) {
            // 把一个编译时异常转换成一个运行时异常,然后通过运行时异常向用户传递信息.用户可以不用额外处理异常
           //也可以自定义一个异常,该异常要继承RuntimeException,也就是自定义异常的第二种
            throw new IllegalArgumentException(strBirthday + "不是有效的日期格式", e);
        }
        this.id = id;
        this.name = name;
        this.sex = sex;
    }try {
            // 把String -> Date
            this.birthday = sdf.parse(strBirthday);
        } catch (ParseException e) {
            // 把一个编译时异常转换成一个运行时异常,然后通过运行时异常向用户传递信息.用户可以不用额外处理异常
           //也可以自定义一个异常,该异常要继承RuntimeException,也就是自定义异常的第二种
            throw new IllegalArgumentException(strBirthday + "不是有效的日期格式", e);
        }
        this.id = id;
        this.name = name;
        this.sex = sex;
    }
      以上代码使外界创建创建更加方便,但是,在parse()方法的声明处 throws ParseException,这是一个编译时异常,需要程序员进行声明捕捉以便抛出
     那么用try{}catch{}还是throws呢?
     1.如果使用throws,那么用户在创建的时候需要对异常进行相关的处理,这有悖于我们的初衷;
     2.如果使用try{}catch{} 在catch中解决了异常,外界得不到异常信息,那么接下来的代码执行就没有意义!!
     
     这种问题的解决方案在catch{}语句块中throws一个运行时异常,这个时候,即不用外界处理异常,还能在异常发生后终止用户之后的操作
     这里我们选择使用IllegalArgumentException来代替(也可以选择第二种自定义异常)
 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值