一、异常处理
1.异常的概述和体系结构
异常的概述:异常,即不正常,我们的代码出现了编译或者运行时的错误;异常包含了错误的类型、原因以及位置。
举例:
package test;
public class test {
public static void main(String[] args) {
int a = 10/0;
}
}
出现异常:
异常的体系结构:
Throwable(异常类最顶层,Object类不算)
Exception:可以处理的问题;
Error:出现的不能够处理的问题;
举例:
电脑系统中毒:重装系统就可以了(为Exception);
电脑主板坏了:买一台新电脑(为Error)。
继承关系如下:
2.异常处理方式
1)jvm处理异常方式:
如果出现异常,我们没有处理,则jvm会处理,会将异常的类型、原因以及位置显示在命令行;并且终止程序,异常代码后面的代码将不再执行。
2)自定义处理异常方式:
捕获处理:
public static void main(String[] args) {
try {
System.out.println(1);
System.out.println(10/0);
System.out.println(2);
}
catch(ArithmeticException ae) {
System.out.println("除数不能为0");
}
System.out.println(3);
}
try…catch的执行顺序:首先执行try语句,如果发现异常,异常下面的代码不再执行,直接跳入catch语句,catch语句结束后,整个try…catch语句结束;如果没有发现异常,try语句执行结束后,try…catch语句直接结束,不再执行catch语句。
try…catch语句执行机理:虚拟机捕获的异常与catch中的异常类型进行比较,若一致,则执行catch的内容。
抛出处理:抛出异常即将异常抛出,抛给调用此方法的对象处理,若主函数调用此方法,则主函数也要抛出异常给虚拟机处理。
import java.io.FileWriter;
public class test {
public static void main(String[] args) throws Exception{
function();
}
public static void function() throws Exception{
FileWriter fw = new FileWriter("a.txt");
}
}
3)处理多个异常:
处理多个异常:使用多个try…catch语句来捕获多个异常;
public static void main(String[] args) {
try {
System.out.println(10/0);
}
catch(ArithmeticException ae) {
System.out.println("出现除0异常");
}
try {
int arr[] = new int[5];
System.out.println(arr[8]);
}
catch(ArrayIndexOutOfBoundsException ae) {
System.out.println("出现数组越界异常");
}
}
处理多种类异常:使用一个try和多个catch来捕获异常;
注:多个catch之间可以有子父类的关系,平级之间没有顺序关系,如果有子父类等级关系,父类异常必须放在后面;
public static void main(String[] args) {
try {
int arr[] = new int[5];
System.out.println(arr[8]);
System.out.println(10/0);
}
catch(ArithmeticException ae) {
System.out.println("出现除0异常");
}
catch(ArrayIndexOutOfBoundsException ae) {
System.out.println("出现数组越界异常");
}
catch(Exception ae) {
System.out.println("出现异常了");
}
}
4)Throwable的常用方法:
String getMessage():异常出现原因;
String toString():异常出现的类型+原因;
void printStackTrace():异常出现的类型+原因+位置。
public static void main(String[] args) {
try {
System.out.println(10/0);
}
catch(ArithmeticException ae) {
//System.out.println(ae.getMessage());
//System.out.println(ae.toString());
ae.printStackTrace();
}
}
运行结果:
5)finally的概述和应用场景:
finally简述:组合try…catch使用,用于资源释放等收尾工作,无论try…catch语句如何执行,finally代码一定会执行。
格式:
try{
有可能出现问题的代码;
}
catch{
处理异常;
}
finally{
释放资源;
清理垃圾;
}
6)异常的分类:
运行时期异常:RuntimeException的子类就是运行时期的异常,在编译时期可以自由选择处理或者不处理;
编译时期异常:是Exception的子类,非RuntimeException的子类,在编译时期必须处理。
3.自定义异常
如何定义一个异常类:非常简单,写一个类去继承Exception或者RuntimeException,然后实现多个构造即可。
我们自定义的异常类(有两种定义方式,一种是继承RuntimeException,另一种是继承Exception)如下所示:
public class MyException extends RuntimeException{ //继承RuntimeException,为运行时异常,若继承Exception为编译时异常
public MyException() {
super();
// TODO Auto-generated constructor stub
}
public MyException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
}
使用自定义的异常:
public class test {
public static void main(String[] args) {
try {
checkScore(110);
}
catch(Exception e) {
e.printStackTrace();
}
}
public static void checkScore(int score) {
if(score < 0 || score > 100) {
//throw new RuntimeException("考试成绩不符合要求");
//throw new Exception("考试成绩不符合要求"); 此时在方法后面加上throws Exception
throw new MyException("考试成绩不符合要求");
}
System.out.println("考试成绩符合要求");
}
}
运行结果:
注意:如果制造(throw)的是编译时期异常,必须在方法声明出抛出(throws)
二、递归
递归:把大问题拆成很多小问题,再把小问题拆成小问题,…,随着小问题的解决,大问题也随之解决;
实现:在方法的本身不断地调用方法本身;
递归注意事项:
递归一定要有出口,否则内存溢出;
递归次数不宜过多,否则内存溢出。
public int jc(int n) {
if(n == 1){ //为递归出口
return 1;
}
else {
return n*jc(n-1);
}
}
递归内存图解:当jc(n=1)执行后,弹出栈,将结果返回到jc(2)方法中,jc(2)执行后弹出栈…,最后将jc(5)的结果赋值给result,程序结束。
斐波那契数列:
古典问题:有一对兔子,出生后第三个月起,每个月都生一对兔子,小兔子长到第三个月后,每个月又生一对兔子,假如兔子都不死,问20个月后有多少只兔子?
分析:到第四个月时,为之前的第二个月生的兔子的第三个月,该兔子可以生兔子(可认为是复制),以此类推,除了第一、二两个月(这两个月可以作为递归程序出口),后面的月份兔子总数为前两个月之和:
第一个月:1
第二个月:1
第三个月:2
第四个月:3
第五个月:5
第六个月:8
public static int method(int n){
if(n == 1){
return 1;
}
else if(n == 2){
return 1;
}
else {
return method(n-1)+method(n-2);
}
}