独到理解 _ Java异常机制

异常机制

一. 概念

异常是Java中提供的一种识别及响应错误情况的一致性机制。有效地异常处理能使程序更加健壮、易于调试。
异常 : 就是错误的另外一种说法
异常发生的原因有很多,比如:

  • 用户输入了非法数据
  • 要打开的文件不存在
  • 网络通信时连接中断
  • JVM内存溢出
  • 这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。
    异常还有 Robust 性 : 就是系统的健壮性

二. 系统异常分类

在这里插入图片描述

  • 异常分两种 : 编译时 和 运行时 ,
    在执行javac命令出错,是编译时
    执行java命令出错,是运行时

在eclipse中 没有运行 就报错,就是编译时

  • 在java中有一个专门模拟所有异常和错误的类(Throwable) 所有的异常类都必须继承这个类
  • 异常机制的处理形式
  1. try…catch… : 一般用在客户端
  2. throws : 一般用在类库端
  3. throw : 异常源点,起点

不同的异常机制的选择 :

  1. 有些错误我们不想处理,或者没有办法处理(这个通常就是在类库中),这个时候就把错误信息交给调用人员,这种情况 直接使用throws
  2. 我们能够处理的,就try…catch…(一般在客户端,main)

finally语句块 :
必须执行的语句块,有必须要执行的代码,可以放到finally中

  • Throwable异常类的祖类

Exception : 直接子类都是编译时异常,只有一个RunTimeException是运行时异常
Error : 系统内部错误,比如栈内存溢出,虚拟机错误等,这种情况JVM会选择终止程序,不需要我们做操作

语法 :

try{
高风险代码段;
}catch(异常类型 变量){
错误的处理机制
}

实例 : 除法出错异常

public class Exception_00 {

	public static void main(String[] args) {
		int a = 10;
		int b = 0;
//		 java.lang.ArithmeticException: / by zero
		System.out.println(a / b);
		System.out.println("-----");
	}
}

我们在实际写程序的过程中,因为 try … catch 会终止生命周期,所以不应该叫错误,而是换一种方式提示
使我们自己的异常,成为正常程序中的一部分

public class Exception_01 {

	public static void main(String[] args) {
		int a = 10;
		int b = 0;
		if (b == 0) {
			System.out.println("除数不能为0");
		} else {
			int c = a / b;
			System.out.println(c);
		}

		// try 捕获异常,catch 捕获指定的异常并进行处理
		try {
			int c = a / b;
			System.out.println(c);
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("除数不能为 0 ");
		}
		System.out.println("-----");
//		除数不能为0
//		java.lang.ArithmeticException: / by zero
//		除数不能为 0 	at exception.Exception_01.main(Exception_01.java:28)
//
//		-----

	}

}

常用的几种方式 :

  1. 用异常类和异常类的父类都可以捕捉

  2. 被捕捉之后的处理措施

1 打印追踪栈帧
printStackTrace();
2 错误栈帧的内存地址
getStackTrace();
3> 错提示信息
getMessage();

public static void main(String[] args) {
		try {
			FileInputStream fis = new FileInputStream("aaa.txt");

		} catch (FileNotFoundException e) {
			e.printStackTrace();
			System.out.println("---");
			System.out.println(e.getStackTrace());
			System.out.println("---");
			System.out.println(e.getMessage());
		}
	}

三. Error

1. 概念

  • 系统内部错误,这类错误由系统进行处理,程序本身无需捕获处理。

比如:OOM(内存溢出错误)、VirtualMachineError(虚拟机错误)、StackOverflowError(堆栈溢出错误)等,一般发生这种情况,JVM会选择终止程序。

2. 示例

堆栈溢出错误

public class TestError {
    public static void recursionMethod() {
        recursionMethod();// 无限递归下去
    }
    public static void main(String[] args) {
        recursionMethod();
    }
}

报错信息:
Exception in thread "main" java.lang.StackOverflowError
    at com.TestError.recursionMethod(TestError.java:5)
    at com.TestError.recursionMethod(TestError.java:5)
    at com.TestError.recursionMethod(TestError.java:5)
    at com.TestError.recursionMethod(TestError.java:5)
    at com.TestError.recursionMethod(TestError.java:5)
    at com.TestError.recursionMethod(TestError.java:5)
    ... ...

四. Exception

1. 介绍

**Exception是所有异常类的父类。**分为非RuntimeException和RuntimeException 。

  • 非RuntimeException
    指程序编译时需要捕获或处理的异常,如IOException、自定义异常等。属于checked异常。

  • RuntimeException
    指程序编译时不需要捕获或处理的异常,如:NullPointerException等。属于unchecked异常。一般是由程序员粗心导致的。如空指针异常、数组越界、类型转换异常等。

2. 示例

空指针异常

public class TestException {
    private static int[] arr;
    public static void main(String[] args) {
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

报错信息:
Exception in thread "main" java.lang.NullPointerException
    at com.TestException.main(TestException.java:7)

3. 常用方法

Exception类和其他普通类一样,有自己的属性和方法,为我们提供异常的相关信息。常用的方法有:

方法说明
public String getMessage()返回关于发生的异常的详细信息。这个消息在Throwable类的构造函数中初始化了。
public void printStackTrace()打印toString()结果和栈层次到System.err,即错误输出流。

【示例】

public class TestException {
    public static void main(String[] args) {
        Exception exp = new Exception("异常方法演示");
        //打印异常信息
        System.out.println("exp.getMessage()=" + exp.getMessage());
        //跟踪异常栈信息
        exp.printStackTrace();
    }
}

4. 异常捕获与处理

程序在执行时如果发生异常,会自动的生成一个异常类对象,并提交给JAVA运行环境,这个过程称为抛出异常。程序也可以自行抛出异常。
当出JAVA运行环境接收到异常对象时,会寻找能处理这个异常的代码并按程序进行相关处理,这个过程称为捕获异常

4.1 throws

【语法格式】

修饰符 返回类型 方法名(参数列表) throws 异常类名列表

一般用于类库段,并不能解决问题,依然会终止程序生命周期执行

public class Exception_03 {

	public static void main(String[] args) throws FileNotFoundException {
		m1();
		System.out.println("----");
	}

	public static void m1() throws FileNotFoundException {
		m2();
	}

	public static void m2() throws FileNotFoundException {
		m3();
	}

	@SuppressWarnings("resource")
	public static void m3() throws FileNotFoundException {
		new FileInputStream("23");
	}
}

如果上家提醒了你,要么你就使用try…catch…解决,要么你就提醒下家

throws 可以抛出多个异常,用 逗号 隔开

并且throws的时候,不需要考虑继承关系和先后顺序,因为只是个提醒

public class Exception_06 {

	@SuppressWarnings("resource")
	public static void main(String[] args) throws FileNotFoundException, IOException {
		new FileInputStream("");
	}

}
4.2 throw

throw的作用是抛出异常,抛出一个异常类的实例化对象。
【语法格式】
throw new XXXException();
【示例】

public class ExceptionDemo { 
public static void main(String[] args) throws Exception { 
Exception exp = new Exception("异常方法演示"); 
throw exp; 
} 
}

【总结】

  • 这种抛出异常的方式一般在满足某条件时主动抛出,即满足用户定义的逻辑错误发生时执行。
  • 含有throw语句的方法,或者调用其他类的有异常抛出的方法时,必须在定义方法时,在方法头中增加throws异常类名列表。
  • 使用throws关键字声明的方法表示此方法不处理异常,而交给方法调用处进行处理。
4.3 try…catch

【语法格式】

try{
有潜在异常抛出的语句组
}catch(异常类名 异常形参){
异常处理语句组
}catch(异常类名 异常形参){
异常处理语句组
}catch(异常类名 异常形参){
异常处理语句组
}catch(异常类名 异常形参){
异常处理语句组
}finally{
语句组
}

其中:

  1. try用来捕获语句组中的异常
  2. catch用来处理异常可以有一个或多个,而且至少要有一个catch语句或finally语句
  3. finally中的语句组无论是否有异常都会执行

常用捕捉异常方式 :

  1. try…catch try…finally
  2. try…catch…finally
  3. try…catch1…catch2…finally(体现异常出现的大小顺序)

说明: 多重catch处理异常,大异常类在后,小异常类在前。

  1. catch 语句可以根据代码的异常写多个
  2. 从上往下,必须是从子类到父类,或者没有继承关系,要不然会因为多态的原因,把子类异常捕捉
  3. 虽然写了多个catch,但是只会有一个 执行,因为最先出错的语句之后的代码不执行,直接执行catch
public class Exception_04 {

	@SuppressWarnings("resource")
	public static void main(String[] args) {
		try {
//			FileNotFoundException
			new FileInputStream("");
			String s = null;
			s.equals("");
		} catch (FileNotFoundException e) {
			System.out.println("找不到指定文件");
		} catch (NullPointerException e) {
			System.out.println("不能使用 null 调用成员属性");
		} catch (Exception e) {
			System.out.println("其他异常");
		}
	}
}

java1.7开始,有一个新的写法,一次catch 多个异常,但是 多个异常之间不能有继承关系,如果有就直接写父类

catch(异常类型1 | 异常类型2| 异常类型3|… 变量)

可以对多个异常,做相同的处理操作
实例 :

public class Exception_05 {

	public static void main(String[] args) {
		try {
//			FileNotFoundException
			new FileInputStream("");
			String s = null;
//			NullPotionException
			s.equals("");
		} catch (FileNotFoundException | NullPointerException e) {
			System.out.println("1111");
			// 空指针异常和找不到文件异常都会在这里执行
		} catch (Exception e) {
			System.out.println("2222");
		}
	}

}
4.4 Finally

finally : 必须执行的语句块

有时候我们需要在程序出错的情况下,有些代码必须执行,比如 开启的资源

  1. finally语句块可以直接和try使用,也可以和try…catch…使用,但是不能单独使用

  2. finally语句块一定会执行,有两种情况不会执行
    1. 编译时异常,编译有错,压根不能运行,更别提代码了
    2. System.exit() : 运行起来之后,这个finally唯一一种不执行的情况,这个等于直接关闭虚拟机

不管程序是否出错,finally都会执行

public class Exception_07 {

	public static void main(String[] args) {
		try {
			int a = 10;
			int b = 0;
			// 执行到这里,JVM直接关闭,参数 0 表示正常关闭,1 表示非正常
//			System.exit(1);
			int c = a / b;
		} finally {
			System.out.println("123456");
		}

		try {

		} catch (Exception e) {
			// TODO: handle exception
		} finally {

		}
	}

}

来看一种 finally 的特殊情况:
因为 finally语句块肯定会执行,当在finally语句块之外遇到return语句时,会执行 return 后的语句,但并不会返回值,因为 finally中还有return语句

public class Exception_08 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println(m1());
	}

	public static int m1() {
		int i = 10;
		try {
//			new FileInoutStream(""):
			System.out.println("---");
			// 这里的 return 不会执行,但是 i++ 会执行,因为 finally 比这个 return 优先级高
			// 所以最终返回 11
			return i++;
		} catch (Exception e) {
			return 1;
		} finally {
			System.out.println("===");
			return i;
		}
//		return 1;
	}
}

来看一下异常的继承机制 :
首先不能比原异常有更宽泛的异常:

import java.io.IOException;

public class Exception_09 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

}

class SupClass {
	public void m1() throws IOException {

	}
}

class SubClass extends SupClass {
	@Override
	// 不能有更宽泛的异常
//	public void m1() throws Exception{
//		
//	}

	// 原异常的子类可以
//	public void m1() throws FileNotFoundException{
//		
//	}

	// 原异常也可以
	public void m1() throws IOException {
		// TODO Auto-generated method stub
		super.m1();
	}
}
4.5 jdk1.7新特性
  1. 多个异常可以在一个 catch 中同时捕捉,使用 | 隔开
  2. 自动关闭资源
    语法 :

try (开启资源语句){
操作;
} catch (异常类型 变量){
处理代码;
}

public class Exception_10 {

	public static void main(String[] args) {
		// 传统关闭资源写法
		FileInputStream fis = null;
		try {
			fis = new FileInputStream("D:/123.txt");
			System.out.println(fis.read());
			fis.close();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (fis == null) {
					fis.close();
				}
			} catch (Exception e2) {
				// TODO: handle exception
			}
		}

		// 新特性写法,自动关闭资源
		try (FileInputStream fis1 = new FileInputStream("D:/123.txt");) {
			// TODO 操作
			System.out.println(fis1.read() + "---");
		} catch (Exception e) {
			// TODO: handle exception
		}
	}

}

5自定义异常

系统定义的异常主要用来处理系统可以预见的常见运行错误,对于某个应用所特有的运行错误,需要编程人员根据特殊逻辑来创建自己的异常类。
【语法格式】

public class 自定义异常类名 extends Exception{

}

异常也是个类,所以我们也可以自己自定义一个异常

每个项目都会有一个自定义异常,来完成自己的异常提醒

那么对于这个异常自己肯定要有个判断,是编译时异常 还是运行时异常

  • 如果是运行时异常,一般继承RunTimeException
    比如 : NullPointerException , 数组越界 , 类型转换异常等 一般是因为程序员粗心导致的错误

  • 如果是编译时异常,一般继承Exception
    比如 : IOException , FileNotFoundException 等,需要在编译阶段进行处理,否则就报错

我们写的自定义异常,一般是编译时异常

规定 :

  1. 必须继承一个已有的异常类,一般就是Exception/RunTimeExcetption
  2. 有一个公共的无参构造
  3. 有一个有参构造(字符串类型)

在构造方法中,使用super(…) 调用父类的构造方法,把String值进行传递
传入的String的值,就是错误信息

示例异常类 :

public class NameLengthException extends Exception {
	public NameLengthException() {

	}

	public NameLengthException(String mgs) {
		super(mgs);
	}
}

服务器端 :

public class UserService {
	public int m1(String name) throws NameLengthException {
		if (name.length() < 6) {
			System.out.println("用户名长度不能小于六位");
			throw new NameLengthException("用户名长度不能小于六位");
		}
		System.out.println("登录成功");
		return 1;
	}
}

客户端 :

public class Client {

	public static void main(String[] args) {
		UserService userService = new UserService();
//		boolean b = userService.m1("123");
//		if (b) {
//			// 注册成功
//		} else {
//			// 注册失败
//		}

		try {
			userService.m1("13456");
		} catch (NameLengthException e) {
			// TODO: handle exception
			e.getMessage();
			e.printStackTrace();
		}
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值