异常
一、异常的概念
异常是指程序运行时出现的特殊情况。
任何程序都存在着未知的问题,如果不处理,可能会导致程序崩溃。所以需要处理程序中的问题。
二、异常的分类
异常的顶层父类为Throwable。
分为两大子类:
Error:JVM或者硬件问题,无法在程序中解决,不用处理。
Exception:程序中需要处理的异常。
RuntimeException:运行时异常(未检查(非受检)异常)。这类的异常一般是程序员没有进行足够的检查判断,属于程序员的bug,不需要使用异常处理的方式处理,应该加入检查的代码。
java.lang.ArithmeticException:算术异常(除数为0)
下标越界异常
空指针异常
类型转换异常
CheckedException:已检查(受检)异常。(非运行时异常)程序员已经进行了足够的检查,但是仍旧无法阻止异常的产生。出现异常是由用户引发的,此类异常无法检查,只能使用异常处理。
注意:运行时异常不会提示需要异常处理,而已检查异常会提示需要异常处理。
1、请简述异常的分类。
2、请列举常见的运行时异常。
三、异常的产生
1、程序产生异常结果。
2、手动抛出异常。
当代码中出现不满足要求的情况时,需要代码中止执行,直接报错,可以抛出异常。当产生异常时,相当于遇到了return,程序会中止。
public class MyArrays {
//3) 顺序查找
//返回该数字的下标,如果没有,则返回-1
public static int search(int[] numbers, int number) {
if(numbers == null || numbers.length == 0) {
// System.out.println("数组不能为空");
throw new RuntimeException("数组不能为空");
}
else {
for (int i = 0; i < numbers.length; i++) {
if(numbers[i] == number) {
return i;
}
}
return -1;
}
}
}
四、异常的传递
当代码中有已检查异常出现,又不想处理时,可以在方法后面声明异常,使用throws关键字,可以声明多个异常,使用逗号隔开。
如果在代码中不处理异常,只是将异常抛出,同时声明异常,那么该异常将由方法的调用者处理。最终会抛给JVM,JVM会直接打印异常。
public class Test2 {
public static void main(String[] args) throws Exception {
a();
}
public static void a() throws Exception {
b();
}
private static void b() throws Exception {
c();
}
private static void c() throws Exception {
d();
}
private static void d() throws Exception {
throw new Exception("出错啦");
}
}
五、异常的处理
在遇到异常时,可以选择处理异常。
处理异常涉及的关键字为:try、catch、finally
语法结构:
try{
// 尝试去执行的,可能出现异常的代码
}catch(Exception e){
// 当遇到异常,捕获该异常,并进行处理
}finally{
// 最终一定会执行的代码
}
public class Test3 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = 5;
System.out.println("请输入一个数字:");
String numStr = input.nextLine();
int num;
try {
// 可能会出现异常的代码
num = Integer.parseInt(numStr);
int m = 20 / num;
int s = n * m;
System.out.println("s的值为:" + s);
}
catch (ArithmeticException e) {
// 当出现了指定异常时
System.out.println("出错啦,除数为0");
num = 1;
int m = 20 / num;
int s = n * m;
System.out.println("s的值为:" + s);
}
catch (NumberFormatException e) {
// 当出现了指定异常时
System.out.println("出错啦,不是数字");
num = 2;
int m = 20 / num;
int s = n * m;
System.out.println("s的值为:" + s);
}
catch (Exception e) {
// 当出现了指定异常时
System.out.println("出错啦");
num = 1;
int m = 20 / num;
int s = n * m;
System.out.println("s的值为:" + s);
}
}
}
- 在try中代码,如果某一句代码出错,那么会直接跳转到异常的捕获,那么try中后面其他的代码将不会执行。不要将大量的代码放到一个try中。
- 可以写多个catch,当出现异常时会从上至下匹配异常的类型,进行捕获。如果写了Exception这种父类异常,应该放到最后,作为一个补救措施,如果放在前面,后面的异常处理会报错。
finally表示一定会执行的,不管是否报错。
语法可以try-finally,也可以try-catch-finally。
public class Test4 {
public static void main(String[] args) {
a();
}
public static int a() {
try {
System.out.println("1");
int n = 5 / 0;
return 1;
}catch (Exception e) {
System.out.println("2");
return 2;
}finally {
System.out.println("3");
}
}
}
注意:finally里面一定会执行,在return前执行。finally块一般用来释放或关闭资源。
// 简单题:
public class Test5 {
public static void main(String[] args) {
int n = a();
System.out.println(n); // n的值为3
}
private static int a() {
try {
int n = 5 / 0;
return 1;
} catch (Exception e) {
return 2;
}finally {
return 3;
}
}
}
// 中等题
public class Test5 {
public static void main(String[] args) {
int n = a();
System.out.println(n); // 值为3
}
private static int a() {
int n = 1;
try {
n = 5 / 0;
n = 2;
return n;
} catch (Exception e) {
n = 3;
// 将n的值3放入栈顶,准备返回,此时栈顶是3
return n;
}finally {
n = 4; // 将n的值改为4
}
}
}
// 中等题
public class Test6 {
public static void main(String[] args) {
int [] n = a();
System.out.println(n[0]); //值为4
}
private static int [] a() {
int [] arr = {1,2,3};
try {
int n = 5 / 0;
arr[0] = 2;
return arr;
} catch (Exception e) {
arr[0] = 3;
return arr;
}finally {
arr[0] = 4;
}
}
}
// 高级题
public class Test6 {
public static void main(String[] args) {
int [] n = a();
System.out.println(n[0]); // 值为7
}
private static int [] a() {
int [] arr = {1,2,3};
try {
int n = 5 / 0;
arr = new int[] {4,5,6};
return arr;
} catch (Exception e) {
arr = new int[] {7,8,9};
return arr;
}finally {
arr = new int[] {1,1,1};
}
}
}
六、自定义异常
注意:异常处理的方式的选择:
有能力处理就处理,没有能力处理的,如果调用者有能力处理时就抛出。
能够由调用者重新调整调用参数就能解决的异常,可以抛出,如果调用者也无能为力的,就处理(使用try-catch处理并记录日志,等待管理员处理)。
系统的异常是用来处理常规异常的,如果项目中需要处理业务异常,需要自定义业务异常。
下面的案例通过自定义一个登录异常来捕获业务异常。
public class LoginException extends Exception{
public LoginException(String message) {
super(message);
}
public LoginException() {
super();
}
}
public class Test7 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = input.nextLine();
System.out.println("请输入密码:");
String password = input.nextLine();
try {
login(username, password);
}catch(LoginException e) {
e.printStackTrace();
}
catch (Exception e) {
e.printStackTrace();
}
}
private static void login(String username, String password) throws LoginException {
if(username.equals("zhangsan") && password.equals("666666")) {
System.out.println("登录成功");
}else {
throw new LoginException("用户名或密码错误");
}
}
}