java基础之异常
本篇重点
1.异常概念与体系结构
2.异常的处理方式
3.异常的处理流程
4.自定义异常类
1.异常概念与体系结构
1.1异常的概念
在生活中,我们身体经常会因为各种原因而出现毛病,当身体出现问题时,我们需要去医院看病,保护我们的身体,而程序也和人一样,一个好的程序员不仅需要拥有编程能力,更要有寻找漏洞,解决在编程时出现的各种问题,只有在不断的调试,解决问题,我们才能不断进步!
在java中,将程序执行时发生的不正常行为称为异常,比如我们在写代码时经常遇见的:算术异常,数组越界,数据格式不对······
(1)算术异常
因为0无法做除数,所以会报错


(2)数组越界
当我们访问数组时,超出数组的长度,就会造成数组越界


(3)空指针异常
在java中,空指针(null)是无法被访问的,当强行访问null时,会报错


同时,根据上面可以知道,不同的异常类型会有不同的类来进行描述
1.2异常的体系结构
在写代码的时候我们会犯不同的错误,为了更好的整理和归类,java内部维护了一个异常的体系结构
下图是异常的体系结构图

上图我们可知:
1.Throwable是异常体系的顶层类,它有两个非常重要的字类:Error和Exception
2.Error指的是Java虚拟机无法解决的严重问题,比如:JVM的内部错误,资源耗尽等,例如:StackOverflowError和OutOfMemoryError,一旦发生回力乏术。
3.Exception:异常完成后程序员可以通过代码进行处理,使程序继续执行,比如:感冒,发烧,我们平时所说的异常就是指它
1.3异常的分类
异常可能在编译时发生,也可能在运行时发生,根据发生的时机不同,我们把异常分为:编译时异常/受查异常和运行时异常/非受查异常。
注意:编译时出现的语法性错误不能称为异常,例如:句子末尾忘记添加分号。这是在“编译期”出现的错误。
2.异常的处理
2.1防御式编程
在操作之前就做充分的检查,叫事前防御型
int []arr={1,4,5};
int n=0;
if(n<0){
//处理数组越界异常
return;
}
这样做的缺点很明显,将正常的代码和处理异常的代码杂糅在一起,代码的整体较混乱,同时也影响读者的阅读。
事后认错型:先操作,出现问题再解决
try{
int []arr={1,4,5};
int n=0;
}catch(n<0){
//处理数组越界异常
}
优势:正常流程和处理异常流程分开,程序员更关注正常流程,代码更清晰,是代码中主要用的核心思想。
java中处理异常的五个关键字: throw,try,catch,final,throws
2.2异常的抛出
可借助关键字throw抛出一个指定的异常对象,将信息告知给调用者
throw new xxxException("异常的原因");
重点:
1.throw必须写在方法体内部;
2.抛出的对象必须是RunTimeException或者Exception的子类对象
3.如果抛出的是RunTimeException或者RunTimeException的子类,则可以不用处理,直接交给JVM来处理
4.如果抛出的是编译时异常,用户必须处理,否则无法通过编译
5.异常一旦抛出,后面的代码就不会再执行
2.3异常的捕获
异常的捕获通常有两种方式:异常声明throws 和 try-catch捕获异常
异常声明throws
处在方法声明是参数列表的后面,当方法中抛出异常时,用户不想处理异常就可以通过借助throws将异常抛给方法的调用者来处理,提醒方法的调用者处理异常
语法格式
修饰符 返回值类型 方法名 (参数列表) throws异常类型1,异常类型2 ...{ }
注意:
1 throws必须跟在方法参数列表的后面;
2 声明的异常必须是Exception及其子类;
3 如果方法抛出多个异常,throws必须跟多个异常,之间用逗号隔开,如果抛出的异常具有父子关系,那只需要声明父类就好了;
4 抛出异常时,调用者必须对该异常进行处理或者选择继续使用throws抛出。
public class ThrowsExample {
// 1. 定义一个方法,声明可能抛出算术异常(ArithmeticException)
// throws 关键字后跟上异常类型,表明该方法不处理这个异常,交给调用者处理
public static int divide(int a, int b) throws ArithmeticException {
// 除数为0时,会自动抛出ArithmeticException
return a / b;
}
public static void main(String[] args) throws ArithmeticException{
// 2. 调用声明了throws的方法,必须处理异常(try-catch),否则编译报错
int result1 = divide(10, 2);
System.out.println("10/2 = " + result1);
int result2 = divide(10, 0);
System.out.println("10/0 = " + result2);
}
try-catch 捕获并处理
上例中的throws并没有对异常进行处理,而是将异常报告给抛出异常方法的调用者,由调用者处理,如果要真正对异常进行处理,就需要try-catch。
语法:
try{
//将可能异常的代码放在这里
}catch(可能出现的异常类型 e){
//对异常进行处理
}[
//可能处理多个异常
catch(可能出现的异常类型 e){
//对异常进行处理
}
finally{
//此代码一定会被执行
}
]
//后续代码,如果上面的异常被正确处理,则这里的代码就会被执行,如果上面出现的异常没有被捕获到或者是处理不正确,这里的代码就不会被执行
注意:
1.try中的代码可能会报错也可能不会报错!
2.try内抛出异常之后的代码不在执行
3.如果try抛出的异常没有被catch捕捉到,就不会被处理 会继续外抛,直到jvm收到后中断程序
4.try中可能会抛出多个不同的异常对象,必须用catch来捕获一种或多种异常,多次捕获
5.由于Exception类是所有异常类的父类,可以用这个类型捕获所有异常,但是不建议使用
关于异常的处理方式:
异常的种类较多,要根据不同的业务处理
对于比较严重的问题(例如算钱),应该让程序崩溃,防止造成更严重的后果
对于不太严重的问题(日常业务),可以记录错误日志,并通过监视报警程序及时提示
对于可能恢复的问题(和网络有关),可以尝试就行重试
如果多个异常的处理方式相同
如果异常具有父子关系,一定是子类异常在前,父类异常在后,如果相反的话会出现语法错误;
finally
在写一些代码的时候,有一些代码无论是否出现异常都要执行,例如程序中打开的资源,最后必须进行收回,另外因为出现异常而可能无法执行一些代码,finally可以很好的解决这个问题
注意:finally的代码一定会被执行,与有无异常或异常是否正确被处理无关。
finally执行的时机是在方法返回之前,如果finally和try中同时有return语句,则会返回finally中的return语句,try中的语句不会被执行。因此我们一般不会在finally中写return
2.4异常的处理流程
关于“调用栈”
方法之间存在互相调用关系,这种关系我们可以用“调用栈”来描述,在JVM中有一块内存空间称为“虚拟机栈”专门存储方法之间的调用关系,当代码中出现异常的时候,我们可以使用e.printStackTrace();的方法查看异常代码的调用栈。
如果本方法中没有合适的处理异常的方法,就会沿着调用栈向上传递
如果向上一直传递都没有合适的方法处理异常,最终会交给JVM处理,程序就会异常终止(与我们最开始没有使用try-catch时是一样的)
2.5异常处理流程总结
1)程序先执行try中的代码;
2)如果try中的代码出现异常,就会结束try的代码,查看try中出现的异常与catch的异常是否匹配;
3)如果匹配,就会先执行catch的代码;
4)如果没有找到匹配的异常,就会向上传递交给上层调用者;
5)finally中的代码无论如何一定会被执行;
6)如果上层调用者也处理得了异常,就会一直向上传递;
7)一直到main方法也没有合适的代码处理异常,就会交给JVM处理,程序就会异常终止
3.自定义类异常(重要)
java中虽然设置了丰富的异常代码,但是实际开发中我们会遇到各种各样的异常,有可能遇到的代码异常没有被定义到,此时就需要我们维护符合实际情况的异常结构
具体方法:
1.自定义一个异常类,继承Exception 或者RunTimeException
2.实现一个带String类型参数的构造方法,参数含义:出现异常的原因
例子:实现一个登录异常的代码
class UserNameException extends Exception{
public UserNameException(String message){
super(message);
}
}
class PasswordException extends Exception{
public PasswordException(String message){
super(message);
}
}
自定义异常通常会继承自Exception(受查异常)或RuntimeException(非受查异常)
例子:写一个登录异常
不使用自定义异常类
class Logic{
public String userName;
public String userPassword;
public void checkUserName(String userName,String userPassword) {
//判断登录的名字是否正确
if (!this.userName.equals(userName)) {
System.out.println("登录账户错误..");
}
//判断登录密码是否正确
if(!this.userPassword.equals(userPassword)){
System.out.println("登录密码错误..");
}
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
}
public class Test {
public static void main(String[] args) {
Logic logic=new Logic();
logic.setUserName("张三");
logic.setUserPassword("123");
logic.checkUserName("李四","456");
}
}
名称和密码均错误,因此登录失败

使用自定义异常类
定义一个账户名称异常类
public class UserNameException extends RuntimeException{
public UserNameException() {
}
public UserNameException(String message) {
super(message);//显示异常原因
}
}
定义一个账户密码异常类
public class UserPassWordException extends RuntimeException{
public UserPassWordException() {
}
public UserPassWordException(String message) {
super(message);//打印异常原因
}
}
class Logic {
public String userName;
public String userPassword;
public void checkUserName(String userName,String userPassword) {
if(!this.userName.equals(userName)){
throw new UserNameException("账户名称错误");
}
if(!this.userName.equals(userPassword)){
throw new UserPassWordException("账户密码错误");
}
System.out.println("登录成功...");
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
}
public class Test {
public static void main(String[] args) {
try{
Logic logic=new Logic();
logic.setUserPassword("123");
logic.setUserName("张三");
logic.checkUserName("张三","456");
}catch(UserNameException e){
e.printStackTrace();
}catch (UserPassWordException e){
e.printStackTrace();
}
}
}

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)