Java基础之异常

Java基础之异常


一、异常体系结构

在java语言中,将程序执行中发生的不正常情况称为异常。
根据异常的种类,可分为两种。
异常分类介绍

  1. ERROR:Error 类是指 java 运行时系统的内部错误和资源耗尽错误。应用程序不会抛出该类对象。如果出现了这样的错误,除了告知用户,剩下的就是尽力使程序安全的终止。
    例如:StackOverflowError和OOM。

  2. EXCEPTION:其他因变成错误或偶然的一些外在因素导致的一般性问题,可以使用针对行的代码进行处理。例如, 空指针访问,试图读取不存在的文件,网络连接中断,数组角标越界

几点说明:
1.Error和Exception都继承于超类Throwable(可抛出的)。
2.Exception又可分为
RunTimeException(运行时异常):NullPointerException 、 ClassCastException,因为是java虚拟机正常运行期间的异常,该异常则被认为一定是程序员的异常。一般来说,不处理的话也不会有什么大问题。
编译时异常:就是我们通常所说的编译时异常,外部原因导致,例如FileNotFoundException,IOException以及SQL Exception等等, 对这些异常我们必须捕获或者声明

所有的异常根据受检和不受检又可分为checkedException和uncheckedException。

受检异常
编译器要求必须处理的异常。正确的程序在运行过程中,经常容易出现的、符合预期的异常情况。一旦发生此类异常,就必须采用某种方式进行处理。除 RuntimeException 及其子类外,其他的Exception 异常都属于受检异常。

非受检异常
编译器不会进行检查并且不要求必须处理的异常,也就说当程序中出现此类异常时,即使我们没有try-catch捕获它,也没有使用throws抛出该异常,编译也会正常通过。该类异常包括运行时异常(RuntimeException极其子类)和 Error(错误)

二、异常的处理

过程一:“抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处跑出异常类的对象(throw new XXXException),并将此对象抛出。一旦抛出以后,其后的代码不再运行。(throw 就是代表着终止)
关于异常对象的产生:1.系统自动生成的异常对象,2,自己手动生成的异常的对象,并抛出throw。这边自己其实也是没解决的。

过程二:“抓”:可以理解为异常的处理方式:try-catch-finally (自己真正解决了) throws(自己没解决)

1.try-catch-finally的使用

try{
    //可能出现异常的代码
}catch(异常类型1 变量名1){
    //处理异常的方式1
}catch(异常类型2 变量名2){
    //处理异常的方式2
}catch(异常类型3 变量名3){
    //处理异常的方式3
}
...finally{}

try-catch-finally 如何使用?

  • try块: 用于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟一个 finally 块。
  • catch块: 用于处理 try 捕获到的异常。
  • finally 块: 无论是否捕获或处理异常,finally 块里的语句都会被执行。当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。

几点说明:
1.finally是可选的(finally不一定会执行)
2.使用try将可能出现的异常代码包装出来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配。
3.一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没有写finally的情况下),继续执行之后的代码。

4catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓。
catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错。(如代码段中的异常类型2的范围要大于异常类型1中的范围)
5.常用的异常对象处理方式 String getMessage() void printStackTrace();
6.在try结构中声明的变量,再出了try结构以后,就不能再被调用
7.try-catch-finally结构可以嵌套

体会1:这个是处理编译时异常,是将程序在编译时不再报错,但是运行时可能报错。
只是将一个编译时可能出现的异常,延迟到运行时异常。
体会2:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。针对于编译时异常,我们说一定要考虑异常的处理。

异常代码示例

public class ExceptionTest1{
    @Test
    public void test1(){
        String str="123";
        str="abc";
        try{
             int num=str.parseInt(str);
             System.out.println("hello1");
        }catch(NumberFormatException e){
            System.out.println("出现数值转换异常了,不要着急");
        }
         System.out.println("hello 2");
    }
}
输出结果:出现数值转换异常了,不要着急
        hello 2
public class exceptiontest {
    @Test
    public  void test(){
        try {
            int a = 0;
            if(a<1) throw  new RuntimeException("数字不合法");
        }catch (Exception E){
            System.out.println(E.getMessage());;
        }finally {
            System.out.println("hello");
        }
    }
}
输出:数字不合法
hello
public class exceptiontest {
    @Test
    public  void test(){
        try {
            int a =4/0;
            throw  new RuntimeException("数字不合法");//如果这边是Exception则需要抛出异常
        }catch (Exception E){
            System.out.println(E.getMessage());;
        }finally {
            System.out.println("hello");
        }
    }
}
并没有走到“数字不合法那里”

这两张图作为对比,我们可以看到try中也可以throw自己想定义的语句,否则会调用她固定的话语。

补充说明:
try-catch-finally中finally的使用:
1.finally是可选的
2.finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中有return语句,catch中有return语句等情况。特殊情况如:jvm退出,线程死亡以及cpu终止运行则不会运行finally中的语句。
3.像数据库连接、输入输出流、网络编程Socket等资源。JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。

2.throws+异常类型

1.throws+异常类型写在方法的声明处,指明此方法执行时,可能会抛出的异常类型。一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,就不再执行。

代码示例

public class exceptiontest {
    @Test
    public  void test() throws FileNotFoundException {
        File file=new File("test.txt");
        FileInputStream fileInputStream = new FileInputStream(file);
        System.out.println("hello");
    }
}
输出结果:java.io.FileNotFoundException: test.txt (No such file or directory)
可以看到下面的hello并没有输出

3.throws与throw的区别

1.throw 在方法体内,只能一个,抛出的是异常对象,throws 函数名后或者参数列表后方法体前,可以跟多个,后面是异常类
2.thows代表一种倾向,可能但不一定实际发生。throw则是抛出了异常,一定抛出了某个异常对象。
3.throws用来声明异常,throw则是抛出具体的异常对象,执行到throw,功能就已经结束了。

三 、自定义异常类

1.继承于现有的异常结构:RuntimeException,Exception
2.提供全局常量:serialVersionUID
3.提供重载构造器

//编写MyException类
//或者继承RuntimeException(运行时异常) 
public class MyException extends Exception { 

  private static final long serialVersionUID = 1L; 

  // 提供无参数的构造方法
  public MyException() { 
  } 

  // 提供一个有参数的构造方法,可自动生成
  public MyException(String msg) { 
    super(msg);// 把参数传递给Throwable的带String参数的构造方法 
  } 

} 
//写一个Student类,手动抛出MyException
class Student{
    private int id;
    // 该异常继承自Exception类,需要捕获或者向上抛出异常
    public void regist(int id) throws Exception {
        if ( id > 0){
            this.id = id;
        }else{
            // 手动抛出异常
            throw new MyException("不能输入负数");
        }
    }
}
//测试类StudentTest
public class StudentTest{
    public static void main(String args[]){
        try{
            Student s = new Student();
            s.regist(-1001);
            // 对Student类throws的异常进行try-catch处理;
            System.out.println(s);
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
        //注意这边已经是调用的主函数了,所以必须捕获
    }
}

四、异常的一些练习

在这里插入图片描述
答案为“进入方法A”,"、调用A方法的finally",“制造异常”,“进入方法B”,“调用B方法的finally”

分析:
首先我们从主函数进入,主函数捕获了异常,所以后面的会输出。
分析A:try中没有用catch来捕获异常,首先输出“进入方法A”,接着throw异常了,所以后面的语句就不会再运行,不管咋样,finally总会运行的,接着在主函数这个地方捕获异常。
B的分析较常规。

五、Try-With-Resources

Java库中有很多资源需要手动关闭,例如打开的文件、连接的数据库等。在java7之前都是try-finally的方式关闭资源,try后面总是跟着一个“{}”。

举个例子:try-finally方式关闭资源

package com.chapter6;

import java.io.*;

public class Example6_8 {
    public static void main(String[] args) {
        copy("E:/MyFirst.java","F:/First.Java");//复制文件
    }

    private static void copy(String src, String des) {
        InputStream in = null;
        OutputStream out = null;
        try{
            in = new FileInputStream(src);
            out = new FileOutputStream(des);
            byte[] buff = new byte[1024];//创建一个长度为1024字节的字节数组
            int n;
            //从输入流一次最多流入buff.length个字节的数据到buff中,直到文件末尾结束
            while ((n=in.read(buff))>=0){
                //将数组buff中的数据从0位置开始,长度为n的字节输出到输出流中
                out.write(buff,0,n);
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            if(in!=null){
                try{
                in.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }

            if(out!=null){
                try{
                    out.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }

    }

}

从上例可以看出,这种实现非常的杂乱冗长。

Java7之后,推出了Try-With-Resourcesz声明代替之前的方式,try后跟括号"(",括号内的部分称为资源规范头。**资源规范头中可以包含多个定义,通过分号进行分隔。**规范头中定义的对象必须实现java.lang.AntoCloseable接口,这个接口中有一个close()方法,因此无论是否正常退出try语句块,这些对象都会在try语句块运行结束之后调用close方法,从而替代以前的在finally中关闭资源的功能,且不需要冗长的代码,另外,Try-With-Resources中的try语句可以不包含catch或者finally语句块而独立存在。

public class Example {
    public static void main(String[] args) {
        copy("E:/MyFirst.java","F:/First.Java");//复制文件
    }

    private static void copy(String src, String des) {
        try (InputStream in = new FileInputStream(src);
             OutputStream out = new FileOutputStream(des)) {
            byte[] buff = new byte[1024];//创建一个长度为1024字节的字节数组
            int n;
            //从输入流一次最多流入buff.length个字节的数据到buff中,直到文件末尾结束
            while ((n=in.read(buff))>=0){
                //将数组buff中的数据从0位置开始,长度为n的字节输出到输出流中
                out.write(buff,0,n);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

总结

出来混的都是要还的,希望自己好好积累,夯实基础。这就去看看异常的面试题,看看今天的学习成果咋样。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值