异常
- 概念:在程序运行过程中出现的一种意外的情况,而非编译错误。
1 异常的分类
- 比如:
Exception所有的子类都是已检查异常,编译时异常。1. Throwable:所有异常的父类。 2. Error:Throwable子类,表示错误,JVM崩溃,硬件出现问题或与操作系统相关的问题;特点:无法避免,无法处理。 3. Exception:Throwable子类,表示异常,编译时异常或已检查异常。 4. RuntimeException:Exception子类,表示异常,运行时异常或未检查异常。
RuntimeException所有的子类都是未检查异常,运行时异常。是一种程序的已知异常,必须手动处理予以解决方案,否则编译错误。 特点:无法避免,必须处理。
是一种因为程序员不好好检查出现的异常,可以提供解决方案也可以不提供,编译器不干预。 特点:可以避免,可处理可不处理。
2 异常的产生方式
2.1 自动抛出异常
- 写程序时不严谨,不好好检查,用错误的使用方式自动出现的异常。
演示:数组下标越界异常。String str = "ABC"; char[] cs = str.toCharArray(); System.out.println( cs[3] );
2.2 手动抛出异常
- 语法如下:
演示:throw 异常对象;
NullPointerException npe = new NullPointerException(); throw npe; throw new NullPointerException(); throw new NullPointerException("自定义异常描述信息");
2.3 异常对程序的影响
- 程序会因为异常的出现而终止。
- 异常传递过程:
演示的代码如下:类似于方法的返回值,将异常对象沿着方法调用链逐层返回。
package com.txw.test;
public class TestException {
public static void main(String[] args) {
System.out.println("main---start");
m1( 0 );
System.out.println("main---end");
}
public static void m1(int n){
System.out.println("m1---start");
// 异常对象
// NullPointerException npe = new NullPointerException();
// if(n==0) throw npe;
m2( n );
System.out.println("m1---end");
}
public static void m2(int n){
System.out.println("m2---start");
if(n==0)throw new NullPointerException("空指针异常");
System.out.println("m2---end");
}
}
3 异常的处理方式【重点】
3.1 声明抛出【消极处理】
- 语法:在出现已检查的方法中添加声明抛出。
声明抛出对程序的影响:自己不处理谁调用谁处理。访问权限修饰符 返回值类型 方法名(参数表) throws 异常,异常2...{ }
演示:
可以声明抛出父类异常处理所有子类异常(多态)。public static void main(String[] args) throws Exception{ System.out.println("main---start"); m1( 2 ); System.out.println("main---end"); } public static void m1(int n)throws IOException,SQLException { System.out.println("m1---start"); m2( n ); System.out.println("m1---end"); } public static void m2(int n)throws EOFException,FileNotFoundException,SQLException{ System.out.println("m2---start"); if(n==0)throw new NullPointerException("空指针异常"); if(n==1)throw new EOFException(); if(n==2)throw new FileNotFoundException(); if(n==3)throw new SQLException(); System.out.println("m2---end"); }
演示的代码如下:
package com.txw.test;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
public class TestThrows {
public static void main(String[] args) throws Exception{
System.out.println("main---start");
m1( 2 );
System.out.println("main---end");
}
public static void m1(int n)throws IOException,SQLException {
System.out.println("m1---start");
m2( n );
System.out.println("m1---end");
}
public static void m2(int n)throws EOFException,FileNotFoundException,SQLException{
System.out.println("m2---start");
if(n==0)throw new NullPointerException("空指针异常");
if(n==1)throw new EOFException();
if(n==2)throw new FileNotFoundException();
if(n==3)throw new SQLException();
System.out.println("m2---end");
}
}
3.2 捕获处理【积极处理】
- 当方法中出现异常时,可以捕获异常对象,跳转到专门用于处理异常的代码块中,处理完毕后程序由异常自动转换为正常,程序不会因为异常的产生而意外终止,可以配合
throws
一起使用。 - 语法如下:
演示的代码如下:try{ // 可能出现异常的代码 // 用于捕获异常 }catch(声明处理的异常1 | 声明处理异常2 e){ [可选]e.printStackTrace(); // 打印异常栈信息(异常来源) [可选]String message = e.getMessage(); // 打印描述信息 System.out.println(message); [可选]throw e; // 再次声明抛出异常 }catch(声明处理的异常 e){ }...finally{ // 无论是否有异常产生,必定执行 // 通常用于释放(关闭)资源 }
package com.txw.test;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.sql.SQLException;
import java.util.zip.DataFormatException;
public class TestTryCatch2 {
public static void main(String[] args) {
try {
m1(1);
}catch(SQLException e){
}catch(Exception e){
}
}
public static void m1(int n) throws SQLException,DataFormatException{ // 耳鼻喉科
try {
System.out.println("m1------1");
m2( n );
System.out.println("m1------2");
}catch(FileNotFoundException | EOFException e) {
e.printStackTrace();
}
/*catch(IOException e){
// 可以处理IOException或任意子类异常
}*/
System.out.println("m1------3");
}
public static void m2(int n)throws EOFException,FileNotFoundException,SQLException,DataFormatException{
System.out.println("m2---start");
if(n==0)throw new NullPointerException("空指针异常");
if(n==1)throw new EOFException("文件已读完!!!!"); // 打喷嚏
if(n==2)throw new FileNotFoundException(); // 嗓子疼
if(n==3)throw new SQLException(); // 牙疼
if(n==4)throw new DataFormatException(); // 头疼
System.out.println("m2---end");
}
}
可以声明处理父类异常,当需要多个catch时,遵循从子到父的顺序。
演示的代码如下:
package com.txw.test;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.sql.SQLException;
public class TestTryCatch1 {
public static void main(String[] args) {
System.out.println("main---start");
m1( 1 );
System.out.println("main---end");
}
public static void m1(int n) {
try {
// 捕获异常对象,为catch中异常引用赋值
System.out.println("m1---start");
m2(n);
}catch(EOFException e){
// 出现EOFException时执行
// System.out.println("catch EOFException");
// e.printStackTrace(); // 打印异常信息
String message = e.getMessage();
System.out.println(message);
}catch(FileNotFoundException e){
// System.out.println("catch FileNotFoundException");
}catch(SQLException e){
// System.out.println("catch SQLException");
}
System.out.println("m1---end");
}
public static void m2(int n)throws EOFException,FileNotFoundException,SQLException{
System.out.println("m2---start");
if(n==0)throw new NullPointerException("空指针异常");
if(n==1)throw new EOFException("文件已读完!!!!");
if(n==2)throw new FileNotFoundException();
if(n==3)throw new SQLException();
System.out.println("m2---end");
}
}
演示:
try {
}catch(SQLException e){
}catch(Exception e){
}
注意:try框中的代码不一定执行,当方法需要返回值时,return语句尽量写在外层。
try{
1. 登录输入卡号密码
2. 输入转账金额
3. 验证余额
4. 从原账户扣款 -2000 出现异常
5. 为对方账户增加余额
}catch(Exception e){
原账户余额 +2000
}finally{
退卡
}
- 常见的try-catch结构。
如果finally代码框中有return语句,那么将作为该方法最终结果返回。1. try{ }catch(Exception e){ } 2. try{ }catch(Exception e){ }finally{ } 3. try{ }finally{ }
关于异常:一般情况下程序员不会手动抛出异常,因为代价太大,通常情况都是在处理由框架或其他类抛出的已检查异常。
演示的代码如下:
package com.txw.test;
import java.io.EOFException;
import java.sql.SQLException;
public class TestTryCatchFinally {
public static void main(String[] args)throws SQLException{
System.out.println("main---start");
m1( 2 );
System.out.println("main---end");
}
public static void m1(int n) throws SQLException{
try {
System.out.println("m1---1");
m2(n);
System.out.println("m1---2");
}catch(EOFException e){
e.printStackTrace();
}finally{
System.out.println(" finally { }");
}
System.out.println("m1---3");
}
public static void m2(int n)throws EOFException,SQLException{
System.out.println("m2---start");
if(n==0)throw new NullPointerException("空指针异常");
if(n==1)throw new EOFException();
if(n==2)throw new SQLException();
System.out.println("m2---end");
}
}
4 自定义异常
- 自定义已检查异常。
// 继承Exception class MyException1 extends Exception{ public MyException1(){} public MyException1(String message){ super(message); // 使用父类构造方法,添加描述信息 } } // 抛出异常 throw new MyException1("自定义已检查异常");
- 自定义未检查异常[推荐]。
// 继承RuntimeException class MyException2 extends RuntimeException{ public MyException2(){} public MyException2(String message){ super(message); // 使用父类构造方法,添加描述信息 } } // 抛出异常 throw new MyException2("自定义已检查异常");
演示的代码如下:
package com.txw.test;
public class TestMyException {
public static void main(String[] args) throws MyException1{
m1(2);
}
public static void m1(int n)throws MyException1{
if(n==1)throw new MyException1("自定义已检查异常"); // 抛出自定义已检查异常
if(n==2)throw new MyException2("自定义未检查异常");
}
}
// 自定义已检查异常
class MyException1 extends Exception{
public MyException1(){}
public MyException1(String message){
super(message); // 使用父类构造方法
}
}
// 自定义未检查异常
class MyException2 extends RuntimeException{
public MyException2(){}
public MyException2(String message){
super(message);
}
}
5 方法覆盖的语法要求
- 语法要求:
演示的代码如下:访问权限修饰符相同或更宽,返回值类型、方法名、参数表与父类一致。 不能比父类抛出更宽泛的异常。
class Super{ public void m1()throws IOException {} } class Sub extends Super{ @Override public void m1()throws FileNotFoundException, EOFException {} // 虽然m1方法抛出的异常数量多,但是都是IOException的子类,并没有比父类方法抛出的异常更宽泛 }
- 面试问题
演示的代码如下:1. final、finally、finalize三个的区别 final 是修饰符 finally 是代码块 finalize 是Object类中的一个方法
package com.txw.test;
import java.io.EOFException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
public class TestOverride {
public static void main(String[] args){
// 可以表示计算机中的一个文件
try {
File f = new File("D:/abc.txt");
f.createNewFile();
}catch(Exception e){
e.printStackTrace();
}
}
}
class Super{
public void m1()throws IOException {}
}
class Sub extends Super{
@Override
public void m1()throws FileNotFoundException, EOFException {}
}
package com.txw.test;
public class TestReturnResult {
public static void main(String[] args) {
System.out.println(method());
}
public static int method(){
// 返回值通常都声明在try框外
int count = 0;
try {
// 定义计数变量
for (int i = 1; i <= 999; i++) {
if(i%2==0)count++;
}
return 1;
}catch(Exception e){
e.printStackTrace();
return 2;
}finally{
return 2000;
}
}
}
习题
1.Java 中所有的错误都继承自 Throwable 类;在该类的子类中,Error 类表示严重的底层错误,对于这类错误,程 序代码不可处理;Exception 表示例外、异常。
2. 查阅 API,完成以下填空:
(1) 异常类 java.rmi.AlreadyBoundException,从分类上说,该类属于已检(已检查|运行时)异常, 从处理方式上说,对这种异常必须处理。
(2) 异常类 java.util.regex.PatternSyntaxException,从分类上说,该类属于|运行时(已检查|运行时) 异常,从处理方式上说,对这种异常可以处理也可以不处理。
3. Java 中用来抛出异常的关键字是( C )。
A. try
B. catch
C. throw
D. finally
4. 在异常处理中,释放资源、关闭文件等应由____处理(C ) 。
A. try 语句
B. catch 语句
C. finally 语句
D. throw 语句
5. finally 语句块中的代码(A) 。
A. 总是被执行
B. 当 try 语句块后面没有 catch 时,finally 中的代码才会被执行
C. 异常发生时才被执行
D. 异常没有发生时才被执行
6. 自定义异常类时,可以继承的类是(C )
A. Error
B. ArrayList
C. Exception
D. NullPointerException
7. 对于 try{ … }catch…语句的排列方式,下列描述正确的是(A )。
A. 子类异常在前,父类异常在后
B. 父类异常在前,子类异常在后
C. 只能有子类异常
D. 父类异常不能与子类异常同时出现
8. 仔细阅读以下代码,将代码补全。
package com.txw.test;
public class TestThrow {
public static void main(String[] args) {
throwException(10);
}
public static void throwException(int n){
if (n == 0) {
// 抛出一个NullPointerException
}else {
// 抛出一个ClassCastException
// 并设定详细信息为类型“转换出错”
}
}
}
答:代码如下:
package com.txw.test;
public class TestThrow {
public static void main(String[] args) {
throwException(10);
}
public static void throwException(int n){
if (n == 0) {
// 抛出一个NullPointerException
throw new NullPointerException();
}else {
// 抛出一个ClassCastException
// 并设定详细信息为"类型转换出错”
throw new ClassCastException("类型转换出错");
}
}
}
- 代码改错:仔细阅读以下程序,将错误的代码进行改正。
package com.txw.test;
class MyException{
}
public class TestException {
public static void main(String[] args) {
ma();
}
public static int ma(){
try {
m();
return 100;
} catch (Exception e) {
System.out.println("Exception");
}catch (ArithmeticException e){
System.out.println("ArithmeticException");
}
}
public static void m(){
throw new MyException();
}
}
答:改正的代码如下:
package com.txw.test;
// MyException类必须要继承自Exception的某一个子类
class MyException extends RuntimeException{
}
public class TestException {
public static void main(String[] args) {
ma();
}
public static int ma(){
try {
m();
} catch (ArithmeticException e) {
System.out.println("ArithmeticException");
}catch (Exception e){ // 捕获Exception的语句应当放在所有catch语句的最后。
System.out.println("Exception");
}
return 100;
}
// 根据 MyException是否继承自RuntimeException
// 此处考虑是否声明抛出
public static void m(){
throw new MyException();
}
}
- 仔细阅读以下代码,当读入的 n 分别为 1,2,3,4,5 时,输出的结果分别是什么?
答:
I. n = 1 时,输出 main1 ma1 mb1 Catch EOFException In finally main2
II. n = 2 时,输出 main1 ma1 mb1 Catch IOException In finally main2
III. n = 3 时,输出 main1 ma1 mb1 Catch SQLException In finally main2
IV. n = 4 时,输出 main1 ma1 mb1 Catch Exception In finally main2
V. n = 5 时,输出 main1 ma1 mb1 mb2 ma2 In finally main2 注意:不论是否出现异常,出现什么异常,In finally 语句都会被打印出来。 - 仔细阅读以下代码:
在//1 处,填入以下__A B____代码可以编译通过,在//2 处,填入____D__代码可以编译通过。
A. throws java.io.IOException
B. throws java.io.FileNotFoundException, java.io.EOFException
C. throws java.sql.SQLException
D. 不能抛出任何异常
原因:
I. 根据方法覆盖的要求,子类的覆盖方法不能比父类的被覆盖方法抛出更宽泛的异常。
II. ma 方法抛出 IOException,则子类可以抛出 IOException,也可以抛出 IOException 的子类异常。
III. mb 方法没有抛出任何异常,则子类也不能抛出任何异常。 - 仔细阅读以下代码,关于程序描述正确的是(A)。
A. 编译不通过
B. 编译通过,输出-1
C. 编译通过,输出 0
D. 以上描述都不正确
原因:n的赋值语句产生异常后,找到catch执行里面的代码,最后return n,此时因为对n的赋值语句异常所以赋值失败,n没有 值,局部变量必须先有值才能使用。 - 仔细阅读以下代码,在 ma 方法中,当读入的 b 为 100 时,输出结果为___100___,当读入的 b 为 0 时,输出结果为100。
原因:Finally中的代码一定会执行。 - 仔细阅读以下代码,在 ma 方法中,读入整数 b,如果读入的值为 10,则输出_ma1 ma2 1 In Finally ;如果读入的 值为 0,则输出 ma1 In Finally。
- 仔细阅读以下代码,是否能编译通过?如果不能,应该如何修改?
答:不能编译通过。由于 MySub2 继承自 MySub 类,因此不能抛出比MySub 中的方法更多的异常。由于MySub 类中的 m 方法抛出 EOFException,而 MySub2 类的 m 方法抛出的 FileNotFoundException不是 EOFException 的子 类,因此这个方法 抛出了 MySub 类中的 m 方法更多的异常,因此编译不通过。 - 仔细阅读以下代码,关于程序描述正确的是(A)。
A. 编译出错
B. 编译正常,输出 main1 ma1 In Catch
C. 编译正常,运行时出错
D. 以上描述都不正确
原因:ma 方法中第二个输出语句,由于上一个语句是 throw 语句,因此第二个输出语句永远都执行不到,因此编译出错。 - 仔细阅读以下程序,下面哪些代码放在/1/处可以编译通过(AB)。
A. catch(NullPointerException npe){}
B. catch(IOException ioe){}
C. catch(SQLException sqle){}
原因:
I. 在/1/处,由于 ma 方法声明有可能抛出 IOException 异常,因此 B 编译通过。
II. 由于 NullPointerException 异常是运行时异常,即使 ma 方法的throws 语句中没有声明抛出,调用时也有可 能产生该异常,因此 A编译通过;
III. 由于 SQLException 没有声明抛出,并且是已检查异常,因此在/1/处不可能捕获到该类异常,因此 C 编译不 通过。 - 简述 final、finlize、finally 的区别。
答:final:修饰变量,方法,类。
修饰变量为常量,只能赋值一次 修饰方法,不能被覆盖 修饰类不能被继承 finally:是异常处理的一部分,finally中的代码一定会执行,多数用于数 数据库和io流中用来释放资源。
finalize:是Ojbect类中的一个方法用于垃圾回收。 - 在 try 里有 return 语句,那 finally 里的语句还会执行么?为什么?
答:会执行,放在finally中的代码一定会执行。 - 仔细阅读以下代码,写出程序执行的结果。
答:
StepB StepE
原因:s为null调用方法会产生空指针异常找到对应的catch,执行完毕后,执行finally中的代码。 - 编程:创建两个自定义异常类 MyException1 和 MyException2,要求如下:
(1) MyException1 为已检查异常,MyException2 为运行时异常;
(2) 这两个异常均具有两个构造函数:一个无参,另一个带字符串参数,参数表示产生异常的信息。
演示的代码如下:
package com.txw.test;
public class TestMyException {
public static void main(String[] args) throws MyException1{
m1(2);
}
public static void m1(int n)throws MyException1{
if(n==1)throw new MyException1("自定义已检查异常"); // 抛出自定义已检查异常
if(n==2)throw new MyException2("自定义未检查异常");
}
}
// 自定义已检查异常
class MyException1 extends Exception{
public MyException1(){}
public MyException1(String message){
super(message); // 使用父类构造方法
}
}
// 自定义未检查异常
class MyException2 extends RuntimeException{
public MyException2(){}
public MyException2(String message){
super(message);
}
}
- 在上一题的基础上,把下面代码补充完整。
演示的代码如下:
package com.txw.test;
import java.util.Scanner;
class MyException1 extends Exception {
public MyException1() {
super();
}
public MyException1(String message) {
super(message);
}
}
class MyException2 extends RuntimeException {
public MyException2() {
super();
}
public MyException2(String message) {
super(message);
}
}
public class TestMyException {
public static void main(String args[]) {
int n;
// 读入n
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
try {
m(n);
} catch (MyException1 ex1) {
// 输出ex1详细的方法调用栈信息
ex1.printStackTrace();
} catch (MyException2 ex2) {
// 输出ex2的详细信息
System.out.println(ex2.getMessage());
// 并把ex2重新抛出 throw ex2;
}
}
public static void m(int n) throws MyException1 {
// 声明抛出MyException1
if (n == 1) {
// 抛出MyException1
// 并设定其详细信息为“n == 1”
throw new MyException1("n == 1");
} else {
// 抛出MyException2
// 并设定其详细信息为“n == 2”
throw new MyException2("n == 2");
}
}
}
总结