目录
一、异常的概念与体系结构
1.1 异常的概念
public class Main {
public static void main(String[] args) {
System.out.println(10/0);
}
}
public static void main(String[] args) {
int[] arr = new int[3];
System.out.println(arr[3]);
}
public static void main(String[] args) {
int[] arr = null;
System.out.println(arr[0]);
}
从上述例子中不难看出
1.2 异常的体系结构
异常种类繁多,为了对不同异常或者错误进行很好的分类管理,Java内部维护了一个异常的体系结
构:
1.3 异常的分类
public class Person implements Cloneable {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1 = new Person("张三",18);
Person person2 = (Person) person1.clone();
}
}
二、异常的处理
2.1 防御式编程
boolean ret = false;
ret = 登陆游戏();
if (!ret) {
处理登陆游戏错误;
return;
}
ret = 开始匹配();
if (!ret) {
处理匹配错误;
return;
}
ret = 游戏确认();
if (!ret) {
处理游戏确认错误;
return;
}
ret = 选择英雄();
if (!ret) {
处理选择英雄错误;
return;}
ret = 载入游戏画面();
if (!ret) {
处理载入游戏错误;
return;
}
try {
登陆游戏();
开始匹配();
游戏确认();
选择英雄();
载入游戏画面();
...
} catch (登陆游戏异常) {
处理登陆游戏异常;
} catch (开始匹配异常) {
处理开始匹配异常;
} catch (游戏确认异常) {
处理游戏确认异常;
} catch (选择英雄异常) {
处理选择英雄异常;
} catch (载入游戏画面异常) {
处理载入游戏画面异常;
}
优势:正常流程和错误流程是分离开的, 程序员更关注正常流程,代码更清晰,容易理解代码
2.2 异常的抛出
在Java中,可以借助throw关键字,抛出一个指定的异常对象,将错误信息告知给调用者。
抛出的异常的方式有以下
某段程序触发
如1.1中所举例
通过关键字 throw 抛出
语法如下
throw new XXXException("异常产生的原因");
public class Test1 {
public static void main(String[] args) {
int a = 10;
if(a == 10){
throw new NullPointerException("测试!");//这里仅为演示手动抛出异常的方法
}
}
}
确实手动抛出了一个异常
throw 一般用于抛出自定义的异常
1.throw 必须写在方法体内部
2.3 异常的捕获
2.3.1异常声明throws
修饰符 返回值类型 方法名(参数列表) throws 异常类型1,异常类型2...{
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1 = new Person("张三",18);
Person person2 = (Person) person1.clone();
}
}
如果一个方法内部存在一个编译时异常,此时这个编译时异常一定要进行处理
目前处理方式是在方法定义时,通过 throws 关键字声明异常
最后这个异常是交给 JVM 处理的
public class Main {
public static void main(String[] args) {
main1();
}
public static void main1() throws CloneNotSupportedException{
Person person1 = new Person("张三",18);
Person person2 = (Person) person1.clone();
}
}
可以看到,这里编译就通过了,没有报错
那么在这里
并没有处理掉这个异常
最终还是交给 JVM 处理的
那么如何自己处理异常?
2.3.2 捕获异常
public static void main(String[] args) {
try {
main1();
}catch (CloneNotSupportedException e){
System.out.println("处理了CloneNotSupportedException 异常!");
}
}
那么通过 try-catch 手动处理和交给 JVM 处理有什么区别呢
public static void main(String[] args) {
System.out.println("first!");
int a = 10 / 0;
System.out.println("last!");
}
public static void main(String[] args) {
System.out.println("first!");
try {
int a = 10 / 0;
}catch (ArithmeticException e){
System.out.println("我处理了ArithmeticException 异常!");
}
System.out.println("last!");
}
通过这里可以对比出
交给 JVM 处理异常
这个异常发生
程序就会异常终止
而通过 try-catch 手动处理异常后
程序还可以正常运行
那么如果想知道异常出现在哪个位置该怎么办呢
可以使用 e.printStackTrace();
此处就可以报出异常出现的位置
public static void main(String[] args) {
System.out.println("first!");
try {
int a = 10 / 0;
System.out.println("666");
}catch (ArithmeticException e){
System.out.println("我处理了ArithmeticException 异常!");
e.printStackTrace();
}
System.out.println("last!");
}
那么这两种方法不存在绝对的优劣之分,需要根据具体的需求选择使用
对于比较严重的问题(例如和算钱相关的场景), 应该让程序直接崩溃, 防止造成更严重的后果
注:
public static void main(String[] args) {
System.out.println("first!");
try {
int a = 10 / 0;
}catch (NullPointerException e){
System.out.println("我处理了ArithmeticException 异常!");
e.printStackTrace();
}catch (ArrayIndexOutOfBoundsException e){
System.out.println("我处理了ArrayIndexOutOfBoundsException 异常!");
e.printStackTrace();
}
System.out.println("last!");
}
public static void main(String[] args) {
try {
int[] arr = null;
arr[1/0] = 1;
}catch (NullPointerException e){
System.out.println("我处理了NullPointerException 异常! ");
}catch (ArithmeticException e){
System.out.println("我处理了ArithmeticException 异常! ");
}
}
catch (NullPointerException e | ArithmeticException e)
捕获多种异常也可以这样写
不过比较少见
2.3.3 finally
public static void main(String[] args) {
fun();
}
public static void fun(){
System.out.println("first!");
try {
return;
} catch (ArithmeticException e) {
System.out.println("我处理了ArithmeticException 异常!");
} finally {
System.out.println("执行了finally");
}
System.out.println("last!");
}
这里可以看到,fun 方法结束后仍然执行了 finally 的代码
public static int fun2(){
try {
System.out.println(10/0);
}catch (ArithmeticException e){
System.out.println("这里返回1");
return 1;
}
finally {
return 0;
}
}
public static void main(String[] args) {
System.out.println("实际返回值是"+fun2());
}
finally 执行的时机是在方法返回之前(try 或者 catch 中如果有 return 会在这个 return 之前执行
finally). 但是如果finally 中也存在 return 语句, 那么就会执行 finally 中的 return, 从而不会执行到
try中原有的 return.
2.4 异常的处理流程
public static void func() {
int[] arr = {1, 2, 3};
System.out.println(arr[100]);
}
public static void main(String[] args) {
func();
}
如果向上一直传递都没有合适的方法处理异常, 最终就会交给 JVM 处理, 程序就会异常终止
这里先调用的 main 方法
再调用的是 func 方法
这里的向上传递值得是向上层调用者传递
public static void func() {
int[] arr = {1, 2, 3};
System.out.println(arr[100]);
}
public static void main(String[] args) {
try {
func();
}catch (ArrayIndexOutOfBoundsException e){
System.out.println("我来处理ArrayIndexOutOfBoundsException 异常了!");
}
}
那么这里既然可能会出现抛出异常的情况
就可以给 func 方法用 throws 关键字声明一下
public static void func() throws ArrayIndexOutOfBoundsException {
int[] arr = {1, 2, 3};
System.out.println(arr[100]);
}
public static void main(String[] args) {
try {
func();
}catch (ArrayIndexOutOfBoundsException e){
System.out.println("我来处理ArrayIndexOutOfBoundsException 异常了!");
}
}
三、 自定义异常类
public class User {
public static void main1(String[] args) {
}
private String username = "Java";
private String password ="123456";
public void Verify(String username,String password){
if(!this.username.equals(username)){
System.out.println("用户名错误!");
}
if(!this.password.equals(password)){
System.out.println("密码错误!");
}
if(this.username.equals(username) && this.password.equals(password)){
System.out.println("登录成功!");
}
}
}
public class UserPasswordException extends Exception{
public UserPasswordException(String message) {
super(message);
}
}
public class UserNameException extends Exception{
public UserNameException(String message) {
super(message);
}
}
public void Verify2(String username,String password) {
if(!this.username.equals(username)){
throw new UserNameException("用户名错误!");
}
if(!this.password.equals(password)){
throw new UserPasswordException("密码错误!");
}
if(this.username.equals(username) && this.password.equals(password)){
System.out.println("登录成功!");
}
}
此时继承的是 Exception
所以在使用时需要使用 throws 声明一下这个异常
public void Verify2(String username, String password) throws UserPasswordException,UserNameException {
if(!this.username.equals(username)){
throw new UserNameException("用户名错误!");
}
if(!this.password.equals(password)){
throw new UserPasswordException("密码错误!");
}
if(this.username.equals(username) && this.password.equals(password)){
System.out.println("登录成功!");
}
}
在调用方法时也需要进行捕获
public static void main(String[] args) {
User user = new User();
try {
user.Verify2("123", "666");
} catch (UserNameException e) {
e.printStackTrace();
} catch (UserPasswordException e) {
e.printStackTrace();
}
}
那么如果是继承的 RunTimeException (运行时异常)
public class UserNameException extends RuntimeException{
public UserNameException(String message) {
super(message);
}
}
public class UserPasswordException extends RuntimeException{
public UserPasswordException(String message) {
super(message);
}
}
就可以不使用 try-catch 进行捕获
public static void main(String[] args) {
User user = new User();
user.Verify3("123","666");
}
直接这样写也不会报错
本文只是概述一下异常的使用,到此结束