Java异常|Java异常处理

58 阅读5分钟

写在前面:本文注重针对异常的处理,对于文中的 IO 操作没有过多解释

对于 Java 异常处理,其实对于所有的语言而言,我们在使用的时候,必须先了解其基本语法

try-catch、throws、throw...

    public static void main(String args[]) throws Exception{
        /**
         * 读取 C 盘下面的文件,写到 D 盘下面
         */
        String filePath = "C:\\Users\\17323\\Desktop\\优惠-批量添加权限.txt";
        String remotePath = "D:\\2.txt";

        //将 C 盘下的文件读取为字节流
        byte[] bytes=new byte[1024];
        InputStream is = new FileInputStream(filePath);
        is.read(bytes);
		
        //将字节流写到 D 盘的文件中
        OutputStream os = new FileOutputStream(remotePath);
        os.write(bytes);
    }

首先肯定一下,这个功能是完成了的

  • 能够将C盘下的文件内容写到D盘的文件中

throws

用法:在方法名后面接 throws + 异常类;声明一个方法可能抛出的各种异常

在上面的代码中,我们在 main() 方法上抛出异常。

反正在我写过这么些的业务代码中,从来没有看到过这样的写法,直接在方法上抛出一个的大的异常,(这里为什么要说一个大的异常,因为一个方法下面受检查异常可能有很多,而且范围也可能不一样)

public static void main(String args[]) throws FileNotFoundException
那么 is.read 和 os.write 会报错,因为捕获他们的异常大于 FileNotFoundException

我们在一个具体的方法中,应该针对某一个具体的行为,比如上面的:

InputStream is = new FileInputStream(filePath);

is.read(bytes);
。。。,
如:对一个数据库返回对象判空等

throw

用法:抛出一个新的异常类;用来明确地抛出一个异常

//这只是一个简单的示例,我们通过再次封装,将 throw new TestException("map 没有数据"); 的内容直接返给前台,这样就很友好
    Map map=new HashMap();
    if(map==null||map.isEmpty()){
        throw new TestException("XXX工号没有权限");
    }

//自己定义了一个简易的异常类
class TestException extends RuntimeException {
    TestException(String msg){
        super(msg);
    }
}

try-catch

  /**
         * 读取 C 盘下面的文件到 D 盘下面
         */
        String filePath = "C:\\Users\\17323\\Desktop\\优惠-批量添加权.txt";
        String remotePath = "D:\\2.txt";

        //读取为字节流
        byte[] bytes = new byte[1024];
        try{
            InputStream is = new FileInputStream(filePath);
            is.read(bytes);
        }catch (IOException e){
            System.out.println("本地路径找不到");
        }

        try{
            OutputStream os = new FileOutputStream(remotePath);
            os.write(bytes);
        }catch (IOException e){
            e.printStackTrace();
        }

//读取的时候发生错误,会打印出:本地路径找不到,这在前台展示的时候更友好,而不是打印出一堆的堆栈错误

当然我们在 catch 的时候,可以指定更加合适的 Exception 类,也可有多个catch「尽管有多个catch,也只会匹配一个,不像 switch 必须在 case 下加上 break ,才不会执行其他的 case」

//大范围的异常类必须放在后面    
try{
      InputStream is = new FileInputStream(filePath);
      is.read(bytes);
  }catch ( FileNotFoundException e){
      System.out.println("本地路径找不到");
  }catch (IOException e){
      System.out.println("读取数据出错");
  }

finally

一般都是 try-cathc-finally 结合使用,finally 对资源进行关闭等

我们开始的代码还差了点事,资源没关闭呢

//只展示读取为字符流的代码
        //读取为字节流
        byte[] bytes = new byte[1024];
        InputStream is=null;
        try{
            is = new FileInputStream(filePath);
            is.read(bytes);
        }catch ( IOException e){
            System.out.println("本地路径找不到");
        } 
// finally 下面关闭资源
finally {
            if(is!=null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

看着代码都烦,对不对,太麻烦臃肿了。

try-with-resources

自 Java7 出现的语法糖:自动关闭资源,不用在写上面的 finally ,手动的关闭资源了。

		InputStream isIs=null;
        //读取为字节流
        byte[] bytes = new byte[1024];
   
/**核心代码,编写的时候只需要这个即可**/
try(InputStream is = new FileInputStream(filePath)){
    isIs=is;
    is.read(bytes);
}catch ( IOException e){
    System.out.println("本地路径找不到");
}


//这里只是验证,是否关闭了
finally {
    try {
         isIs.read(bytes);
    } catch (IOException e) {
         e.printStackTrace();//若关闭了,会报错  java.io.IOException: Stream Closed
    }
}

是不是觉得又学到了一个另外的小技能:如何验证 IO 流已经关闭 :)

具体案例1

try-catch

public class ForCheckAnyObject {

    public static void run(){
        Map map=new HashMap();
        map.get("ad").toString();
    }
    public static void main(String args[])  {
        /**
         * 上面讲的都是受检查异常,现在来讲运行时异常
         * 而我们开发业务代码中,大多数遇到都是业务代码,都是运行时异常
         */
        try{
            run();
        }catch(Exception e){
            System.out.println("上层报错");
        }
    }
}

//上层报错

这就是当调用 run() 方法时内部错误,run() 的 catch 捕获到,这样其实,我们就不能直观的看出错误到底是什么,还要必须要具体分析。所以这个时候我们应该优化

//run方法中修改如下:如打印出真正出错的地方以及原因  
Map map=new HashMap();
        try{
            map.get("ad").toString();
        }catch (NullPointerException e){
            e.printStackTrace();
        }

但是我们一般都不像上面这样用,而是下面这样:

 if(map.isEmpty()||map==null) {
     //抛出一个具体的错误(显示友好的)
}

具体案例2

try-catch-finally 解释和执行过程

try 块:如果在方法内部抛出了异常(或者在方法内部调用其他方法抛出了异常),这个方法将在抛出异常的过程中结束。如果不希望就此结束,可以设置一个特殊的块,用来尝试各种(可能产生的异常)方法调用,这就是 try 块。

catch :捕获异常,如果 try 中方法抛出了异常,那么 catch 会捕获到并进行处理。

finally :对于一些代码块,可能希望无论 try 块是否抛出异常,都能得到执行。通常适用于内存回收之外的情况:比如,已经打开的文件或网络连接

那么 finally 一定会被执行吗?

一、假如 try 中有 return 语句,那么 finally 还会被执行吗?

public class ForCheckAnyObject {

    public static String run(int i) {
        String s="";
        try {
            i++;
            System.out.println("try i=" + i);
            return s;
        } catch (Exception e) {

        } finally {
            System.out.println("yes");
            return s;
        }
    }

    public static void main(String args[]) {
        String j=run(2);
        System.out.println(j);
    }
}

/**
* try i=3
* yes
*/

因为return表示的是要整个方法体返回,所以,finally中的语句会在return之前执行 。

二、finally 什么时候不执行?

  • 如果对应的 try 块没有被执行,那么对应的 finally 也不会被执行
  • try/catch块中有System.exit(0)强制退出JVM,也不会执行