异常处理
一、异常及异常分类
在使用计算机语言进行程序开发的过程中,即使技术再牛,代码写再完善,在程序的运行过程中总会遇到一些问题,因为很多问题不是靠代码就能够解决的,比如客户端输入的数据格式有问题,读取某个文件不存在,需要联网的程序网络不畅通等等。我么现在可以给异常下一个定义:在JAVA语言中,将程序执行过程中发生的不正常的情况叫做异常。
Java的异常可以分为两类:
- Error : 表示错误,Java虚拟机无法解决的严重问题,比如JVM内部的错误,系统资源的耗尽等等严重情况,这种情况下程序一般不写针对性的代码。
- Exception:表示异常,其他因编程错误或者外在的因素导致的一般性的问题,可以使用针对性的代码进行处理,比如之前遇到过的Exception有空指针异常,数组下标(索引)越界异常,算数异常等。
异常:程序在运行时发生错误,导致程序中断运行。
在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理。异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行。
异常机制其实是帮助我们找到程序中的问题,异常的根类是 java.lang.Throwable ,其下有两个子类: java.lang.Error 与 java.lang.Exception ,平常所说的异常指 java.lang.Exception 。
异常的体验:当用户输入0或者用户输入的内容非数字,出现异常
package com.wanbangee.exceptiondemo;
import java.util.Scanner;
public class ExceptionDemo01 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int x = 100;
System.out.println("请输入被除数:");
int y = scanner.nextInt();
System.out.println("结果为:" + (x/y) );
}
}
对于这些错误,一般有两种解决方案,一种就是遇到错误程序终止运行,另外一种就是程序员在编写程序的时候,就考虑到错误的检测,错误消息的提示,以及错误的处理。我们现在要学习的就是第二种解决方案,错误的处理方式有两种,一种叫做捕获异常,二中叫做抛出异常。捕获异常最理想的时期应该是在程序编译期间,但是有的错误只有在运行时才会发生,比如说被除数为0,数组下标越界等。
这个时候我们可以将异常进行分类:
- 编译时异常(受检查的异常) ,比如 FileNotFindException,在程序编译期间,提示需要强制处理异常,Exception的所有的子类及其后代类(不包含RuntimeException及其后代)都是编译时异常。
- 运行时异常(不受检查的异常),比如ArithmeticException,在程序编译期间,没有任何问题,但是在运行过程中可能出现异常导致程序终止运行,不受检查的异常包含RuntimeException这个异常类及其后代类异常都属于运行时异常。
二、常见异常
三、异常的处理
在开发中,对于异常来说,一般都要进行处理,处理异常的方式有两种:
- 捕获异常,try catch捕获异常
- 抛出异常,throws不停的往上抛异常,最后交给JVM虚拟机处理
3.1 异常的捕获【抓异常】
异常捕获一,try catch
捕获异常的语法:
try{
可能出现异常的代码;
}catch(可能出现的异常对象){
异常出现之后的处理
}
public static void main(String[] args) {
try {
//可能出现异常的代码
System.out.println("算术异常出现之前");
System.out.println(1/0);
System.out.println("算术异常出现之后");
} catch (ArithmeticException e) {//可能出现的异常的对象
//出现异常之后的处理
System.out.println("出现了算术异常");
}
}
再次说明:如果try中出现异常,则try中的代码终止运行,并且执行catch中的代码,如果try中没有出现异常,则将try中的代码运行结束,并且不会执行catch中的代码。需要强调的是,catch中可能出现的异常的对象,可以是可能出现的异常对象,也可以是可能出现的异常的父类或者祖先类对象,异常的最高父类就是Exception,表示可以处理所有可能出现的异常,在开发中,经常使用Exception处理异常。
异常捕获二,一try 多catch
一try 多catch,如果可能出现的异常有多种,而且不想使用Exception处理所有的异常,而是单个异常单个catch,这个时候就可以使用多重的catch。
try{
可能出现异常的代码;
}catch(可能出现的异常对象1){
出现异常对象1的处理
}catch(可能出现的异常对象2){
出现异常对象2的处理
}catch(可能出现的异常对象3){
出现异常对象3的处理
}
public static void main(String[] args) {
try {
//可能出现异常的代码
System.out.println("算术异常出现之前");
System.out.println(1/1);
System.out.println("算术异常出现之后");
System.out.println("数组下标越界异常出现之前");
int arr[] = new int[] {1,2,3};
System.out.println(arr[5]);
System.out.println("数组下标越界异常出现之后");
} catch (ArithmeticException e) {//可能出现的异常的对象
//出现异常之后的处理
System.out.println("出现了算术异常");
} catch (IndexOutOfBoundsException e) {//可能出现的异常的对象
System.out.println("出现了数组下标越界异常");
}
}
在开发中一般都是用最高的异常父类Exception处理所有的异常。
还有一种情况:比如try中可能出现多种异常,而两种异常我希望单独的使用catch处理,而其他的异常统统交给统一的父类Exception处理,使用Exception的catch就必须放在最后。
public static void main(String[] args) {
try {
//可能出现异常的代码
System.out.println("算术异常出现之前");
System.out.println(1/1);
System.out.println("算术异常出现之后");
System.out.println("数组下标越界异常出现之前");
int arr[] = new int[] {1,2,3};
System.out.println(arr[5]);
System.out.println("数组下标越界异常出现之后");
System.out.println("空指针异常出现之前");
Person p = null;
System.out.println(p.getName());
System.out.println("空指针异常出现之后");
} catch (ArithmeticException e) {//可能出现的异常的对象
//出现异常之后的处理
System.out.println("出现了算术异常");
} catch (IndexOutOfBoundsException e) {//可能出现的异常的对象
System.out.println("出现了数组下标越界异常");
} catch(Exception e) {
System.out.println("出现了非算术异常及非越界异常");
}
}
异常捕获三,try catch finally
try catch finally,在实际开发中,比如IO操作,数据库操作,程序运行不管有没有出现异常,我们都必须关闭IO流和数据库连接。这就意味着不管有没有出现异常,都要执行关闭操作。这个时候就可以使用try catch finally。
try{
可能出现异常的代码;
}catch(可能出现的异常对象){
异常出现之后的处理
} finally{
不管有没有异常都会执行的代码(一般进行资源的释放)
}
public static void main(String[] args) {
try {
//可能出现异常的代码
System.out.println("算术异常出现之前");
System.out.println(1/1);
System.out.println("算术异常出现之后");
/*
* System.out.println("数组下标越界异常出现之前"); int arr[] = new int[] {1,2,3};
* System.out.println(arr[5]); System.out.println("数组下标越界异常出现之后");
*
* System.out.println("空指针异常出现之前"); Person p = null;
* System.out.println(p.getName()); System.out.println("空指针异常出现之后");
*/
} catch (ArithmeticException e) {//可能出现的异常的对象
//出现异常之后的处理
System.out.println("出现了算术异常");
} catch (IndexOutOfBoundsException e) {//可能出现的异常的对象
System.out.println("出现了数组下标越界异常");
} catch(Exception e) {
System.out.println("出现了非算术异常及非越界异常");
} finally {
System.out.println("不管有没有异常都会执行的代码");
}
}
在开发中,经常在catch中,使用一个方法,来进行异常的打印:public void printStackTrace(),打印异常的方法一般放在catch后,出现异常后怎么处理,打印异常,还有就是syso输出异常提示语句,这个方法在Throwable类中定义,所有的异常的对象都可以调用方法。
catch(Exception e) {
e.printStackTrace();
System.out.println(“出现了什么什么异常”);
}
public static void main(String[] args) {
try {
//可能出现异常的代码
System.out.println("算术异常出现之前");
System.out.println(1/0);
System.out.println("算术异常出现之后");
} catch (ArithmeticException e) {//可能出现的异常的对象
//出现异常之后的处理
System.out.println(出现了算术异常);
e.printStackTrace();
//打印异常,开发中使用较多,可以更快速的找到异常出现的位置
} finally {
System.out.println("不管有没有异常都会执行的代码");
}
}
3.2 异常的抛出【抛异常】
异常的抛出使用throws关键字,抛出的异常有两种类型:
- Java中已经定义的异常
- 自定义异常
通过throws 关键字,表示向上抛出异常,意思就是本方法中不处理抛出的异常类型,而是交给调用此方法的代码中处理(要么抛要么抓,如果main方法继续向上抛的话,表示将异常交给Java虚拟机处理)。
在使用throws关键字抛出异常的时候,如果此方法中有多个异常,只要抛出这些异常的共同的父类异常,则就可以抛出此方法中可能出现的所有的异常了。在实际开发中,不管是何种异常,都可以使用throws Exception,表示抛出所有的异常。
package com.wanbangee.exceptiondemo;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class ExceptionDemo04 {
/**
* 如果main方法中抛出异常,表示交给Java虚拟机处理
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
readFile();
}
/**
*
* @throws IOException 抛出的是IO异常
*/
public static void readFile() throws IOException {
// 编译时异常采用异常处理中的 抛 的方案
File file = new File("C:\\Users\\Administrator\\Desktop\\新1.txt");
InputStream in = new FileInputStream(file);
byte b[] = new byte[(int) file.length()];
in.read(b);// 将流中的内容读取到字节数组中
String s = new String(b);
System.out.println("读取的内容:" + s);
in.close();
}
}
自定义异常
自定义异常:在开发中使用并不多,但是在以后自己去开发属于一个自己的框架,要定义一些自己的异常。
自定义异常在开发中只需编写一个异常类,继承异常就可以了。
package com.wanbangee.exteption;
public class ExceptionDemo03{
public static void main(String[] args) {
throw new MyException("自定义的异常");
//通过throw关键字调用自定义的异常
}
}
//自定义异常,extends最高异常父类Exception也行
class MyException extends RuntimeException{
public MyException(String message) {
super(message);//调用父类的构造方法
}
}
自定义异常对象使用throw关键字调用,对于throw关键字和throws关键一定要区别好来。throws关键字用于方法声明处抛异常。
try{
throw new RuntimeException(); //自定义异常,可能出现异常的代码
}catch(Exception e){ //定义异常类型对象
System.out.println("出现了异常");
//出现异常后的处理,输出打印异常信息提示
e.printStackTrace();//打印异常
}finally{
不管有没有异常都会执行的代码(一般进行资源的释放)
}
总结:
- 程序在编译或者运行时出现的问题,我们叫做异常,分为两类
- 错误
- 异常
Throwable是异常和错误的最高父类。
2.异常是指程序编译或者运行时出现的错误,导致程序中断,分为两类: - 编译时异常:程序编译不通过,编译时异常又称为受检查的异常
- 运行时异常:程序编译通过,运行时才出现的异常,运行时异常又称为不受检查的异常
Exception是异常的最高父类
3.异常的捕获使用try catch代码块,try中声明可能出现异常的代码,catch中表示异常出现之后的处理,try中如果出现异常则中断程序运行,执行catch的语句,try中如果没有出现异常,则将try中的代码执行完成,并且不执行catch中的语句。
4.可以使用多重catch处理不同类型的异常,如果多重catch中存在有异常最高父类Exception,则放在最后的catch中
5.可以使用try catch finally来处理异常,其中finally块中的语句表示不管有没有出现异常,都会执行的代码
6.throws关键字用于方法声明处,表示本方法不处理异常,交个调用本方法的程序处理异常,如果main方法也使用throws关键字抛出异常,则最终异常交给JVM处理
7.throw关键字用于声明自定义异常,在开发中,使用比较少。
练习:
键盘输入一个int类型的整数。如果录入整数过大,给以提示“录入整数过大,请重新输入一个整数”;如果录入的是小数,给提示“录入的是小数,请重新输入一个整数”;如果录入的其他字符,请提示“录入的是非法字符,请输入一个整数”。
package com.wanbangee.exteption;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Scanner;
public class ExceptionDemo04 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
do {
System.out.print("请输入数字:");
String line = sc.next();//接收输入的一行内容
try {
int input = Integer.parseInt(line);
//如果转换没有出现异常,则表示输入的是整数
System.out.println("您输入的数字是整数");
break;
} catch (Exception e) {
//BigInteger 超出了的Integer范围的整数
try {
new BigInteger(line);//转换成大整数,表示超出了整数范围
System.out.println("您输入的内容是大整数,超出了范围");
} catch (Exception e2) {
try {
new BigDecimal(line);//表示一个大数字
System.out.println("您输入的是大数字,超出了范围");
} catch (Exception e3) {
System.out.println("您输入的是非法内容");
}
}
}
}while(true);
}
}
Exception又包含了运行时异常(RuntimeException, 又叫非检查异常)和编译时异常(又叫检查异常)
- Error是程序无法处理了, 如果OutOfMemoryError、OutOfMemoryError等等, 这些异常发生时, java虚拟机一般会终止线程。
- 编译时异常(受检查的异常) ,比如 FileNotFindException,在程序编译期间,提示需要强制处理异常,Exception的所有的子类及其后代类(不包含RuntimeException及其后代)都是编译时异常。
- 运行时异常(不受检查的异常),比如ArithmeticException,在程序编译期间,没有任何问题,但是在运行过程中可能出现异常导致程序终止运行,不受检查的异常包含RuntimeException这个异常类及其后代类异常都属于运行时异常。