什么是异常?
异常:就是程序在运行过程中出现的不正常情况,这种不正常情况就叫做异常。
下面举例说明: (运行下面的代码,就会输出相关异常)
/**
* @author Jason
* @create 2020-07-05 15:16
*/
public class ExceptionTest01 {
public static void main(String[] args) {
int num1=10;
int num2=0;
int num3=num1/num2;
System.out.println(num1+"/"+num2+"="+num3);
}
}
控制台输出:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at ExceptionTest02.main(ExceptionTest02.java:9)
异常的作用是什么?
提高程序的健壮性,通过输出的异常信息,让程序员判断并修改代码。
异常是以什么形式存在的呢?
异常是以类的形式存在的,也就是说,当我们的程序遇到异常的时候,就是去实例化那个异常类,创建一个异常对象。
举例说明:
/**
* @author Jason
* @create 2020-07-05 15:04
*/
public class ExceptionTest02 {
public static void main(String[] args) {
NumberFormatException nfe = new NumberFormatException("数字格式化异常");
System.out.println(nfe);
NullPointerException npe = new NullPointerException("空指针议程发生了什么?");
System.out.println(npe);
}
}
输出结果:
java.lang.NumberFormatException: 数字格式化异常
java.lang.NullPointerException: 空指针议程发生了什么?
那么异常类的底层图是什么样子的呢?
Java的异常处理机制:
Exception的直接子类:编译时异常(要求程序员在编写程序阶段必须预先对这些异常进行处理,如果不处理编译器报错,因此得名编译时异常。)
RuntimeException:运行时异常。(在编写程序阶段程序员可以预先处理,也可以不管,都行。)
编译时异常和运行时异常的区别?
编译时异常:编译时异常不是在编译阶段发生的异常,而是表示程序在编写的时候预先对这种异常进行处理,如果不处理编译器就会报错,编译时异常发生的概率较高,编译时异常又称为受检异常,或受控异常。
运行时异常:在编写程序阶段可以选择处理,也可以选择处理,也可以选择不处理,运行时异常发生概率较低,运行时还有另外一个名字:未受检异常或非受控异常
注意:
所有异常都是发生在运行阶段
java异常的处理方式:
第一种方式:在方法声明的位置上,使用throws关键字,抛给上一级。 谁调用我,我就抛给谁。抛给上一级。
第二种方式:使用try..catch语句进行异常的捕捉。这件事发生了,谁也不知道,因为我给抓住了。异常到此为止,不在上抛
思考:
异常发生之后,如果我选择了上抛,抛给了我的调用者,调用者需要对这个异常继续处理,那么调用者处理这个异常同样有两种处理方式。
一、继续上抛:throws
二、自己处理:try...catch
注意:
Java中异常发生之后如果一直上抛,最终抛给了main方法,main方法继续向上抛,抛给了调用者JVM,JVM知道这个异常发生,只有一个结果。终止java程序的执行。
编译时异常实例:
/**
* @author Jason
* @create 2020-07-05 22:00
* 编译时异常
*/
public class ExceptionTest03 {
//第一种方法:选择继续上抛异常
public static void main(String[] args) throws ClassNotFoundException {
//doSome()上有编译时异常,我们在调用这个方法的时候必须对这个异常预先进行处理,否则报错
//doSome() //千万不可用不处理异常
doSome();
}
//第二种方法:自己把异常拦截下来
/*public static void main(String[] args) {
//doSome()上有编译时异常,我们在调用这个方法的时候必须对这个异常预先进行处理,否则报错
//doSome() //千万不可用不处理异常
try {
doSome();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}*/
public static void doSome()throws ClassNotFoundException{
System.out.println("doSome!!");
}
}
1、throws上抛给上一级处理实例:
/**
* @author Jason
* @create 2020-07-06 8:45
* 编译时异常处理:上抛形式处理
*/
public class ExceptionTest04 {
public static void main(String[] args) {
System.out.println("main begin");
//这里就不建议继续上抛给main方法了,因为main方法会继续上抛给jvm,这里就只会对异常选择终止程序
try {
n1();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
System.out.println("main over");
}
public static void n1() throws FileNotFoundException {
System.out.println("n1 begin");
//继续上抛
n2();
System.out.println("n1 over");
}
public static void n2() throws FileNotFoundException {
System.out.println("n2 begin");
//此时的异常并没有消失,这里也需要我们对异常进一步处理,这里的处理方式很多:
//继续上抛:这里可以选择IOException或Exception,当然这里也可以选择多个异常处理
n3();
System.out.println("n2 over");
}
public static void n3() throws FileNotFoundException {
System.out.println("n3 begin");
//分析保存原因:这里调用了一个构造方法,这个构造方法的底层如下:这个构造方法的声明位置上有一个编译时异常FileNotFoundException
/*public FileInputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null);
}*/
//此时我们有两种处理方法:自己处理异常或者选择上抛给调用者处理(此处我们选择上抛给调用者)
new FileInputStream("文件路径...");
System.out.println("n3 over");
}
}
控制台输出:
main begin
n1 begin
n2 begin
n3 begin
main over
java.io.FileNotFoundException: 文件路径... (系统找不到指定的文件。)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at ExceptionTest04.n3(ExceptionTest04.java:44)
at ExceptionTest04.n2(ExceptionTest04.java:33)
at ExceptionTest04.n1(ExceptionTest04.java:25)
at ExceptionTest04.main(ExceptionTest04.java:15)
注意:采用上报方式抛异常,此方法后续的代码不会执行,另外try语句块中某一行出现异常,该行后面的代码不会执行,try...catch捕捉异常后,后续的代码可以执行
2、try...catch捕获异常:
/**
* @author Jason
* @create 2020-07-06 9:34
* try...catch形式捕获异常深入学习
*/
public class ExceptionTest05 {
public static void main(String[] args) {
//第一种:
/*try {
FileInputStream fileInputStream = new FileInputStream("文件路径...");
System.out.println("上面出现异常,这里无法执行...");
//这里可以是具体异常的catch
} catch (FileNotFoundException e) {
e.printStackTrace();
}*/
//第二种:
/*try {
FileInputStream fileInputStream = new FileInputStream("文件路径...");
System.out.println("上面出现异常,这里无法执行...");
//这里可以是直接父类异常的catch
} catch (IOException e) { //多态:IOException e = new FileNotFoundException();
e.printStackTrace();
}*/
//第三种:
/*try {
FileInputStream fileInputStream = new FileInputStream("文件路径...");
System.out.println("上面出现异常,这里无法执行...");
//这里可以是父类异常的catch
} catch (Exception e) { //多态:Exception e = new FileNotFoundException();
e.printStackTrace();
}*/
//JDK8新特性
try {
FileInputStream fileInputStream = new FileInputStream("文件路径...");
System.out.println(5/0);
} catch (FileNotFoundException | ArithmeticException |NullPointerException e) {
e.printStackTrace();
}
}
}
3、我们在开发中有上抛异常、有try...catch捕获那么到底选择哪个呢?
希望调用者来处理,选择throws上报,其他情况使用try...catch方式捕获。
getMessage方法和printStackTrace方法
/**
* @author Jason
* @create 2020-07-06 10:19
* getMessage方法和printStackTrace方法
*/
public class ExceptionTest06 {
public static void main(String[] args) {
//getMessage方法:获取异常简单的描述信息
NullPointerException nullPointerException = new NullPointerException("空指针异常...");
String message = nullPointerException.getMessage();
System.out.println(message);
//printStackTrace方法:打印异常追踪的堆栈信息
try {
m1();
} catch (FileNotFoundException e) {
e.getMessage();
e.printStackTrace();
}
}
public static void m1() throws FileNotFoundException {
m2();
}
public static void m2() throws FileNotFoundException {
m3();
}
public static void m3() throws FileNotFoundException {
FileInputStream fileInputStream = new FileInputStream("文件路径...");
}
}
控制台打印信息:
空指针异常...
java.io.FileNotFoundException: 文件路径... (系统找不到指定的文件。)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at ExceptionTest06.m3(ExceptionTest06.java:34)
at ExceptionTest06.m2(ExceptionTest06.java:30)
at ExceptionTest06.m1(ExceptionTest06.java:26)
at ExceptionTest06.main(ExceptionTest06.java:18)
注意:异常信息需要从上往下看,很多使用后面的异常是因为前面引起的。
try...catch中的finally子句
如果没有finally语句,前面的语句一旦出现了异常,后面的语句就不会被执行,如果是需要被关闭的流呢?
但是流又必须关闭,因为它是需要占用资源的,所以这里就体会到了finally的关键。通过把流放到finally中,这样就可以确保他一定被执行到,也就不用担心他的关闭问题了。
下面看看这个简单的实例代码:
/**
* @author Jason
* @create 2020-07-06 10:39
* try...catch中的finally子句
*/
public class ExceptionTest07 {
public static void main(String[] args) {
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream("F:\\学习资料\\JavaSE-资料\\001-JavaSE课堂笔记+思维导图\\02-JavaSE零基础每日复习与笔记\\day18课堂笔记.txt");
String str = null;
str.toString();
System.out.println("Hello Exception!");
//注意:此处不会被执行,由于上面的代码行出现了异常,但是流又必须关闭,因为它是需要占用资源的,所以这里就体会到了finally的关键
//fileInputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}catch (NullPointerException e){
e.printStackTrace();
}finally {
//将流放到finally中关闭是最保险的,因为finally中的代码无论如何都是会被执行到的
if (fileInputStream != null){ //避免空指针异常
try {
System.out.println("Hello Jason!");
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println("Hello World!");
}
}
控制台输出结果:
java.lang.NullPointerException
at ExceptionTest07.main(ExceptionTest07.java:16)
Hello Jason!
Hello World!
final finally finalize有什么区别?
1、final是关键字,表示最终的,不变的 ,如:final int i = 10;
2、finally关键字和try联合使用,经常在异常处理机制中使用,finally语句中的代码一定会执行
3、finalize是object类中的一个方法,作为方法名出现,finalize是标识符
自定义异常
1、为何需要自定义异常?
JDK中的异常不够用,在实际开发中,有很多业务出现异常后JDK中没有这些和业务相关的异常,所有这个时候就需要我们自己自定义异常。
2、如何自定义异常呢?
第一步:编写一个类继承Exception类或RuntimeException
第二步:提供两个构造方法,一个是无参数的,一个是有String类型参数的构造方法
3、自定义异常实例:
/**
* @author Jason
* @create 2020-07-06 15:26
* 自定义编译时异常
*/
public class ExceptionTest09 {
public static void main(String[] args) {
MyException e = new MyException("用户名不能为空!");
String message = e.getMessage();
System.out.println(message);
e.printStackTrace();
}
}
/**
* @author Jason
* @create 2020-07-06 15:29
* 自定义编译时异常
*/
public class MyException extends Exception{
public MyException(){}
public MyException(String s) {
super(s);
}
}
控制台输出结果:
用户名不能为空!
MyException: 用户名不能为空!
at ExceptionTest09.main(ExceptionTest09.java:7)
自定义异常的使用:
/**
* @author Jason
* @create 2020-07-06 15:53
*/
public class ExceptionTest10 {
public static void main(String[] args) {
// 创建栈对象
MyStack stack = new MyStack();
// 压栈
try {
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
// 这里栈满了
stack.push(new Object());
} catch (MyStackOperationException e) {
// 输出异常的简单信息。
System.out.println(e.getMessage());
}
// 弹栈
try {
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
// 弹栈失败
stack.pop();
} catch (MyStackOperationException e) {
System.out.println(e.getMessage());
}
}
}
/**
* @author Jason
* @create 2020-07-06 15:50
*/
public class MyStack {
private Object[] elements;
private int index;
/**
* 无参数构造方法。默认初始化栈容量10.
*/
public MyStack() {
// 一维数组动态初始化
// 默认初始化容量是10.
this.elements = new Object[10];
// 给index初始化
this.index = -1;
}
/**
* 压栈的方法
* @param obj 被压入的元素
*/
public void push(Object obj) throws MyStackOperationException {
if(index >= elements.length - 1){
throw new MyStackOperationException("压栈失败,栈已满!");
}
// 程序能够走到这里,说明栈没满
// 向栈中加1个元素,栈帧向上移动一个位置。
index++;
elements[index] = obj;
// 在声明一次:所有的System.out.println()方法执行时,如果输出引用的话,自动调用引用的toString()方法。
System.out.println("压栈" + obj + "元素成功,栈帧指向" + index);
}
/**
* 弹栈的方法,从数组中往外取元素。每取出一个元素,栈帧向下移动一位。
* @return
*/
public void pop() throws MyStackOperationException {
if(index < 0){
throw new MyStackOperationException("弹栈失败,栈已空!");
}
// 程序能够执行到此处说明栈没有空。
System.out.print("弹栈" + elements[index] + "元素成功,");
// 栈帧向下移动一位。
index--;
System.out.println("栈帧指向" + index);
}
// set和get也许用不上,但是你必须写上,这是规矩。你使用IDEA生成就行了。
// 封装:第一步:属性私有化,第二步:对外提供set和get方法。
public Object[] getElements() {
return elements;
}
public void setElements(Object[] elements) {
this.elements = elements;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
/**
* @author Jason
* @create 2020-07-06 15:52
*/
public class MyStackOperationException extends Exception{ // 编译时异常!
public MyStackOperationException(){
}
public MyStackOperationException(String s){
super(s);
}
}
注意:
重写之后的方法不能比重写之前的方法抛出更多的异常,可以更少