目录
01 异常概述与异常体系结构
异常
在 Java 语言中 将程序执行中发生的不正常情况称为 异常 。开发过程中的语法错误和逻辑错误不是异常
Error(编译时期,只能通过改代码处理)
Java 虚拟机无法解决的严重问题 。 如: JVM 系统内部错误 、 资源耗尽等严重 情况 。 比如: StackOverflowError(栈溢出--》递归) 和 OOM(堆溢出--》new Integer[1024 * 1024 *1024]) 。 一般 不编写针对性 的代码进行处理
Exception(本节重点)
其它因编程错误或偶然的外在因素导致的一般性问题 可以使用针对性的代码进行处理。
- 空指针访问
- 试图读取不存在的文件
- 网络 连接中断
- 数组角标越界
1.1 异常分类
运行时异常(红色)
是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该积极避免其出现的异常。 java.lang.RuntimeException 类及它的子类都是运行时异常。
对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。
编译时异常(蓝色)
是指编译器要求必须处置的异常。即程序在运行时由于外界因素造成的一般性异常。 编译器 要求 J ava 程序必须捕获或声明所有编译时异常。
对于这类异常,如果程序不处理,可能会带来意想不到的结果。
02 常见异常
import java.io.File;
import java.util.Date;
import java.util.InputMismatchException;
import java.util.Scanner;
/*
java.lang.Throwable
* |----java.lang.Error:一般不编写针对性的代码进行处理
* |----java.lang.Exception:可以进行异常处理
* |----编译时异常(checked)
* |----IOEXception
* |----FileNotFoundException
* |----ClassNotFoundException
* |----运行时异常(unchecked)
* |----NullPointerException
* |----ArrayIndexOutOfBoundsException
* |----ClassCaseException
* |----NumberFormatException
* |----InputMismatchException
* |----ArithmaticException
*/
public class ExceptionTest {
public static void main(String[] args){
// 编译时异常
// File file = new File();
// File file = new File("hello.txt");
// FileInputStream fis = new FileInputStream(file);
//
// int data = fis.read();
// while(data != -1){
// System.out.print((char)data);
// data = fis.read();
// }
//
// fis.close()
/////*************运行时异常***********
// InputMismatchException
Scanner scanner = new Scanner(System.in);
int scan = scanner.nextInt();
// 如果输入不是int型,则有异常
// NumberFormatException
String str1= "123";
int num = Integer.parseInt(str1); // 此时可以
String str2 = "abc";
int num2 = Integer.parseInt(str2); // 出现异常
// ClassCastException
Object obj = new Date();
String str = (String) obj; // 不能转型
// ArrayIndexOutOfBoundsException
int[] arr1 = new int[3];
System.out.println(arr1[3]);
String str3 = "null";
System.out.println(str3.charAt(5));
// NullPointerException
int[] arr = null;
System.out.println(arr[3]);
String str4 = null;
System.out.println(str4.charAt(0));
}
}
03 异常处理一(try-catch-finally)
在编写程序时,经常要在可能出现错误的地方加上检测的代码,如:
进行x/y运算时,要检测分母为0,数据为空,输入的不是数据而是字符等。过多的if-else分支会导致程序的代码加长、臃肿,可读性差。因此采用异常处理机制。
Java采用的异常处理机制,是将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁、优雅,并易于维护。
方式一:try-catch-finally
方式二:throws + 异常类型
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.junit.Test;
/*
* 异常的处理:抓抛模型
*
* 过程一:“抛”:程序在征程执行过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象
* 并将此对象抛出。
* 一旦抛出对象以后,其后的代码就不再执行。
*
* 过程二:“抓”:可以理解为异常的处理方式:① try-catch-finally ② throws
*
* 二、try-catch-finally的使用
*
* try{
* //可能出现异常的代码
* }catch(异常类型1 变量名1){
* //处理异常的方式1
* }catch(异常类型2 变量名2){
* //处理异常的方式2
* }catch(异常类型3 变量名3){
* //处理异常的方式3
* }
* ...
* finally{
* //一定会执行的代码
* }
*
* 说明:
* 1.finally是可选的。
* 2.使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象
* 的类型,去catch中进行匹配。
* 3.一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的
* try-catch结构(在没有写finally的情况)。继续执行其后的代码。
* 4.catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓。(互斥)
* catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错
* 5.常用的异常对象处理的方式: ① String getMessage() ② printStackTrace()
* 6.在try结构中声明的变量,再出了try结构以后,就不能再被调用,例65行:System.out.println(num);
* 7.try-catch-finally结构可以嵌套
*
* 体会1:使用try-catch-finally处理编译时异常,使得程序在编译时就不再报错,但是运行时仍可能报错。
* 相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。
*
* 体会2:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。
* 针对于编译时异常,我们说一定要考虑异常的处理。
*/
public class ExceptionTest1 {
@Test
public void test2(){
try{ //运行代码块
File file = new File("hello.txt");
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while(data != -1){
System.out.print((char)data);
data = fis.read();
}
fis.close();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
}
@Test
public void test1(){
String str = "123";
str = "abc";
try{
int num = Integer.parseInt(str);
System.out.println("hello-----1");
}catch(NumberFormatException e){
// System.out.println("出现数值转换异常了,不要着急....");
//String getMessage():
// System.out.println(e.getMessage());
//printStackTrace():
e.printStackTrace();
}catch(NullPointerException e){
System.out.println("出现空指针异常了,不要着急....");
}catch(Exception e){
System.out.println("出现异常了,不要着急....");
}
// System.out.println(num);
System.out.println("hello----2");
}
}
3.1 finally的使用(可选的)
* 1.finally是可选的。
* 2.finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中有return语句,catch中有 return语句等情况。
* 3.像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.junit.Test;
public class FinallyTest {
@Test
public void test2() {
FileInputStream fis = null; // 模块外声明
try {
File file = new File("hello1.txt");//文件可能不存在,而出现异常
fis = new FileInputStream(file);
int data = fis.read();
while (data != -1) {
System.out.print((char) data);
data = fis.read();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally { // 关闭文件,释放资源
try {
if (fis != null) // 可以嵌套,防止空指针异常
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void testMethod() {
int num = method();
System.out.println(num);
}
public int method() {
try {
int[] arr = new int[10];
System.out.println(arr[10]);
return 1;
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
return 2;
} finally {
System.out.println("我一定会被执行");
return 3; // 结果返回3,如果没有return则返回catch中的语句
}
}
@Test
public void test1() {
try {
int a = 10;
int b = 0;
System.out.println(a / b);
} catch (ArithmeticException e) {
// e.printStackTrace();
int[] arr = new int[10];
System.out.println(arr[10]);
} catch (Exception e) {
e.printStackTrace();
}
// System.out.println("我好慢呀~~~");
finally {
System.out.println("我好慢呀~~~");
}
}
}
04 异常处理机制二:throws + 异常类型
声明抛出异常是Java中处理异常的第二种方式;
如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。(ClassCastException 和Exception)
throws并未真正处理抛出的异常,而是交给该方法的调用者处理
4.1 代码举例
public class ExceptionTest2 {
public static void main(String[] args){
try {
method2();
} catch (IOException e) {
e.printStackTrace();
}
method3();
}
public static void method3(){
try {
method2();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void method2() throws IOException{
method1();
}
public static void method1() throws FileNotFoundException,IOException{
File file = new File("hello1.txt");
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while(data != -1){
System.out.print((char)data);
data = fis.read();
}
fis.close();
System.out.println("hahaha!");
}
}
4.2 抛出异常的原则
import java.io.IOException;
/*
子类抛出异常不能超过父类,如果父类没有抛出异常,子类也不能抛出
*/
public class Override {
public void display (Superclass s){
try{
s.method(); // 可能会抛出一个异常,要对异常进行处理
}catch (IOException e){
e.printStackTrace();
}
}
}
class Superclass{
public void method() throws IOException{
}
}
class Subclass extends Superclass{
public void method() throws Exception{ // 这里报错
}
}
/* 3. 开发中如何选择使用try-catch-finally 还是使用throws?
* 3.1 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果 子类重写的方法中有异常,必须使用try-catch-finally方式处理。
* 3.2 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws 的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理。
*/
4.3 手动抛出异常
Java 异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出 也可根据需要 使用 人工 创建 并抛出 。
首先要生成异常类对象 然后通过 throw 语句实现抛出操作 提交给 Java 运行环境 。
IOException e = new IOException
throw e
可以抛出的异常必须是 Throwable 或其子类的实例 。 下面的语句在编译时将会产生语法错误:
throw new String("want to throw")
public class StudentTest {
public static void main(String[] args) {
try {
Student s = new Student();
// s.regist(1001); // 运行正常
s.regist(-1001);
System.out.println(s);
} catch (Exception e) {
// e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
class Student{
private int id;
public void regist(int id) throws Exception{ // 方法抛出异常
if(id > 0){
this.id = id;
}else{
// System.out.println("您输入的数据非法!");
//手动抛出异常
// throw new RuntimeException("您输入的数据非法!");
throw new Exception("您输入的数据非法!"); // 新建Exception
}
}
@Override
public String toString() {
return "Student [id=" + id + "]";
}
}
05 如何自定义异常类
/*
* 如何自定义异常类?
* 1.继承于现有的异常结构:RuntimeException 、Exception
* 2.提供全局常量:serialVersionUID // 模仿,提供序列号作为类的标识
* 3.提供重载的构造器
*
*/
public class MyException extends RuntimeException{ // 运行时异常
static final long serialVersionUID = -7034897193246939L;
//构造器
public MyException(){
}
public MyException(String msg){
super(msg);
}
}
06 练习
练习1——ReturnExceptionDemo类
public class ReturnExceptionDemo {
static void methodA() {
try {
System.out.println("进入方法A"); // -------1
throw new RuntimeException("制造异常"); //--------3
} finally {
System.out.println("用A方法的finally");// -------2
}
}
static void methodB() {
try {
System.out.println("进入方法B");
return;
} finally {
System.out.println("调用B方法的finally");
}
}
public static void main(String[] args) {
try {
methodA(); // 进入方法a;用a的finally
} catch (Exception e) {
System.out.println(e.getMessage()); // 制造异常
}
methodB();
}
}
练习二
/*
编写应用程序 EcmDef java 接收命令行的两个参数 要求不能输入负数 计算两数相除 。
对数据类型不一致
NumberFormatException 、 缺少命令行参数ArrayIndexOutOfBoundsException 、
除0 ArithmeticException 及输入负数 EcDef 自定义的异常 进行异常处理 。
提示:
(1 在主类 EcmDef 中定义异常方法 ecm 完成两数相除功能 。
(2 在 main() 方法中使用异常处理语句进行异常处理 。
(3 在程序中 自定义对应输入负数的异常类 EcDef 。
(4 运行时接受参数 java EcmDef 20 10 args 0 20 args 1 10
(5 Interger 类的 static 方法 parseInt (String s) 将 s 转换成对应的 int 值 。
如int a= Interger parseInt 314 ”)”);;// 314
*/
public class EcmDef {
public static void main(String[] args){ // 命令行参数丛这接Shou
try{
int i = Integer.parseInt(args[0]); // NumberFormatException
int j = Integer.parseInt(args[1]); // 如果少一个 ArrayIndexOutOfBoundsException
int result = ecm(i,j);
System.out.println(result);
}catch(NumberFormatException e){
System.out.println("数据类型不一致");
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("缺少命令行参数");
}catch(ArithmeticException e){
System.out.println("除0");
}catch(Ecdef e){
System.out.println(e.getMessage());
}
}
public static int ecm(int i, int j) throws Ecdef{ // 定义方法抛出异常
if(i < 0 || j < 0){
throw new Ecdef("分子分母不能为负数"); //出现异常的处理,新建一个异常对象,并输出信息
}
return i / j;
}
}
// 首先自定义异常Ecdef
class Ecdef extends Exception{
// 定义全局常量
static final long serialVersionUid = -3344232442323l;
// 构造器
public Ecdef() {
}
public Ecdef(String message) {
super(message);
}
}