Java异常
异常本质是一种程序上的错误,在程序运行过程中,意外发生的情况,偏离我们程序本身的意图的表现,可以将其理解为异常。错误在我们代码编写的过程中会经常发生,包括编译期间和运行期间的错误。像大括号没有正常配对、语句结束后没写分号、关键字编写错误,这些就是典型的编译期间的错误,通常编译器会提示修正。像循环数组时的下标越界、使用空的对象引用调用方法、算数运算除数为0、类型转换无法正常转型,这些就是常见的运行期间的错误,但是这类错误在编译时不会产生错误,比较讨厌。 程序运行期间产生异常可能导致程序不正常运行,也可能中断运行,造成用户数据丢失,系统资源无法释放,甚至于可能系统会直接崩溃。
1.异常类的继承结构
在整个Java的异常结构中,实际上有两个最常用的类,分别是Exception和Error,这两个类全都是Throwable的子类。
1.1Exception
Exception一般表示的是程序中出现的问题,可以直接用try...catch处理。Exception也是程序开发中异常处理的核心,可以分为运行时异常(RuntimeException)和检查异常(CheckException)。 (1)运行时异常(RuntimeException):在Java虚拟机正常运行期间抛出的异常,可以被捕获并处理,如果出现这个异常,一定是程序发生错误导致的。常见的运行时异常有空指针(NullPointerException)、类强制转换异常(ClassCastException)、数组下标越界异常(ArrayIndexOutOfBoundsException)。 (2)检查异常(CheckException):在编译阶段编译器会检查该异常并强制程序捕获和处理,即要求程序在可能出现异常的地方通过try catch语句捕获处理。常见的检查异常有输入或输出异常(IOException)、SQL语法异常(SQLException)、类不存在异常(ClassNotFoundException)。
1.2Error
Error一般指的是JVN错误,程序中无法处理,系统只能记录错误的成因和安全终止。如果程序在启动时出现Error,则启动失败;如果在运行过程中出现Error,则系统将退出进程。出现Error通常是因为系统的内部错误或者是资源耗尽。
2.Java的异常处理机制
2.1处理步骤
在整个Java的异常处理中,实际上也是按照面向对象的方式进行处理,处理步骤如下: (1)一旦产生异常,则首先会产生一个异常类的实例化对象; (2)在try语句中对此异常对象进行捕捉; (3)产生的异常对象与catch语句中的各个异常类型进行匹配,如果匹配成功,则执行catch语句中的代码。 try-catch-finally语法要求: (1)try 块后面可接0个或多个catch块,如果没有catch块,后面则必须跟一个finally;
2.2处理案例
下面写一个除数为0,抛出算术错误异常的案例:
public class Demo {
public static void main(String[] args) {
//要求:定义两个整数,接受用户键盘输入,输出两数之商
Scanner input = new Scanner(System.in);
System.out.println("=======运算开始========");
try { //包裹可能出错的代码
System.out.print("请输入第一整数:");
int one = input.nextInt();
System.out.print("请输入第二个整数:");
int two = input.nextInt();
System.out.println("one和two的商是:" + (one / two));
}catch (Exception e){//捕获异常
e.printStackTrace();//打印错误的堆栈信息,包括错误的描述、错误的类型和错误出现的位置
System.out.println("程序出错了~~~~~");//出错后执行的语句
}
finally {//无论是否出错都一定会执行的语句
System.out.println("======运算结束========");
}
}
}
输出:
=======运算开始========
请输入第一整数:1
请输入第二个整数:0
java.lang.ArithmeticException: / by zero
程序出错了~~~~~
======运算结束========
at person.xsc.datamanage.DemoException.main(DemoException.java:16)
2.3常见异常类型及原因分析
(1)NumberFormatException: 异常说明:数字格式化异常; 出现条件:涉及到类型转换时,比如不符合转换格式的字符串转换为数字。 示例:
String str="abbc123";
System.out.print(Integer.parseInt(str));
(2)ArrayIndexOutOfBoundsException: 异常说明:数组下标越界异常; 出现条件:涉及到使用超出数组下标范围的下标; 示例:
int [] num={1,2,3,4,5};
for(int i=0;i<=5;i++){
System.out.println(num[i]);
}
(3)NullPointerException: 异常说明:空指针异常; 出现条件:当使用了未经初始化的对象或者是不存在的对象时; 示例:
String str=null;
System.out.println(str.length());
(4)ClassCastException: 异常说明:类型转换异常; 出现条件:如进行向下转型时,转换对象无法完成正常转换; 示例:
clas Father{
}
class Son extends Father{
}
class Brother extends Father{
}
class Test{
public static void main (String[]args){
Father f=new Son();
Brother b=(Brother)f;
}
}
(5)ArrayStoreException: 异常说明:数组中包含不兼容的值异常; 出现条件:数组中实际存入的数据与预定不符,如子类数组经过向上转型,存入父类对象; 示例:
clas Father{
}
class Son extends Father{
}
class Test{
public static void main (String[]args){
//子类数组
Son[] sons=new Son[10];
Father[] fats=sons;
fats[0]=new Father();
}
}
(6)InputMismatchException: 异常说明:输入格式错误异常; 出现条件:接收数据与预期数据格式不符; 示例:
Scanner sc=new Scanner(System.in);
int num=input.nextInt();//这里输入一个字母
(7)FileNotFoundException: 异常说明:文件未找到异常; 出现条件:操作文件内容时发现文件不存在;
2.4使用多重catch结构处理异常
2.4.1 案例
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("=======运算开始========");
try { //包裹可能出错的代码
String str1=args[0];
int i=Integer.parseInt("ada1");
int j=Integer.parseInt("0");
int temp=i/j;//此处会产生异常
System.out.print("两个数相除结果:"+temp);
System.out.print("---------------------------");
}catch (ArithmeticException e){//捕获算数异常
e.printStackTrace();//打印出错的位置
}catch (NumberFormatException e){//捕获数字转换异常
e.printStackTrace();//打印出错的位置
}catch (ArrayIndexOutOfBoundsException e){//捕获数组下标越界异常
e.printStackTrace();//打印出错的位置
}catch (Exception e){//捕获其他异常
e.printStackTrace();//打印出错的位置
}
finally {//无论是否出错都一定会执行的语句
System.out.println("======运算结束========");
}
}
2.4.2 关于try...catch应用中常见问题
(1)try...catch里面定义的变量是局部变量,外部不可以使用; (2)catch(Exception e)这个参数命名不一定非要使用e,只有符合Java命名规范就可以; (3)虽然Exception是异常父类,包容性强,但是更推荐使用多重catch语句有针对性地设置针对不同异常产生的处理方案; (4)使用catch进行异常捕获时,要注意将大范围即父类后置;
2.5 终止finally语句执行
在异常处理语句中,写一句System.exit(这里输入一个非零的数),就可以无条件终止程序运行。
2.6 return关键字使用
public class Test1 {
public static void main(String[] args) {
Test1 test1 = new Test1();
System.out.println(test1.say());
}
public String say() {
String str = "abc";
try {
Integer.parseInt(str);
return "try";
} catch (Exception e) {
System.out.println("catch");
return "catch";
} finally {
System.out.println("finally");
}
}
}
输出:
catch
finally
catch
从上述代码可以看出运行顺序(分别执行完catch、finally语句之后才会执行return跳出并结束程序): (1)Integer.parseInt(str); (2)System.out.println("catch"); (3)System.out.println("finally"); (4)return "catch"; 注意:finally中也可以有return,但会覆盖其他的return。所以不推荐finally里有return语句。
2.7 throw关键字使用
throw是语句抛出一个异常,总是出现在函数体中,程序会在throw语句后立即终止,它后面的语句执行不到,然后在包含它的所有try块中(可能在上层调用函数中)从里向外寻找含有与其匹配的catch子句的try块。 使用注意: throw抛出的只能够是可抛出类Throwable或者其子类的实例对象。 使用方案:
- throw抛出异常处理方案1: try...catch... 语句包含throw语句。
public void method(){ try{ //代码段1 throw new 异常类型(); }catch (异常类型 ex){ //对异常处理的而代码段2 } }
- throw抛出异常处理方案2:谁调用谁处理。 在方法定义时抛出异常对象,然后再方法声明处通过throws 关键字声明这个异常类型。最后在方法调用处对异常进行处理,同样可以继续往上抛或者try.. catch..语句。
public void method() throws 异常类型{ //代码段1 throw new 异常类型(); }
2.8 throws关键字使用
可以通过throws声明将要抛出何种类型的异常,通过throw将产生的异常抛出。 - 使用场景*: 当一个方法可能会出现异常,但没有能力处理这种异常。可以通过方法声明处用throws语句来抛出这种异常。谁调用该方法谁就要处理这种异常。
- 使用注意*:
- throws后可以跟单个或者多个异常类型,注意不是异常对象。多个异常对象使用逗号隔开。
- 对于非检查异常不会直接有报错信息,但是可以通过文档注释的方法提醒程序员调用该方法时需要对某种异常进行处理。
通过throws抛出异常时,针对可能出现的多种异常情况,解决方案:
- throws后面接多个异常类型,中间使用逗号分隔
- throws后直接写Exception父类,调用处要解决该方法异常。注意此时可以使用或者单个多重catch语句,但是要注意最后一个catch块一定要使用Exception父类!否则会报错。
3.关于异常的面试问题
- 运行时异常与一般异常有何异同?
异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。
- error和exception有什么区别?
error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。 exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。
- try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,在return前还是后?
会执行,在return前执行。
- Java中的异常处理机制的简单原理和应用。
当JAVA程序违反了JAVA的语义规则时,JAVA虚拟机就会将发生的错误表示为一个异常。违反语义规则包括2种情况。一种是JAVA类库内置的语义检查。例如数组下标越界,会引发IndexOutOfBoundsException;访问null的对象时会引发NullPointerException。另一种情况就是JAVA允许程序员扩展这种语义检查,程序员可以创建自己的异常,并自由选择在何时用throw关键字引发异常。所有的异常都是java.lang.Thowable的子类。
- 给我一个你最常见到的runtime exception。
ClassCastException,ArrayStoreException,NullPointerException......
- JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗?
Java通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。在Java中,每个异常都是一个对象,它是Throwable类或其它子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并进行处理。Java的异常处理是通过5个关键词来实现的:try、catch、throw、throws和finally。一般情况下是用try来执行一段程序,如果出现异常,系统会抛出(throws)一个异常,这时候你可以通过它的类型来捕捉(catch)它,或最后(finally)由缺省处理器来处理。 用try来指定一块预防所有"异常"的程序。紧跟在try程序后面,应包含一个catch子句来指定你想要捕捉的"异常"的类型。 throw语句用来明确地抛出一个"异常"。 throws用来标明一个成员函数可能抛出的各种"异常"。 Finally为确保一段代码不管发生什么"异常"都被执行一段代码。 可以在一个成员函数调用的外面写一个try语句,在这个成员函数内部写另一个try语句保护其他代码。每当遇到一个try语句,"异常"的框架就放到堆栈上面,直到所有的try语句都完成。如果下一级的try语句没有对某种"异常"进行处理,堆栈就会展开,直到遇到有处理这种"异常"的try语句。