java中的异常
异常的基本概念
异常的分类
getMessage和printstackTrace
finally关键字
final、finalize和finally
如何自定义异常及使用
异常的基本概念
- 异常概述
1、什么是异常?
程序执行过程中发生了不正常的情况,而这种不正常的情况叫做:异常。
2、java提供异常处理机制有什么用?
java语言是很完善的语言,提供了异常的处理方式。程序执行过程中出现了不正常情况,java把该异常信息打印输出到控制台,供程序员参考。程序员看到异常信息之后,可以对程序进行修改,让程序更加的健壮。
3、java语言中异常是以什么形式存在的呢?
异常在java中以类的形式存在,每一个异常类都可以创建异常对象。如:
NumberFormatException nfe = new NumberFormatException("数字格式化异常");
System.out.println(nfe);
4、编译时异常和运行时异常,都是发生在运行阶段。编译阶段异常是不会发生的。
因为编译时异常必须在编译(编写)阶段预先处理,如果不处理编译器报错。所有异常都是在运行阶段发生的。因为只有程序运行阶段才可以new对象。异常的发生就是new异常对象。
异常的分类
- 编译时异常和运行时异常的区别
1、编译时异常一般发生的概率比较高。运行时异常一般发生的概率比较低。
2、对于一些发生概率较高的异常,需要在运行之前对其进行预处理。
3、编译时异常还有其他名字:
受检异常:CheckedException
受控异常
运行时异常还有其它名字:
未受检异常:UnCheckedException
非受控异制 - 异常的两种处理方式
1、
第一种方式:在方法声明的位置上,使用throws关键字,抛给上一级。
第二种方式:使用try...catch语句进行异常的捕捉。
2、例子:
某集团的一个销售员,因为失误,导致公司损失了1000元。
“损失1000元"这可以看做是一个异常发生。
有两种处理方式。第一种方式:把这件事告诉领导(异常上抛)第二种方式:自己掏腰包把这个钱补上。(异常的捕捉)
3、注意:
Java中异常发生之后如果一直上抛,最终抛给了main方法, main方法继续向上抛,抛给了调用者JVM,JVM知道这个异常发生,只有一个结果。终止Java程序的执行。 - 运行时异常编译时可以不处理
代码示例:
public class Demo{
public static void main(String[] args){
/*程序执行到此处发生了ArithmeticException异常
底层new了—个ArithmeticException异常对象,然后抛出了。
由于是main方法调用了10/0,
所以这个异常ArithmeticException抛给了main方法。
main方法没有处理,将这个异常自动抛给了JVM。
JVM最终终止程序的执行。
ArithmeticException继承RuntimeException,
属于运行时异常。在编写程序阶段不需要对这种异常进行预先的处理。*/
System.out.println(10/0);
/*这里的hello world没有输出,没有执行。*/
System.out.println("hello world!");
}
}
- 方法声明位置上使用throws
1、代码示例:
public class Demo{
public static void main(String[] args){
/**
* main方法中调用doSome()方法。
* 因为doSome()方法声明位置上有:
* throws ClassNotFoundException,
* 我们在调用doSome()方法的时候必须
* 对这种异常进行预先的处理。
* 如果不处理,编译器就报错。
* 编译器报错信息:
* Unhandled exception: java.Lang.CLassNotFoundException
* 报错原因:
* 因为doSome()方法声明位置上使用了:
* throws ClassNotFoundException
* 而CLassNotFoundException是编译时异常。
* */
doSome();//报错
}
/**
* doSome方法在方法声明的位置上使用了:
* throws classNotFoundException。
* 这个代码表示doSome()方法在执行过程中,
* 有可能会出现CLassNotFoundException异常。
* 这个异常直接父类是:Exception,
* 所以CLassNotFoundException属于编译时异常。
* @throws ClassNotFoundException
*/
public static void doSome() throws ClassNotFoundException{
System.out.println(111);
}
}
2、处理
第一种处理方式:
在方法声明的位置上继续使用:throws来完成异常的继续上抛。抛给调用者。
(上抛类似于推卸责任,继续把异常传递给调用者)
public static void main(String[] args) throws ClassNotFoundException {
doSome();
}
第二种处理方式:
try...catch进行捕捉
(捕捉等于把异常拦下了,异常真正的解决了。调用者是不知道的。)
public static void main(String[] args){
try {
doSome();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
- 异常捕捉和上报的联合使用
1、报错代码示例:
import java.io.FileInputStream;
public class Demo{
public static void main(String[] args){
System.out.println("main begin");
m1();
System.out.println("main over");
}
public static void m1(){
System.out.println("m1 begin");
m2();
System.out.println("m1 over");
}
public static void m2(){
/*
* 编译报错的原因是什么?
* 第一:这里调用了一个构造方法:
* FileInputstream(String name)
* 第二:这个构造方法的声明位置上有:
* throws FileNotFoundException
* 第三:通过类的继承结构看到:
* FileNotFoundException父类是TOException,
* IOException的父类是Exception
* 最终得知,FileNotFoundException是编泽时异常。
* 所以编译时异常要求程序员编写程序阶段必须对它进行处理,
* 不处理编译器就会报错。
* */
new FileInputStream("D:\\111.txt");
}
}
2、第一种处理方式:在方法声明的位置上使用throws关键字抛出,谁调用这个方法,就抛给谁。抛给调用者来处理。
代码示例:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Demo{
public static void main(String[] args) throws FileNotFoundException {
System.out.println("main begin");
m1();
System.out.println("main over");
}
public static void m1() throws FileNotFoundException {
System.out.println("m1 begin");
m2();
System.out.println("m1 over");
}
/*
* 只能抛本身或者其父类,抛其它的没用。
* throws后面也可以写多个异常,可以使用逗号隔开。
* */
public static void m2() throws FileNotFoundException {
new FileInputStream("D:\\111.txt");
}
}
一般不建议在main方法上使用throws,因为这个异常如果真正的发生了,一定会地给JVM,JVM只有终止。异常处理机制的作用就是增强程序的健壮性,要做到异常发生了也不影响程序的执行。所以一般main方法中的异常建议使用try...catch进行捕捉。main就不要继续上抛了。
3、第二种处理方式:使用try...catch语句对异常进行捕捉。
try {
//try尝试
//出现异常直接进入catch语句块
} catch (异常名 变量名) {
//catch是捕捉异常之后走的分支。
}
代码示例:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Demo{
public static void main(String[] args){
System.out.println("main begin");
try {
m1();
} catch (FileNotFoundException e) {
//e.printStackTrace();
System.out.println("文件不存在!");
}
System.out.println("main over");
}
public static void m1() throws FileNotFoundException {
System.out.println("m1 begin");
m2();
System.out.println("m1 over");
}
public static void m2() throws FileNotFoundException {
new FileInputStream("D:\\111.txt");
}
}
输出:
或:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Demo{
public static void main(String[] args){
System.out.println("main begin");
m1();
System.out.println("main over");
}
public static void m1(){
System.out.println("m1 begin");
m2();
System.out.println("m1 over");
}
public static void m2(){
try {
new FileInputStream("D:\\111.txt");
} catch (FileNotFoundException e) {
//e.printStackTrace();
System.out.println("文件不存在!");
}
}
}
输出:
- 深入try...catch
1、catch后面的小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类型。
2、catch可以写多个。建议catch的时候,精确地一个一个处理。这样有利于程序的调试。
3、catch写多个的时候,从上到下,必须遵守从小到大。
4、java8新特性
代码示例:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Demo{
public static void main(String[] args){
try {
FileInputStream fis = new FileInputStream("D:\\111.txt");
System.out.println(100 / 0);
} catch (FileNotFoundException | ArithmeticException e) {
//e.printStackTrace();
System.out.println("文件不存在?数学异常?都有可能!");
}
}
}
- 处理编译异常时,如何选择上报还是捕捉?
如果希望调用者来处理,选择throws上报。
其它情况使用捕捉的方式。
getMessage和printstackTrace
- 异常对象的常用方法
1、获取异常简单的描述信息:
string msg = exception.getMessage();
2、取得异常的堆栈信息(比较适合于程序调试阶段):
exception.printstackTrace();
(一般使用这种,try...catch里就是这种)
代码示例:
public class Demo{
public static void main(String[] args){
NullPointerException n = new NullPointerException("空指针异常");
String msg = n.getMessage();
System.out.println(msg);
n.printStackTrace();
}
}
输出:
finally关键字
- 关于try..catch中的finally子句
1、finally子句中的代码是最后执行的,并且是一定会执行的。(即使try语句块中的代码出现了异常)
finally子句必须和try一起出现,不能单独编写。
2、finally语句通常使用在哪些情况下呢?
通常在finally语句块中完成资源的释放/关闭。
因为finally中的代码比较有保障。
即使try语句块中的代码出现异常,finally 中代码也会正常执行。
3、代码示例:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Demo{
public static void main(String[] args){
FileInputStream fis = null;
try {
//创建输入流对象
fis = new FileInputStream("D:\\111.txt");
//这里一定会出现空指针异常
String s = null;
s.toString();
/*
流使用完需要关闭,因为流是占用资源的。
但是上面的程序出现异常,导致不能关闭流。
*/
//fis.close();
System.out.println(111);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NullPointerException e){
e.printStackTrace();
}
finally {
System.out.println(222);
/*
* 流的关闭放在这里比较保险。
* 即使try中出现了异常。
* finally中的代码是一定会执行的。
* */
if (fis != null){
try {
//close()方法有异常,采用捕捉的方式。
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println(333);
}
}
输出:
- 退出JVM,finally语句不执行
代码示例:
public class DemoTest{
public static void main(String[] args) {
/*
* 没有catch,
* 只有try和finally也可以。
* 以下代码的执行顺序:
* 先执行try...
* 再执行finally...
* 最后执行return(return语句只要执行方法必然结束)
* */
try{
System.out.println("try...");
return;
/*只有退出JVM,finally语句中的代码才不会执行。
* System.exit(0);
* */
}finally {
System.out.println("finally...");
}
}
}
输出:
将return替换为:
System.exit(0);
输出:
- 关于finally的一道题
代码示例:
public class Demo{
public static void main(String[] args){
int n = m();
System.out.println(n);
}
public static int m(){
int i = 100;
try {
return i;
}finally {
i++;
}
}
}
输出:
因为:
/*
反编译后的代码为:
*/
public static int m() {
byte i = 100;
byte var1;
try {
var1 = i;
} finally {
int i = i + 1;
System.out.println(i);
}
return var1;
}
final、finalize和finally
- 代码示例
public class Demo{
public static void main(String[] args){
/*final是一个关键字,表示最终的。不变的。*/
final int i = 10;
/*
finally也是一个关键字,和ry联合使用。
使用在异常处理机制中,
在finally语句块中的代码是一定会执行的。
*/
try {
}finally {
System.out.println("finally...");
}
/*
finalize()是object类中的一个方法。
作为方法名出现,所以finalize是标识符。
*/
}
}
如何自定义异常及使用
- 自定义异常
第一步:编写一个类继承Exception或者RuntimeException。
第二步:提供两个构造方法,一个无参数的,一个带有String参数的。 - 继承Exception或者RuntimeException
继承Exception:(编译时异常)
/*编译时异常*/
public class MyException extends Exception{
public MyException() {
}
public MyException(String message) {
super(message);
}
}
继承RuntimeException:(运行时异常)
/*运行时异常*/
public class MyExcption extends RuntimeException{
public MyExcption() {
}
public MyExcption(String message) {
super(message);
}
}
- 代码示例:
/*编译时异常*/
public class MyException extends Exception{
public MyException() {
}
public MyException(String message) {
super(message);
}
}
public class MyExceptionTest {
public static void main(String[] args) {
//创建异常对象(只new了异常对象,并没有手动抛出)
MyException e = new MyException("用户名不能为空!");
//打印异常堆栈信息
e.printStackTrace();
//获取异常简单描述信息
String msg = e.getMessage();
System.out.println(msg);
}
}
输出:
- 异常在实际开发中的作用
代码示例:
public class MyException extends Exception{//编译时异常
public MyException() {
}
public MyException(String message) {
super(message);
}
}
public class Demo{
public void Demo(int x) throws MyException {
if(x < 0){
/*改良前*/
//System.out.println("请输入大于0的数!");
//return;
/*改良后*/
throw new MyException("请输入大于0的数!");
} else {
System.out.println(x);
}
}
}
public class DemoTest{
public static void main(String[] args) {
Demo demo = new Demo();
try {
demo.Demo(1);
demo.Demo(2);
demo.Demo(-3);
demo.Demo(4);
} catch (MyException e) {
//e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
输出:
- 重写之后的方法不能比重写之前的方法抛出更多(更宽泛)的异常,但是可以更少。