java异常处理机制

知识基础

java基础

版本

版本增加内容
20190727初始版本

1、异常的定义

异常:程序指令引起的CPU问题。
比如将0作为除数时,CPU产生一个类型0(除法错)的中断,来终止程序并作出相应处理。1
在这里插入图片描述
详见中断、故障、陷阱、异常等概念区别。

2、java异常处理机制概述

当程序出现异常时,
1、为了帮助程序员定位异常。java会把异常记录在特定的异常类(见java异常分类)的对象中,并将这个对象向着方法的调用方传递,最终程序会终止,并且会将错误信息打印出来(见java异常提示机制)。
2、为了让错误不影响程序的其他地方。java给开发者提供了异常捕获机制和异常打印机制,只要开发者捕获了异常,程序就不会终止。
另外,为了使开发者根据需要终止后面程序,java还提供了抛出异常实例和自定义异常的机制(见java异常抛出机制、自定义异常)

3、java异常分类机制

根据异常出现的时间分为:编译时异常,运行时异常
根据异常出现的原因分为:

  • 程序启动错误导致的异常
    如java环境配置错误导致启动后报错。
  • 程序代码错误导致的异常
分类例子解决方案
拼写、语法错误缺少分号,未初始化变量这类问题一般eclipse、idea等开发平台会有提示
逻辑错误空指针异常,越界访问,对用户输入的参数未做检查检查逻辑,校验参数

对于一些极有可能出错的方法,java一般都抛了异常。比如文件的读取方法。

3.1、Throwable

说明:Throwable是最大的异常类,直接继承Object。
包路径:Java.lang.Throwable
继承结构如下:
在这里插入图片描述
可以将其中的Error和RunTimeException称为非检查异常,将Exception中其他异常称为检查异常。(详见后文描述)
在这里插入图片描述

3.2、Error

3.2.1、定义

java系统内部问题,代码拼写、语法错误。

3.2.2、例子

java.lang.Error
错误。是所有错误的基类,用于标识严重的程序运行问题。这些问题通常描述一些不应被应用程序捕获的反常情况。
以下代码少了一个分号。
在这里插入图片描述
在这里插入图片描述
java.lang.AbstractMethodError
抽象方法错误。当应用试图调用抽象方法时抛出。
java.lang.AssertionError
断言错。用来指示一个断言失败的情况。
java.lang.ClassCircularityError
类循环依赖错误。在初始化一个类时,若检测到类之间循环依赖则抛出该异常。
java.lang.ClassFormatError
类格式错误。当Java虚拟机试图从一个文件中读取Java类,而检测到该文件的内容不符合类的有效格式时抛出。
java.lang.ExceptionInInitializerError
初始化程序错误。当执行一个类的静态初始化程序的过程中,发生了异常时抛出。静态初始化程序是指直接包含于类中的static语句段。
java.lang.IllegalAccessError
违法访问错误。当一个应用试图访问、修改某个类的域(Field)或者调用其方法,但是又违反域或方法的可见性声明,则抛出该异常。
java.lang.IncompatibleClassChangeError
不兼容的类变化错误。当正在执行的方法所依赖的类定义发生了不兼容的改变时,抛出该异常。一般在修改了应用中的某些类的声明定义而没有对整个应用重新编译而直接运行的情况下,容易引发该错误。
java.lang.InstantiationError
实例化错误。当一个应用试图通过Java的new操作符构造一个抽象类或者接口时抛出该异常.
java.lang.InternalError
内部错误。用于指示Java虚拟机发生了内部错误。
java.lang.LinkageError
链接错误。该错误及其所有子类指示某个类依赖于另外一些类,在该类编译之后,被依赖的类改变了其类定义而没有重新编译所有的类,进而引发错误的情况。
java.lang.NoClassDefFoundError
未找到类定义错误。当Java虚拟机或者类装载器试图实例化某个类,而找不到该类的定义时抛出该错误。
java.lang.NoSuchFieldError
域不存在错误。当应用试图访问或者修改某类的某个域,而该类的定义中没有该域的定义时抛出该错误。
java.lang.NoSuchMethodError
方法不存在错误。当应用试图调用某类的某个方法,而该类的定义中没有该方法的定义时抛出该错误。
java.lang.OutOfMemoryError
内存不足错误。当可用内存不足以让Java虚拟机分配给一个对象时抛出该错误。
java.lang.StackOverflowError
堆栈溢出错误。当一个应用递归调用的层次太深而导致堆栈溢出时抛出该错误。
java.lang.ThreadDeath
线程结束。当调用Thread类的stop方法时抛出该错误,用于指示线程结束。
java.lang.UnknownError
未知错误。用于指示Java虚拟机发生了未知严重错误的情况。
java.lang.UnsatisfiedLinkError
未满足的链接错误。当Java虚拟机未找到某个类的声明为native方法的本机语言定义时抛出。
java.lang.UnsupportedClassVersionError
不支持的类版本错误。当Java虚拟机试图从读取某个类文件,但是发现该文件的主、次版本号不被当前Java虚拟机支持的时候,抛出该错误。
java.lang.VerifyError
验证错误。当验证器检测到某个类文件中存在内部不兼容或者安全问题时抛出该错误。
java.lang.VirtualMachineError
虚拟机错误。用于指示虚拟机被破坏或者继续执行操作所需的资源不足的情况。

3.3、RuntimeException

3.3.1、定义

程序的逻辑错误。

由程序错误导致的异常属于RuntimException。
RuntimException这名字很让人混淆。实际上,现在讨论的错误都发生在运行时。
C++中有两个异常类,一个是runtime_error;另一个是logic_error。logic_error类相当于Java中的RuntimException,它表示程序中的逻辑错误。2

3.3.2、例子

在这里插入图片描述

3.4、受查异常

3.4.1、定义,作用

为了让程序更加稳定,java强制开发者处理一些有可能发生的异常。
java方法声明的受查异常
如下图,由于在读取文件时,文件很有可能不存在,因此java在文件输入流的构造方法中声明了FileNotFoundExcption,并且java要求开发者在使用这个构造方法时应对其进行抛出或捕获(见java异常抛出机制、java异常捕获机制),否则启动程序后编译器便会报错,显示下图中的编译错误。
在这里插入图片描述
在这里插入图片描述
当然eclipse会在开发者启动项目前就用红色波浪线提示开发者这里需要处理。
开发者声明的受查异常
同样,开发者在方法上声明此类异常后,如果不捕获或继续抛出。在调用此方法时,编译器也会报错。
在这里插入图片描述
在这里插入图片描述
非受查异常
由于RunTimeException是逻辑错误,Erorr是拼写、语法错误和系统内部错误,这些要么是开发者应该避免出现的,要么是很难预测的。因此java不强制开发者对其抛出或捕获。
如下图,如果在方法上声明NullPointException,调用了此方法且不做处理,则启动后不会报错。

3.4.2、例子

ClassNotFoundException 未找到相应类异常
IOException 输入/输出流异常
FileNotFoundException 文件未找到异常
SQLException 操作数据库异常
TimeoutException 操作超时异常
MathException 数学异常

4、java异常提示机制

如下图,当3/0算数异常后,后面的代码将不再执行,java在控制台输出了异常内容。
在这里插入图片描述
在这里插入图片描述

5、java异常捕获机制

5.1、语法1

语法
向上章所说,如果程序出现异常,程序就会终止执行,在控制台展现异常信息。
但是,如果使用try……catch捕获异常,那么程序并不会完全终止。try……catch后面的代码还会继续执行。并且也不会自动打印异常信息了。

try{
   //1、异常代码
   //异常代码后面代码(不执行)
} catch(AException e){// Aexception是用以捕获的异常类型,e是对象名
   //2、对异常的处理代码
} finally {
   //3、不管出错不出错,都会执行。通常放置一些释放资源、关闭对象的代码
   //此句不执行的情况:1)finally中代码出错 2)前面代码中使用了System.exit()退出程序 3)程序所在线程死亡
}
// 4、try……catch后面代码(执行)

例子
在这里插入图片描述
在这里插入图片描述

5.2、语法2

捕获异常时根据捕获的类型数量,可以写多个catch语句。父类型异常要放在后面.

    try {
    } catch(子类型Exception e) {
    } catch(父类型Exception e) {
    } 

5.3、语法3

如果对捕获的两个类型的异常做相同的处理,可以用|连接,前一个异常不能加变量名。

try {
}catch(NullPointerException | ClassCastExcepiton e){
}

5.4、语法4

语法
try和finally中如果都有return,try中return的值将会被覆盖。
例子
在这里插入图片描述

5.5、语法5

带资源的try,见《java核心技术1》278页

5.6、原则

  • 不要在try中写大量代码:这样会增加分析异常原因的难度。
  • 避免使用catch(Exception e):如果所有异常采用相同处理方式,将导致无法对不同异常分情况处理。

6、java异常打印机制

6.1、作用

当异常在try……catch语句中被捕获后,如果不做处理,程序不会自动打印异常信息。
因此,如果想要了解异常信息,就需要使用java提供的方法来打印异常信息。

6.2、方法

我们可以在try……catch的catch语句中打印异常信息。

try{
} catch(Exception e){ //e:异常对象
   e. getMessage();
}

相关方法有:
e.getMessage():打印异常消息
e.getCause():打印异常原因
e.toString():获取异常的类型与消息
e. printStackTrace():打印异常类型、消息、堆栈位置

6.3、例子

在这里插入图片描述

7、Java异常抛出机制

7.1、说明

如果某个方法可能发生异常,但不想在当前方法中处理这个异常,可以使用throw、throws关键字在方法上将异常对象传递给调用方。从而方便编程者在适当的代码位置处理异常的程序。
在java运行时,如获得一个异常对象,他会自动寻找处理该异常的代码。它从生成异常对象的代码构建开始,沿着方法调用栈,按层回溯寻找,直到处理该类异常方法为止。如一个异常达到调用栈的顶层,该线程将中止。
【不能在main方法抛出异常,因为抛出后就无法处理了】

7.2、手动抛出异常实例:throw

7.2.1、语法

throw关键词往往和if合用,用于表示在某种情况下需要抛出异常实例。当程序执行throw语句后,后面的程序将不再执行。

if(....) {
   //此处代码执行
   throw new AException("...");
   //此处代码不执行
}
//此处代码不执行

7.2.2、例子

public class Test3 {
	public static void main(String[] args) {
		f();
	}
	private static void f() {
		System.out.println("输入两个浮点数:");
		double a = newScanner(System.in).nextDouble();
		double b = newScanner(System.in).nextDouble();
		try {
			double r = divide(a, b);
			System.out.println(r);
		} catch (ArithmeticException e) {
			System.out.println("不能除零,是我们的错,请鞭笞我们吧!");
			e.printStackTrace();
		}
	}
	private static double divide(double a, double b) {
		if(b == 0) {
			throw new ArithmeticException("/ by zero");
		}
		return a/b;
	}
}

7.3、在方法上声明受查异常:throws

7.3.1、用途3

在方法上声明本方法中抛出或可能抛出的受查异常类型。这样,别人再调用你的代码时,就无需查看你的源代码有没有抛出的异常,然后去捕获。而只需要看一下你方法的声明,就可以知道了。
由于 C# 语言的函数上不需要标记它可能抛出的异常,为了确保一个函数不会抛出异常,你就需要检查这个函数的源代码,以及它调用的那些函数的源代码……也就是说,你必须检查这个函数的整个“调用树”的代码,才能确信这个函数不会抛出异常。这不但费时费力,看得你眼花缭乱,还容易漏掉出错。
显然让人做这种事情是不现实的,所以绝大部分时候,程序员都不能确信这个函数调用不会出现异常。因此就不得不做最坏的打算,把代码写成:

try{
    foo();
} 
catch (Exception){
}

因为不知道 foo 函数里面会有什么异常出现,所以catch 语句里面也不知道该做什么。大部分人只能在里面放一条 log,记录异常的发生。这是一种非常糟糕的写法,不但繁复,而且可能掩盖运行时错误。

7.3.2、语法1

void a() throws 受查异常名 {
}

非受查异常存在默认的抛出管道。因此无需声明:
在这里插入图片描述

7.3.3、语法2

抛出多个异常,用逗号分开

private static void f() throws ParseException, IOException {
}

7.3.4、语法3

子类重写父类方法,不能比父类声明的异常类型大。

7.4、受查异常包装成非受查异常抛出

7.4.1、说明

有时,重写java自带的方法,我们需要抛出受查异常,但是此方法中没有声明受查异常,我们重写方法也不能抛受查异常。这时,我们可以在catch中直接抛出RuntimeException,因为非受查异常默认存在于方法上。

7.4.2、例子

因为compare方法声明异常,所以要包装成RuntimeException异常。

public class Test4 {
	public static void main(String[] args) {
		f();
	}
	private static void f() {
		ArrayList<String> list = new ArrayList<>();
		Collections.addAll(
			list, 
			"2018-11-16","2018-11-20","2018-11-16",  "2018-11-1","2018-11-2","2018-11-10","asdfafgsdfgsdfg","2018-11-30","2018-11-3");
		Collections.sort(list, new Comparator<String>() {
			@Override
			public int compare(String o1, String o2) {
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
				try {
					Date d1 = sdf.parse(o1);
					Date d2 = sdf.parse(o2);
					returnd1.compareTo(d2);
				} catch (ParseException e) {
					throw new RuntimeException(e);
				}
			}
		});
		System.out.println(list);
	}
}

8、自定义异常

8.1、作用4

情况一:系统定义的异常信息和不够充分。拿常见的NullPointerException来说,该提示只会给出哪里出现了空指针异常,而如果我们自定义类似的异常我们就可以给出是哪个变量因为哪种原因在哪里抛出了该异常。
情况二:团队约定。比如团队开发一个API,要统一对外的异常展示。
情况三:程序可能出现错误但无法预知谁会调用该程序。比如入参要求是电话号码,调用方可能会输入英文字符,这个时候我们有两种选择,一种是做判断之后返回一个error code,此时的方法返回值就会被限制为int类型,而且需要给调用方出示一个error code含义表;另一种方法就是抛出异常,我不用考虑调用者是谁,也不用限定返回值,调用方只要调用了我的代码编译器就会提示他必须对这种可能的错误进行预处理。

8.2、方式

1、继承某个异常类
当代码极有可能出现异常时,继承Exception,因为其包含了受查异常。其余情况可以继承RuntimeException。
2、实现父类的构造方法
Exception没有强制要求子类实现其方法,但最好还是实现一下。
我们可以通过构造方法类封装异常信息。
当然如果只为给一个简单的提示或者根据异常名称就可以快速知道发生了什么,也可以不用定义这些。

public class AException extends Exception{
	public AException() {
		super();
	}
	public AException(String message, Throwable cause) {
		super(message, cause);
	}
	public AException(String message) {
		super(message);
	}
	public AException(Throwable cause) {
		super(cause);
	}
}

8.3、例子

创建找不到用户名异常类


public class UsernameNotFoundException extends Exception {
	public UsernameNotFoundException() {
		super();
	}
	public UsernameNotFoundException(String message, Throwable cause) {
		super(message, cause);
	}
	public UsernameNotFoundException(String message) {
		super(message);
	}
	public UsernameNotFoundException(Throwable cause) {
		super(cause);
	} 	
}

创建密码错误异常类

public class WrongPasswordException extends Exception {
	public WrongPasswordException() {
		super();
	}
	public WrongPasswordException(String message, Throwable cause) {
		super(message, cause);
	}
	public WrongPasswordException(String message) {
		super(message);
	}
	public WrongPasswordException(Throwable cause) {
		super(cause);
	} 	
}

创建测试类

public class Test5 {
	public static void main(String[] args) {
		System.out.print("用户名:");
		String n = newScanner(System.in).nextLine();
		System.out.print("密码:");
		String p = newScanner(System.in).nextLine();
		try {
			login(n,p);
			System.out.println("欢迎登录");
		} catch (UsernameNotFoundException e) {
			System.out.println("用户名错误");
		} catch (WrongPasswordException e) {
			System.out.println("密码错误");
		}
	}
	private static void login(String n, String p) 
throws UsernameNotFoundException, WrongPasswordException {
		//abc,  123
		if(! "abc".equals(n)) {
			throw new UsernameNotFoundException();
		}
		if(! "123".equals(p)) {
			throw new WrongPasswordException();
		}
	}
}

9、spring的全局异常处理机制

见spring:待写

参考资料


  1. 计算机组成原理:中断和异常:https://www.cnblogs.com/sfzyk/p/7467040.html ↩︎

  2. 《java核心技术1:异常、断言和日志》 ↩︎

  3. Kotlin 和 Checked Exception:http://www.yinwang.org/blog-cn/2017/05/23/kotlin ↩︎

  4. Java异常机制: https://www.jianshu.com/p/d82fe75ee01d ↩︎

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值