java如何抛出异常:throw与throws用法

java如何抛出异常,核心依靠throw关键字手动抛出异常实例,搭配throws在方法声明上声明可能抛出的受检异常两种机制;throw用于方法执行内部主动触发异常对象,throws只做异常声明、把处理责任转移给上层调用方,受检异常(Exception子类,不含RuntimeException)必须使用throws声明或者内部try-catch捕获,非受检异常RuntimeException及其子类可以不声明直接抛出,该机制遵循Java语言规范JLS(JavaLanguageSpecification)第11章异常章节定义,适合后端业务校验、参数合法性判断、分支错误标识这类编码场景,不适用于正常业务流程的分支跳转。

java如何抛出异常:throw手动抛出实例

你使用throw时,后面必须紧跟一个Throwable类型的实例,Throwable是Java异常体系的顶层父类,绝大多数业务场景会选用Exception或者RuntimeException的子类,写法为thrownewIllegalArgumentException("参数id不能为空");,执行到这一行代码时,当前方法立刻终止后续语句执行,异常对象沿着调用栈向上传递,直到匹配到对应的catch块被捕获处理,没有匹配catch则继续向上传播,最终会终止线程。throw支持自定义异常类,你只需要继承Exception或者RuntimeException,编写带String消息、带Throwablecause构造器,就可以在业务代码里new之后throw出去,携带业务专属错误码与提示信息。

throw不能抛出基本数据类型、普通POJO对象,只允许Throwable及其子类实例,如果你new的对象不是Throwable体系,编译阶段就会报错。有一种常见错误写法是只写throw关键字忘记new实例,编译直接失败,这类写法无法生成异常对象,不能触发异常传播流程。

java如何抛出异常:throws声明受检异常

throws写在方法签名的圆括号之后,用来声明该方法可能向外抛出一个或多个受检异常,多个异常使用逗号分隔,例如publicUserqueryUser(Longid)throwsSQLException,IOException{},它本身不会触发异常,只是编译期约束,告诉调用这个方法的上层代码,必须要么用try-catch捕获该异常,要么继续在调用方法上追加throws继续向上传递。这里需要区分边界:RuntimeException、Error以及它们的子类属于非受检异常,即便不在方法上写throws,也可以通过throw抛出,编译器不会强制要求上层处理。

对比维度throwthrows
位置方法体内部方法声明签名处
作用实际抛出异常对象,触发传播声明异常,转移处理责任
数量一次只能抛出一个异常实例可声明多个异常类型
编译约束抛出受检异常时,仍需要配合throws或try-catch仅对受检异常产生强制编译校验

java如何抛出异常:自定义异常与链式异常

链式异常是抛出异常时保留原始异常根因,构造方法传入cause原始Throwable,写法thrownewServiceException("查询失败",e);,上层捕获后可以通过getCause()拿到底层原始异常,方便排查多层调用栈里的真实错误来源,在数据库、远程调用封装代码里使用较多。自定义异常如果继承Exception,属于受检异常,方法签名必须加throws;如果继承RuntimeException,属于非受检异常,无需throws声明,编码自由度更高,业务项目大多优先选择RuntimeException子类,减少层层throws带来的方法签名污染。

该方式的适用边界是,不要把异常当成正常业务返回标识,比如把查询无数据当作异常抛出,这类场景优先使用返回值、Optional标识,频繁用异常控制正常流程会增加JVM创建异常栈轨迹的开销,在高并发接口下会明显影响吞吐性能。

java如何抛出异常:抛出后的调用栈传播规则

异常抛出之后,JVM会沿着方法调用栈逐层回溯,查找匹配类型的catch处理器,catch匹配遵循类型兼容原则,子类异常可以匹配父类catch块,编写catch时要把更具体的子类异常写在前面,父类异常写在后面,否则会出现子类catch永远无法命中的编译报错。如果整条调用链全部没有catch,异常最终到达线程入口,线程会打印完整堆栈并终止当前线程,主线程场景下会导致整个程序退出。

你在抛出异常前,可以按需填充异常栈信息,new出来的异常对象默认会采集当前线程调用栈,如果你复用已经存在的异常实例反复throw,栈信息不会自动刷新,需要调用fillInStackTrace()重新采集调用栈。

finally块会在异常传播过程中执行,即便已经执行throw语句,只要进入try块,退出前依然会运行finally里的资源释放代码,适合数据库连接、流对象关闭这类资源清理操作。

JLS第11章明确规定,受检异常的声明与捕获属于编译期检查,非受检异常不受该编译校验约束,你在跨模块开发时,团队编码规范可以额外约定是否对RuntimeException也写throws声明,这类约定属于项目规范,不属于Java编译器强制要求。

敬慕百科汇集百科知识与游戏文化,带你发现世界的每一个精彩角落。

想要了解更多关于java如何抛出异常的文章欢迎访问:百科