JAVA基础知识之异常

一、什么是异常

        程序运行中出现的各种问题就称为异常

二、异常处理机制

        JAVA语言中针对异常而设计的处理机制称为异常处理机制

三、 异常的分类

        1. JAVA中所有的异常都是派生于Throwable类

        2.  Error类和Exception类派生于Throwable类,是异常的最主要的两大类

        3.  Error类描述程序运行时系统的内部错误等,该异常非常严重,出现后只能通告用户,然后终止程序,但是很少出现

        4.  Exception类又派生出了两大类(主要),RuntimeException和IOException



        5.  RuntimeException是描述程序错误导致的异常,而IOException是程序本身没有问题,但是由于I/O等错误产生的异常

        6.  RuntimeException的异常一定是程序编写者的问题,代表着这样的问题是人为的,可以避免的

        7. 所有派生于Error类和RuntimeException类的异常我们成为非受查异常,一个是没办法解决,一个是可以代码中控制避免的

        8. 派生于IOException等其他类的异常统称为受查异常,代表着是不可控制但可以解决的,因此要进行检查

四、声明受查异常

        1. 当我们定义一个方法时,若方法中的代码有可能产生受查异常,那么我们必须要声明该异常或者解决该异常,否则编译报错

        2. 声明异常:在方法签名后+关键字throws+异常类型

    public void test(String name) throws FileNotFoundException
    {
        File file=new File(name);
        
        FileInputStream inputStream=new FileInputStream(file); //将可能产生FileNotFoundException异常,
        
        
    }

        3. 当多个异常存在时,声明异常则异常类型间用,号隔开

    public void test(String name) throws FileNotFoundException, ClassNotFoundException
    {
        File file=new File(name);
        
        FileInputStream inputStream=new FileInputStream(file); //将可能产生FileNotFoundException异常,
        
       Class class1=Class.forName(name); //反射获取类,可能产生ClassNotFoundException异常
           
    }

五、抛出受查异常

          1.  当方法中代码逻辑明确有错误产生的时候,我们可以抛出一个异常用于结束方法,提示调用者进行异常处理

          2.  抛出异常的方式; 找到合适的异常类,生成异常类对象,通过throw关键字抛出

          3.  抛出异常的同时必须声明该异常,否则编译报错

          4.  抛出异常后调用该方法的方法必须要解决异常或者继续抛出异常

public class Test2
{

    public void readFile(String name) throws FileNotFoundException
    {
        
        File file=new File(name);
        
        if(!file.exists())
        {
            FileNotFoundException exception=new FileNotFoundException("文件不存在");
            throw exception;  
        }  
        
        System.out.println("文件存在");
    }

}

六、处理异常

        1.  java异常处理机制使用try catch 语句处理

        2.  可能产生异常的代码放入到try代码块中,若产生异常,则剩余代码不执行,跳入到catch代码块中进行处理解决

        3.  catch中的条件参数就是异常的类型,必须是try代码中可能产生的异常类型,否则编译报错,设置catch毫无意义

如下:若没有异常发生,则代码执行顺序为1,2,3,5;若发生异常,则顺序为1,2,4,5

public class Test
{
    public void readFile(String name)
    {
       
        try
        {
            File file=new File(name);  //1
            
            FileInputStream inputStream=new FileInputStream(file);  //2         将可能产生异常的代码放入到try代码块中
            
            System.out.println("文件流产生,文件存在"); //3  
        }
        catch (FileNotFoundException e)    //设置异常类型及变量,必须是try代码块中可能产生的异常类型,否则毫无意义
        {
            System.out.println(e.getMessage()); //  4  打印异常的信息
            
        }
        
        System.out.println("方法结束"); //5
    }
    
   
}

            4. 若有多个异常存在,可以使用多个catch语句进行异常捕获处理

public class Test
{
    public void readFile(String name)
    {

        try
        {
            File file = new File(name);

            FileInputStream inputStream = new FileInputStream(file); // 将可能产生FileNotFoundException异常

            System.out.println("文件流产生,文件存在");

            Class class1 = Class.forName(name); // 可能产生ClassNotFoundException异常

            System.out.println("类存在");
        }
        catch (FileNotFoundException e) // 设置异常类型及变量,必须是try代码块中可能产生的异常类型,否则毫无意义
        {
            System.out.println(e.getMessage());

        }
        catch (ClassNotFoundException e) // 设置异常类型及变量,必须是try代码块中可能产生的异常类型,否则毫无意义
        {
            System.out.println(e.getMessage());
        }

        System.out.println("方法结束");
    }

}

            5. 若有多个异常存在,也可以使用一个catch语句进行多个异常捕获处理(这种方式要求异常之间不存在子类关系)

正确设置:

public class Test
{
    public void readFile(String name)
    {

        try
        {
            File file = new File(name);

            FileInputStream inputStream = new FileInputStream(file); // 将可能产生FileNotFoundException异常

            System.out.println("文件流产生,文件存在");

            Class class1 = Class.forName(name); // 可能产生ClassNotFoundException异常

            System.out.println("类存在");
        }
        catch (FileNotFoundException | ClassNotFoundException e) // 设置异常类型及变量,必须是try代码块中可能产生的异常类型,否则毫无意义
        {
            System.out.println(e.getMessage());

        }

        System.out.println("方法结束");
    }

}

错误设置:

public class Test
{
    public void readFile(String name)
    {

        try
        {
            File file = new File(name);

            FileInputStream in = new FileInputStream(file); //可能产生FileNotFoundException异常
            
            System.out.println("文件流产生,文件存在");
            
            int b;
            
            while((b=in.read())!=-1)  //可能产生IOException异常
            {
                
                System.out.println("读取文件");
                
            }

        }
        catch (FileNotFoundException | IOException e) // 报错,因为FileNotFoundException 是IOException 的子类,不能这样写,只能多个catch处理
        {
            System.out.println(e.getMessage());

        }
       
        System.out.println("方法结束");
    }
}


七、finally语句

            1.  finally语句用于异常处理机制中,代表着无论是否发生异常,最终都要执行finally语句

            2.  如关闭文件资源,in.close(),若这方法在try语句中,一旦读取发生异常,程序会跳过剩余代码,因此不会执行该方法,若在catch语句中,那么不发生异常的话也不会执行该方法,因此finally语句就有发挥的地方

            3.  特别注意点:try代码块,catch代码块,finally代码块内的变量不互通,不能相互调用,因此一般变量要放在try代码块前

八、示例代码

             1.  该代码是之前个人编码常用的思路,捕捉异常关闭资源都照顾到了,看起来完美

            2.  但存在一个问题,当文件不存在时,第一个catch语句处理FileNotFoundException异常,而后进入finally语句,此时in是null对象,in.close就会报java.lang.NullPointerException异常,而这个异常是我们没有考虑到的

public class Test
{
    public void readFile(String name) throws IOException
    {

        File file = new File(name);

        FileInputStream in = null; // 变量定义在try之前,这样finally才能调用

        try
        {
            in = new FileInputStream(file); // 可能产生FileNotFoundException异常

            System.out.println("文件流产生,文件存在");

            int b;

            while ((b = in.read()) != -1) // 可能产生IOException异常
            {

                System.out.println("读取文件中");

            }

        }
        catch (FileNotFoundException e)
        {
            System.out.println(e.getMessage());

        }
        catch (IOException e)
        {
            System.out.println(e.getMessage());
        }
        finally
        {
            in.close();
            System.out.println("不管异常,最终都要执行关闭资源方法");
        }

        System.out.println("方法结束");
    }

}

                3.  因此作出改进,在关闭资源前进行in 是否为null的判断,这样看起来比较合理,测试解决了null问题,但是随即又发现了一个新问题,readFile方法因为in.close()可能存在IOException异常,因此要么在finally语句中加trycatch解决,要么进行声明,这里我们选择了声明,是否发现越来越麻烦? 

public class Test
{
    public void readFile(String name) throws IOException
    {

        File file = new File(name);

        FileInputStream in = null;  //变量定义在try之前,这样finally才能调用
        
        try
        {
            in= new FileInputStream(file); // 可能产生FileNotFoundException异常

            System.out.println("文件流产生,文件存在");

            int b;

            while ((b = in.read()) != -1) // 可能产生IOException异常
            {

                System.out.println("读取文件中");

            }

        }
        catch (FileNotFoundException e)
        {
            System.out.println(e.getMessage());

        }
        catch (IOException e)
        {
            System.out.println(e.getMessage());
        }
        finally
        {  
            if(in !=null)
            {
                in.close();
            }
           
            System.out.println("不管异常,最终都要执行关闭资源方法");    
        }

        System.out.println("方法结束");
    }
    
}

                    4.  因此我们可以灵活使用try finally语句和try catch 语句,内部try finally语句进行文件处理并关闭资源,外围try catch语句确保处理任何的异常,这样就可以很大程度提高代码清晰度和逻辑性

public class Test
{
    public void readFile(String name) 
    {

        try
        {
            File file = new File(name);

            FileInputStream in =null;
            
            try
            {
                in = new FileInputStream(file);

                int b;

                while ((b = in.read()) != -1) 
                {

                    System.out.println("读取文件中");

                }
            }
            finally
            {
                if(in !=null)
                {
                    in.close();     //有可能的异常被外围的try catch语句给捕获解决了
                }
             
                System.out.println("关闭资源");
            }

        }
        catch (FileNotFoundException e)
        {
            System.out.println(e.getMessage());

        }
        catch (IOException e)
        {
            System.out.println(e.getMessage());
        }

        System.out.println("方法结束");
    }

}

                5.   JAVA中针对这类带有资源读取产生异常的情况,有一个特殊的办法,就是带资源的try语句,这样的try语句不管是否异常都会在结束方法时关闭资源(实际编程中第4点和第5点都可以使用,看个人习惯)

public class Test
{
    public void readFile(String name)
    {

        File file = new File(name);

        try (FileInputStream in = new FileInputStream(file))
        {

            int b;

            while ((b = in.read()) != -1)
            {

                System.out.println("读取文件中");

            }
        }
        catch (FileNotFoundException e)
        {
            System.out.println(e.getMessage());

        }
        catch (IOException e)
        {
            System.out.println(e.getMessage());
        }

        System.out.println("方法结束");
    }

}

九、异常的特别注意点

        1)不要过分细分异常:有可能产生异常的语句可以放在一个try语句中,没有必要去分几个try

如下面代码就是不建议的:

public class Test
{
    public void readFile(String name)
    {

        File file = new File(name);

        try
        {
            FileInputStream in = new FileInputStream(file);
        }
        catch (FileNotFoundException e)
        {
            e.printStackTrace();
        }

        try
        {
            Class class1 = Class.forName(name);
        }
        catch (ClassNotFoundException e)
        {

            e.printStackTrace();
        }

        System.out.println("方法结束");
    }

}

应该修改为:

public class Test
{
    public void readFile(String name)
    {

        File file = new File(name);

        try
        {
            FileInputStream in = new FileInputStream(file);
            
            Class class1 = Class.forName(name);
        }
        catch (FileNotFoundException e)
        {
            e.printStackTrace();
        }
        catch (ClassNotFoundException e)
        {

            e.printStackTrace();
        }

        System.out.println("方法结束");
    }

}

            2)利用异常层次结构:抛出异常要抛出更详细的异常,不要只抛出RuntimeException这类的不方便精确定位问题

如这个明明是FileNotFoundException的异常,但是却捕获它的父类异常,这是不合理的

public class Test
{
    public void readFile(String name)
    {

        File file = new File(name);

        try
        {
            FileInputStream in = new FileInputStream(file);
            
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }

        System.out.println("方法结束");
    }
}

           

               3)早抛出:遇到异常时抛出该异常比抛出空对象导致后面引起空异常好

              4)晚捕获:遇到异常时不要全部捕获解决,有的时候应该抛出异常让更高层次的调用者通知错误发生(个人理解是service层产生异常时应该传递,到controller层在处理控制?)











猜你喜欢

转载自blog.csdn.net/ai_bao_zi/article/details/80996602