day18 异常
异常的描述
1.异常:开发过程中发生了一些错误,或者是不正常的情况(可以在代码中进行处理,甚至可以避免)。
2.异常解决的方式:
1. 异常声明或抛出
2. 异常捕获,解决异常
3. 代码终止
3.error:错误,表示代码中发生的比较严重的问题,一般都会去解决,只能去修改代码。比如:内存溢出。
4.体系:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e9x35dmM-1575467332904)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20191204200733601.png)]
Throwable:所有错误和异常的顶层父类
Error:错误
RuntimeException:运行时异常(出现了这种异常,代码没有提示你进行处理)
编译异常
Exception:异常
5.jvm异常处理机制:发生异常后,谁也不处理,一层一层向上抛出异常,最后抛到jvm处,jvm打印异常信息,代码停止。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lJo9ELRa-1575467332906)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20191204201539209.png)]
6.手动异常处理方式
-
异常声明:代码运行过程中(方法中),发生了异常,在方法上进行这个一行的声明动作,作用就是告知方法的调用者,我这个方法运行时,可能有问题,就是为了让方法的调用者进行适当的异常处理。
注意:如果是运行时异常,不需要声明处理;如果是编译异常,需要声明。
-
异常处理:代码运行过程中,发生了异常,在方法内部,可能发生异常的代码,进行处理,处理完毕,代码可以不用停止,继续运行。
-
(1)try…catch(2)try…catch…finaly(3)try…finaly
异常
1. throw关键字:抛出异常
-
throw关键字:表示抛出,用于抛出异常
-
使用场景:数据合法性验证,或者可能会发生问题
-
语法结构:
throw new 异常()
-
注意事项
(1)throw后每次只能创建出一个异常
(2)如果代码中有多个异常,那么可多次使用throw进行异常的抛出
(3)代码中执行到throw,方法中throw之后的代码都不在运行。
public class ThrowException{
public static void main(String[] args) {
// TODO Auto-generated method stub
//int[] arr = {2, 5, 4, 9, 7, 1};
int[] arr = null;
getElement(arr, 6);
}
public static void getElement(int[] arr, int index) {
//控制数组空指针异常
if(arr==null||arr.length==0) {
throw new NullPointerException("数组不能为空,或没有元素");
}
//控制数组越界异常
if(index >= arr.length) {
throw new ArrayIndexOutOfBoundsException("提供的索引"+index+"在数组中不存在");
}
System.out.println(arr[index]);
}
}
2. throws:声明异常
-
throws关键字:功能就是进行异常声明,在方法上进行这个异常的声明动作。
-
语法结构:
修饰符 返回值类型 方法名(参数列表) throws 异常1, 异常2,…{
方法逻辑
}
3.throws使用场景
-
如果方法内部抛出的是运行时异常,那么可以不声明
-
如果方法内部抛出的异常是编译异常,必须声明(或处理)
-
throws特点:
(1)throws只是作为异常的声明,作用,通知方法的调用者,方法内部可能会出现异常,提示方法调用者做一个提前处理。
(2)throws关键字后面可以写多个异常,异常之间使用逗号分隔
(3)方法中的运行异常,不需要声明,编译异常,需要声明或处理
-
代码
/*
* throws:异常声明
* */
public class ThrowsException {
public static void main(String[] args) throws ParseException {
// TODO Auto-generated method stub
//int[] arr = {2, 5, 4, 9, 7, 1};
int[] arr = null;
getElement(arr, 6);
dateException();
}
public static void dateException() throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String date = "2019-09-09";
//将字符串解析成日期
sdf.parse(date);
}
public static void getElement(int[] arr, int index) throws NullPointerException, ArrayIndexOutOfBoundsException{
//控制数组空指针异常
if(arr==null||arr.length==0) {
throw new NullPointerException("数组不能为空,或没有元素");
}
//控制数组越界异常
if(index >= arr.length) {
throw new ArrayIndexOutOfBoundsException("提供的索引"+index+"在数组中不存在");
}
System.out.println(arr[index]);
}
}
3.throw与throws关键字之间的区别
-
定义位置不同:
(1)throw定义在方法内部
(2)throws定义在方法上
-
功能不同
(1)throw表示异常的抛出,直接把发生的异常抛出
(2)throws表示异常的声明,只是给方法的调用者一个提醒
-
异常个数不同
(1)throw每次只能创建和抛出一个异常
(2)throws可以声明多个异常
Try…catch:异常处理
-
try:关键字,功能就是对异常信息的检测
catch:关键字,功能就是捕获异常
-
语法结构
try{
// 可能会发生异常的代码
}catch(异常类型 名字){
// 异常的处理方式
}...finally{
// 一定会执行的代码
}
- try…catch的运行方式
-
先执行try中的代码, 如果没有异常发生,将try中的内容执行完毕,代码继续向下运行
-
如果try代码中发生了异常, try会将异常进行抛出, 抛给catch代码块, catch进行异常信息的匹配, 如果匹配执行catch代码块中的内容,执行完毕,代码接着try…catch之后继续运行; 如果catch没有将发生的异常正确匹配, 那么异常还是要抛给JVM进行自动处理
-
运行代码
public class TryCatchException { public static void main(String[] args) { int i = 5; try { int m = i/0; System.out.println(m); } catch(ArithmeticException e){ //System.out.println("除数不能为0"); e.printStackTrace(); } System.out.println("hello world"); } }
多Catch异常处理
-
语法结构
public class TryCatchException { public static void main(String[] args) { int i = 5; try { int m = i/0; System.out.println(m); } catch(ArithmeticException e){ //System.out.println("除数不能为0"); e.printStackTrace(); } System.out.println("hello world"); } }
-
运行
- 先执行try中的代码块内容,没有异常,还不执行catch,继续代码向下运行
- 如果try中出现异常,从出现异常的代码位置直接跳转到catch语句, 将try中的异常信息依次(从上到下)与catch中的异常信息进行匹配, 匹配上某一个异常信息那么执行对应catch中的异常处理逻辑, 逻辑执行完毕,整个try…catch运行完毕,代码继续向下运行;
- 如果try中的异常与所有catch异常都不匹配,那么JVM自动处理异常
-
注意:
-
多个catch中只会执行到其中一个符合异常的匹配 ;
-
如果有多catch语句去捕获多个异常,那么一定要将子类的异常写在上面的catch中,将父类异常写在下面的catch中(因为父类异常能使用多态获取子类异常,那下面catch中的代码就没有执行到的机会,于是系统报出错误)。
-
-
运行:
public class MoreCatchDemo { public static void main(String[] args) { int[] arr = {1, 5}; try { //可能会出现异常的代码 getArrIndex(arr, 4); } catch(NullPointerException e) { System.out.println("数组不能为空"); } catch(ArrayIndexOutOfBoundsException e) { System.out.println("超过数组的长度"); } } public static void getArrIndex(int[] arr, int i) { System.out.println(arr[i]); } }
自定义异常
项目需求总有些场景需要异常信息但是JDK没有给提供, 数字范围的问题定义步骤:
-
创建出一个类, 让这个类的名字以Exception为结尾, 以Exception为结尾就是一个类型标志, 表示这个类是一个异常类
-
这个类必须作为Exception或者是RuntimeException的子类
-
如果是Exception的子类,那么这个异常类型就是编译异常(一旦出现必须声明或者处理)
-
如果是RuntimeException的子类,那么这个异常类型就是运行时异常(出现后不需要做任何处理)
-
-
可以写有些构造方法, 一般情况下异常中都可以通过构造输入字符串具体异常信息
代码:
public class MyException extends RuntimeException{//继承Exception需要在方法名后throws声明异常
//继承RuntimeException可以声明也可以不声明,不需要在方法名后声明
MyException(){}
MyException(String message){
super(message);
}
}
/*
* 测试自定义异常
* */
public class MyExceptionTest {
public static void main(String[] args) throws MyException {
int age = 11;
if(age < 0) {
throw new MyException("年龄不能为负数");
}
}
}
异常信息打印
异常信息的打印通常使用于 catch代码块中, 用于将异常的详细信息进行输出, 便于开发人员进行问题的排查
-
getMessage() : 最简洁的异常信息打印
-
toString() : 相对比较简洁的异常信息, 包括异常的类型, 简短的异常原因
-
printStackTrace() : 将异常的最详细的信息进行输出, 没有返回值类型,只有第三种是开发中比较常用的
try{
方法应用----> 牵连处很多逻辑处理----> 就会有很多的异常
}catch(Exception ex){
ex.printStackTrace(); // 为了让开发人员进行错误排查,输出的信息打印到日志中,就是代码的运行过程记载文件
}
递归
递归 : 递,表示传递,一个接着一个 ; 归 : 回归, 回归到本质
递归 : 在Java中就是表示方法的调用方式, 方法自己调用自己
递归的使用场景 : 1) 规律性很强 2) 功能的实现过程,都是方法本身的运行过程,只不过运行过程需要反复 3) 要求递归一定是随着方法的运行,数据范围越来越小
例子:
求一个数字的阶乘
5 ! = 5 * 4 * 3 * 2 * 1
4 ! = 4 * 3 * 2 * 1
代码:
package com.zgjy.digui;
public class JieChengDemo {
public static void main(String[] args) {
int total = jieCheng(5);
System.out.println(total);// 120
System.out.println(jieChengDiGui(5));// 120
}
// 1. 没有使用递归
public static int jieCheng(int n) {
// 阶乘从n乘到1结束
int total = 1;
// 1. 需要将n到1的每一个数字获取到
for(int i = n ; i >= 1 ; i--) {
total = total * i ;
}
return total;
}
/*
* 5 ! = 5 * 4 * 3 * 2 * 1 ---> 5 * 4!--->5表示参数n----> n * (n-1)!
4 ! = 4 * 3 * 2 * 1---> 4 * 3!--->4表示n---> n*(n-1)!
一次类推,到n = 1 , 结束
*
*
* 参数n为3: 表示求3!
* 1. return n*jieChengDiGui(n-1) ----> 3 * jieChengDiGui(2) ; 3 * 2 = 6
* 2. jieChengDiGui(2)---> 2 * jieChengDiGui(2-1)---->2 * jieChengDiGui(1) ; 2 * 1 = 2
* 3. jieChengDiGui(1)---> return 1;
*
* */
public static int jieChengDiGui(int n) {
/*if( n == 1 ) {
return 1;
}*/
return n * jieChengDiGui(n-1); // n * (n-1)!
}
}
内存图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cTdzUXh3-1575467332907)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20191204213848691.png)]
结论:
(1)使用递归运行方式,性能不好,因为同时有多个方法在栈内存中,内存的利用很低。
engDiGui(2) ; 3 * 2 = 6
* 2. jieChengDiGui(2)—> 2 * jieChengDiGui(2-1)---->2 * jieChengDiGui(1) ; 2 * 1 = 2
* 3. jieChengDiGui(1)—> return 1;
*
* */
public static int jieChengDiGui(int n) {
/if( n == 1 ) {
return 1;
}/
return n * jieChengDiGui(n-1); // n * (n-1)!
}
}
内存图:
[外链图片转存中...(img-cTdzUXh3-1575467332907)]
结论:
(1)使用递归运行方式,性能不好,因为同时有多个方法在栈内存中,内存的利用很低。
(2)如果写了递归,一定要给递归一个出口,出口就是return一个具体结果。