- 浏览: 880272 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
hzw2312:
C = sin(MLatA)*sin(MLatB)*cos(M ...
根据地球上任意两点的经纬度计算两点间的距离 -
zhang_sun:
rewind方法的limit又是多少呢?等于capacity? ...
ByteBuffer的flip,clear及rewind区别 -
kalogen:
一种每次都获取到不同的随机数的办法int ranseed=12 ...
J2ME中Random类的使用 -
kalogen:
估计部署在某个端口下吧,仔细检查一下发布的配置文件
Tomcat负载均衡和集群环境的搭建 -
zhuchao_ko:
文件大点就嗝屁了~~~
Axis 1.4 上传二进制文件(base64Binary)
简介: 关于在 Java 语言中使用异常的大多数建议都认为,在确信异常可以被捕获的任何情况下,应该优先使用检查型异常。语言设计(编译器强制您在方法签名中列出可能被抛出的所有检查型异常)以及早期关于样式和用法的著作都支持该建议。最近,几位著名的作者已经开始认为非检查型异常在优秀的 Java 类设计中有着比以前所认为的更为重要的地位。在本文中,Brian Goetz 考察了关于使用非检查型异常的优缺点。请在附带的讨论论坛中与作者和其他读者一起分享您有关本文的心得体会(您也可以点击文章顶部或底部的 讨论来访问该论坛。)
与 C++ 类似,Java 语言也提供异常的抛出和捕获。但是,与 C++ 不一样的是,Java 语言支持检查型和非检查型异常。Java 类必须在方法签名中声明它们所抛出的任何检查型异常,并且对于任何方法,如果它调用的方法抛出一个类型为 E 的检查型异常,那么它必须捕获 E 或者也声明为抛出 E(或者 E 的一个父类)。通过这种方式,该语言强制我们文档化控制可能退出一个方法的所有预期方式。
对于因为编程错误而导致的异常,或者是不能期望程序捕获的异常(解除引用一个空指针,数组越界,除零,等等),为了使开发人员免于处理这些异常,一些异常被命名为非检查型异常(即那些继承自 RuntimeException
的异常)并且不需要进行声明。
在下面的来自 Sun 的“The Java Tutorial”的摘录中,总结了关于将一个异常声明为检查型还是非检查型的传统观点(更多的信息请参阅 参考资料):
因为 Java 语言并不要求方法捕获或者指定运行时异常,因此编写只抛出运行时异常的代码或者使得他们的所有异常子类都继承自RuntimeException
,对于程序员来说是有吸引力的。这些编程捷径都允许程序员编写 Java 代码而不会受到来自编译器的所有挑剔性错误的干扰,并且不用去指定或者捕获任何异常。尽管对于程序员来说这似乎比较方便,但是它回避了 Java 的捕获或者指定要求的意图,并且对于那些使用您提供的类的程序员可能会导致问题。
检查型异常代表关于一个合法指定的请求的操作的有用信息,调用者可能已经对该操作没有控制,并且调用者需要得到有关的通知 —— 例如,文件系统已满,或者远端已经关闭连接,或者访问权限不允许该动作。
如果您仅仅是因为不想指定异常而抛出一个RuntimeException
,或者创建RuntimeException
的一个子类,那么您换取到了什么呢?您只是获得了抛出一个异常而不用您指定这样做的能力。换句话说,这是一种用于避免文档化方法所能抛出的异常的方式。在什么时候这是有益的?也就是说,在什么时候避免注明一个方法的行为是有益的?答案是“几乎从不。”
换句话说,Sun 告诉我们检查型异常应该是准则。该教程通过多种方式继续说明,通常应该抛出异常,而不是 RuntimeException
—— 除非您是 JVM。
在 Effective Java: Programming Language Guide一书中(请参阅 参考资料),Josh Bloch 提供了下列关于检查型和非检查型异常的知识点,这些与 “The Java Tutorial” 中的建议相一致(但是并不完全严格一致):
-
第 39 条:只为异常条件使用异常。也就是说,不要为控制流使用异常,比如,在调用
Iterator.next()
时而不是在第一次检查Iterator.hasNext()
时捕获NoSuchElementException
。
-
第 40 条:为可恢复的条件使用检查型异常,为编程错误使用运行时异常。这里,Bloch 回应传统的 Sun 观点 —— 运行时异常应该只是用于指示编程错误,例如违反前置条件。
-
第 41 条:避免不必要的使用检查型异常。换句话说,对于调用者不可能从其中恢复的情形,或者惟一可以预见的响应将是程序退出,则不要使用检查型异常。
-
第 43 条:抛出与抽象相适应的异常。换句话说,一个方法所抛出的异常应该在一个抽象层次上定义,该抽象层次与该方法做什么相一致,而不一定与方法的底层实现细节相一致。例如,一个从文件、数据库或者 JNDI 装载资源的方法在不能找到资源时,应该抛出某种
ResourceNotFound
异常(通常使用异常链来保存隐含的原因),而不是更底层的IOException
、SQLException
或者NamingException
。
最近,几位受尊敬的专家,包括 Bruce Eckel 和 Rod Johnson,已经公开声明尽管他们最初完全同意检查型异常的正统观点,但是他们已经认定排他性使用检查型异常的想法并没有最初看起来那样好,并且对于许多大型项目,检查型异常已经成为一个重要的问题来源。Eckel 提出了一个更为极端的观点,建议所有的异常应该是非检查型的;Johnson 的观点要保守一些,但是仍然暗示传统的优先选择检查型异常是过分的。(值得一提的是,C# 的设计师在语言设计中选择忽略检查型异常,使得所有异常都是非检查型的,因而几乎可以肯定他们具有丰富的 Java 技术使用经验。但是,后来他们的确为检查型异常的实现留出了空间。)
Eckel 和 Johnson 都指出了一个关于检查型异常的相似的问题清单;一些是检查型异常的内在属性,一些是检查型异常在 Java 语言中的特定实现的属性,还有一些只是简单的观察,主要是关于检查型异常的广泛的错误使用是如何变为一个严重的问题,从而导致该机制可能需要被重新考虑。
您已经有多少次看见(或者编写)一个抛出 SQLException
或者 IOException
的方法,即使它看起来与数据库或者文件毫无关系呢?对于开发人员来说,在一个方法的最初实现中总结出可能抛出的所有异常并且将它们增加到方法的 throws 子句(许多 IDE 甚至帮助您执行该任务)是十分常见的。这种直接方法的一个问题是它违反了 Bloch 的 第 43 条 —— 被抛出的异常所位于的抽象层次与抛出它们的方法不一致。
一个用于装载用户概要的方法,在找不到用户时应该抛出 NoSuchUserException
,而不是 SQLException
—— 调用者可以很好地预料到用户可能找不到,但是不知道如何处理 SQLException
。异常链可以用于抛出一个更为合适的异常而不用丢弃关于底层失败的细节(例如栈跟踪),允许抽象层将位于它们之上的分层同位于它们之下的分层的细节隔离开来,同时保留对于调试可能有用的信息。
据说,诸如 JDBC 包的设计采取这样一种方式,使得它难以避免该问题。在 JDBC 接口中的每个方法都抛出 SQLException
,但是在访问一个数据库的过程中可能会经历多种不同类型的问题,并且不同的方法可能易受不同错误模式的影响。一个 SQLException
可能指示一个系统级问题(不能连接到数据库)、逻辑问题(在结果集中没有更多的行)或者特定数据的问题(您刚才试图插入行的主键已经存在或者违反实体完整性约束)。如果没有犯不可原谅的尝试分析消息正文的过失,调用者是不可能区分这些不同类型的 SQLException
的。( SQLException
的确支持用于获取数据库特定错误代码和 SQL 状态变量的方法,但是在实践中这些很少用于区分不同的数据库错误条件。)
不稳定的方法签名问题是与前面的问题相关的 —— 如果您只是通过一个方法传递异常,那么您不得不在每次改变方法的实现时改变它的方法签名,以及改变调用该方法的所有代码。一旦类已经被部署到产品中,管理这些脆弱的方法签名就变成一个昂贵的任务。然而,该问题本质上是没有遵循 Bloch 提出的第 43 条的另一个症状。方法在遇到失败时应该抛出一个异常,但是该异常应该反映该方法做什么,而不是它如何做。
有时,当程序员对因为实现的改变而导致从方法签名中增加或者删除异常感到厌烦时,他们不是通过使用一个抽象来定义特定层次可能抛出的异常类型,而只是将他们的所有方法都声明为抛出 Exception
。换句话说,他们已经认定异常只是导致烦恼,并且基本上将它们关闭掉了。毋庸多言,该方法对于绝大多数可任意使用的代码来说通常不是一个好的错误处理策略。
因为许多方法都抛出一定数目的不同异常,错误处理的代码相对于实际的功能代码的比率可能会偏高,使得难以找到一个方法中实际完成功能的代码。异常是通过集中错误处理来设想减小代码的,但是一个具有三行代码和六个 catch 块(其中每个块只是记录异常或者包装并重新抛出异常)的方法看起来比较膨胀并且会使得本来简单的代码变得模糊。
我们都看到过这样的代码,其中捕获了一个异常,但是在 catch
块中没有代码。尽管这种编程实践很明显是不好的,但是很容易看出它是如何发生的 —— 在原型化期间,某人通过 try...catch
块包装代码,而后来忘记返回并填充 catch
块。尽管这个错误很常见,但是这也是更好的工具可以帮助我们的地方之一 —— 对于异常淹没的地方,通过编辑器、编译器或者静态检查工具可以容易地检测并发出警告。
极度通用的 try...catch
块是另一种形式的异常淹没,并且更加难以检测,因为这是 Java 类库中的异常类层次的结构而导致的(可疑)。让我们假定一个方法抛出四个不同类型的异常,并且调用者遇到其中任何一个异常都将捕获、记录它们,并且返回。实现该策略的一种方式是使用一个带有四个 catch
子句的 try...catch
块,其中每个异常类型一个。为了避免代码难以理解的问题,一些开发人员将重构该代码,如清单 1 所示:
try { doSomething(); } catch (Exception e) { log(e); } |
尽管该代码与四个 catch
块相比更为紧凑,但是它具有一个问题 —— 它还捕获可能由 doSomething
抛出的任何 RuntimeException
并且阻止它们进行扩散。
如果异常是在一个底层的设施中生成的,并且通过许多代码层向上扩散,在最终被处理之前它可能被捕获、包装和重新抛出若干次。当异常最终被记录的时候,栈跟踪可能有许多页,因为栈跟踪可能被复制多次,其中每个包装层一次。(在 JDK 1.4 以及后来的版本中,异常链的实现在某种程度上缓解了该问题。)
Bruce Eckel, Thinking in Java(请参阅 参考资料)的作者,声称在使用 Java 语言多年后,他已经得出这样的结论,认为检查型异常是一个错误 —— 一个应该被声明为失败的试验。Eckel 提倡将所有的异常都作为非检查型的,并且提供清单 2 中的类作为将检查型异常转变为非检查型异常的一个方法,同时保留当异常从栈向上扩散时捕获特定类型的异常的能力(关于如何使用该方法的解释,请参阅他在 参考资料小节中的文章):
class ExceptionAdapter extends RuntimeException { private final String stackTrace; public Exception originalException; public ExceptionAdapter(Exception e) { super(e.toString()); originalException = e; StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); stackTrace = sw.toString(); } public void printStackTrace() { printStackTrace(System.err); } public void printStackTrace(java.io.PrintStream s) { synchronized(s) { s.print(getClass().getName() + ": "); s.print(stackTrace); } } public void printStackTrace(java.io.PrintWriter s) { synchronized(s) { s.print(getClass().getName() + ": "); s.print(stackTrace); } } public void rethrow() { throw originalException; } } |
如果查看 Eckel 的 Web 站点上的讨论,您将会发现回应者是严重分裂的。一些人认为他的提议是荒谬的;一些人认为这是一个重要的思想。(我的观点是,尽管恰当地使用异常确实是很难的,并且对异常用不好的例子大量存在,但是大多数赞同他的人是因为错误的原因才这样做的,这与一个政客位于一个可以随便获取巧克力的平台上参选将会获得十岁孩子的大量选票的情况具有相似之处。)
Rod Johnson 是 J2EE Design and Development(请参阅 参考资料) 的作者,这是我所读过的关于 Java 开发,J2EE 等方面的最好的书籍之一。他采取一个不太激进的方法。他列举了异常的多个类别,并且为每个类别确定一个策略。一些异常本质上是次要的返回代码(它通常指示违反业务规则),而一些异常则是“发生某种可怕错误”(例如数据库连接失败)的变种。Johnson 提倡对于第一种类别的异常(可选的返回代码)使用检查型异常,而对于后者使用运行时异常。在“发生某种可怕错误”的类别中,其动机是简单地认识到没有调用者能够有效地处理该异常,因此它也可能以各种方式沿着栈向上扩散而对于中间代码的影响保持最小(并且最小化异常淹没的可能性)。
Johnson 还列举了一个中间情形,对此他提出一个问题,“只是少数调用者希望处理问题吗?”对于这些情形,他也建议使用非检查型异常。作为该类别的一个例子,他列举了 JDO 异常 —— 大多数情况下,JDO 异常表示的情况是调用者不希望处理的,但是在某些情况下,捕获和处理特定类型的异常是有用的。他建议在这里使用非检查型异常,而不是让其余的使用 JDO 的类通过捕获和重新抛出这些异常的形式来弥补这个可能性。
关于是否使用非检查型异常的决定是复杂的,并且很显然没有明显的答案。Sun 的建议是对于任何情况使用它们,而 C# 方法(也就是 Eckel 和其他人所赞同的)是对于任何情况都不使用它们。其他人说,“还存在一个中间情形。”
通过在 C++ 中使用异常,其中所有的异常都是非检查型的,我已经发现非检查型异常的最大风险之一就是它并没有按照检查型异常采用的方式那样自我文档化。除非 API 的创建者明确地文档化将要抛出的异常,否则调用者没有办法知道在他们的代码中将要捕获的异常是什么。不幸的是,我的经验是大多数 C++ API 的文档化非常差,并且即使文档化很好的 API 也缺乏关于从一个给定方法可能抛出的异常的足够信息。我看不出有任何理由可以说该问题对于 Java 类库不是同样的常见,因为 Jav 类库严重依赖于非检查型异常。依赖于您自己的或者您的合作伙伴的编程技巧是非常困难的;如果不得不依赖于某个人的文档化技巧,那么对于他的代码您可能得使用调用栈中的十六个帧来作为您的主要的错误处理机制,这将会是令人恐慌的。
文档化问题进一步强调为什么懒惰是导致选择使用非检查型异常的一个不好的原因,因为对于文档化增加给包的负担,使用非检查型异常应该比使用检查型异常甚至更高(当文档化您所抛出的非检查型异常比检查型异常变得更为重要的时候)。
如果决定使用非检查型异常,您需要彻底地文档化这个选择,包括在 Javadoc 中文档化一个方法可能抛出的所有非检查型异常。Johnson 建议在每个包的基础上选择检查型和非检查型异常。使用非检查型异常时还要记住,即使您并不捕获任何异常,也可能需要使用 try...finally
块,从而可以执行清除动作例如关闭数据库连接。对于检查型异常,我们有 try...catch
用来提示增加一个 finally
子句。对于非检查型异常,我们则没有这个支撑可以依靠。
<!-- CMA ID: 54641 --><!-- Site ID: 10 --><!-- XSLT stylesheet used to transform this file: dw-article-6.0-beta.xsl -->
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文.
- 请阅读 Brian Goetz 的完整的 Java 理论与实践 系列。
- 请查看 Srikanth Shenoy 的精彩文章“ EJB 异常处理的最佳做法”( developerWorks, 2002 年 5 月)。
- 在“ Merlin 的魔力:异常和日志记录”中( developerWorks,2001 年 12 月), John Zukowski 讨论了 J2SE 的变化。
- 在“The Java Tutorial”关于 运行时异常的一节中列举了关于检查型以及非检查型异常的官方立场。
- Bruce Eckel 是 Thinking in Java (Prentice Hall)一书的作者,他在自己的 Web 站点上总结他的 对于检查型异常的立场。
-
异常辩论在 TheServerSide Web 站点上发动了一次活跃的讨论。
- Bill Venners 就 C# 中的非检查型异常问题 采访了 C# 的设计师 Anders Hejlsberg。
- Joshua Bloch 的 Effective Java: Programming Language Guide (Addison-Wesley, 2001)提供了关于 Java 编码样式的大量建议,包括有关异常的一些非常敏感的建议。
- Rod Johnson 的 J2EE Design and Development (Wrox 2002)是一本关于利用 Java 技术开发应用程序的许多方面最佳实践的优秀教程。
- 请访问 Developer Bookstore,获取技术书籍的完整列表,其中包括数百本 Java 相关的书籍。
- 在 developerWorksJava 技术专区 可以找到数百篇关于 Java 编程的各个方面的文章。
Brian Goetz 在过去 15 年里一直是专业软件开发人员。他是 Quiotix 的首席顾问,Quiotix 是一家位于加利福尼亚州洛斯拉图斯(Los Altos)的软件开发和咨询公司,他还为几个 JCP 专家组服务。可以在业界流行的出版物上查阅 Brian 已发表的和即将发表的文章。
评论
One:已检查异常(编译异常)
Two:未检查异常(RunIng异常)
检查时异常指的是在编译期间
Java的编译器对代码的一种检测
比如 int i;
System.out.println(i+2);
在编译期就过不去,Java对变量的使用必须初始化
为检查异常就比较多了
数组越界,类型转换错误,空指针........
在程序跑起来之后才晓得
发表评论
-
log4j配置输出hibernate执行的SQL和相应参数以及打印结果
2017-09-01 20:26 5559hibernate的配置文件:hibernate.prope ... -
使用Gson将对象类转成Json对象时出现\u003d的问题
2015-11-03 17:07 4078Gson将对象转成Json对象的方法 ... -
maven pom.xml加载不同properties配置
2015-10-30 11:52 12561.pom.xml =================== ... -
剖析淘宝TDDL(TAOBAO DISTRIBUTE DATA LAYER)
2015-10-19 19:03 716剖析淘宝 TDDL ( TAOBAO DISTRIBUTE ... -
"org.eclipse.wst.validation" has been removed
2015-10-15 11:22 1168从SVN服务器上导出maven工程遇到的问题," ... -
mysql/Java服务端对emoji的支持
2015-09-19 10:43 847前言: 最近开发的iOS项目因为需要用户文本的存储,自然就 ... -
Ehcache配置详解及CacheManager使用
2015-04-09 14:40 2049<?xml version="1.0&quo ... -
详解 Too many open files
2014-09-07 00:25 1233运行在Linux系统上的Java程序可能会出现" ... -
Could not find jar tool executable问题解决
2014-03-21 00:28 1258eclipse 中,在用PROGUARD生成混淆包Obfus ... -
eclipse不小心删除了代码文件的一个解决办法
2013-08-15 17:02 1310平时用eclipse写代码,不小心删除了一个文件,一般就找不回 ... -
关于ProGuard的学习了解
2012-09-20 09:51 1435在Android项目中用到JNI, ... -
Java实现MD5加密
2012-08-23 23:32 1077import java.io.UnsupportedEnco ... -
叫你怎么下载开源代码,例如:hg clone https://gtalksms.googlecode.com/hg/ gtalksms .
2012-07-04 17:29 1643svn的就不说了,git的也不说了,这些都是常用的,直说hg ... -
Java NIO与IO 区别和比较
2012-06-17 11:20 1468本文将通过一些实例来 ... -
jar打包出现java.io.IOException: invalid header field 解决方案
2012-06-10 12:10 4837执行: D:\aaa\DMDemo>jar -cvfm ... -
java内存原型分析-基本知识
2012-06-06 13:53 778java虚拟机内存原型寄存 ... -
java InputStream读取数据问题
2012-05-21 15:45 9821. 关于InputStream.read() 在从数 ... -
java读取文件夹下的所有文件夹和文件
2012-05-15 10:46 1162package com.borland.samples.wel ... -
Java中yield(),sleep()以及wait()的区别
2012-05-08 11:03 744往往混淆了这三个函数的使用。 从操作系统的角度讲,os会维护一 ... -
Netbeans 7和Subversion(svn) 1.7
2012-05-02 23:07 2075Netbeans的SVN插件,最后一次更新是07年了,所以它根 ...
相关推荐
【Java中的`java.net.BindException: Address already in use: JVM_Bind`异常】 在Java编程中,当你尝试启动一个服务器端应用,如Tomcat,或者任何需要监听特定端口的服务时,可能会遇到`java.net.BindException: ...
`alipay-sdk-java-1.0.0.jar` 是支付宝的基础SDK,它提供了与支付宝服务器交互的基础框架和类库。这个库包含了加密算法、请求和响应处理、异常处理等核心组件,是所有支付宝接口调用的基石。开发者可以通过这个库中...
### Java异常详解 #### 1. `java.lang.NullPointerException` `NullPointerException`是当程序尝试访问一个为`null`的对象实例的字段、调用其方法或构造函数时抛出的异常。这种异常通常发生在程序员错误地假设某个...
MySQL是世界上最受欢迎的开源关系型数据库管理系统之一,而`mysql-connector-java-5.1.32`则是MySQL提供的一款用于Java应用程序与MySQL数据库进行交互的驱动程序。这个驱动程序,也被称为JDBC(Java Database ...
### 2024年Java面试题:Java异常方面的面试题 #### 核心知识点解析 **一、Java异常架构** ...通过以上内容的深入理解,你可以更好地准备Java面试中关于异常处理的部分,并在实际开发工作中有效地运用这些知识。
### Java 错误处理:java.lang.OutOfMemoryError: Java heap space 在Java应用程序开发过程中,经常遇到的一个问题就是内存溢出错误,特别是在处理大量数据或长时间运行的应用时。其中,“java.lang....
Java编程异常处理最佳实践【推荐】 Java中的异常处理是非常重要的,特别是在大型项目中,异常处理机制的设计和实现将直接影响系统的稳定性和可靠性。 Java中的异常处理机制可以分为两大类:Checked Exception和...
- **检查性异常(checked exception)**:这类异常是Java语言强制要求程序员必须处理的异常。如果某个方法可能会抛出此类异常,那么要么在方法内部捕获并处理,要么在方法声明中通过`throws`关键字声明该异常,告知...
本资源“Java程序设计研究与实践-理论和实践.zip”包含了一份深入探讨Java编程的PDF文档,旨在帮助读者从理论到实践全面掌握Java语言。 理论部分,主要涵盖了以下几个关键知识点: 1. **Java语言基础**:包括Java...
- 不要忽视异常,即使在测试环境中也要处理异常,因为它们可能揭示潜在的代码问题。 了解并妥善处理这些异常可以帮助提升程序的健壮性和可靠性,减少因未预期错误导致的程序崩溃。通过阅读和理解Java API文档,可以...
通过这个详细教程,你不仅会学习到以上理论知识,还能通过提供的源代码实例进行实践操作,理解如何在实际项目中应用异常处理。这些实例可能包括读写文件、网络通信、数据库操作等常见场景,帮助你更好地应对各种可能...
Title: OCA: Oracle Certified Associate Java SE 8 Programmer I Study Guide: Exam 1Z0-808 Author: Jeanne Boyarsky, Scott Selikoff Length: 432 pages Edition: 1 Language: English Publisher: Sybex ...
学习Java首先要安装Java Development Kit (JDK),它包含了编译、调试和运行Java程序所需的所有工具,如javac编译器和java解释器。配置好环境变量PATH和JAVA_HOME后,即可在命令行中使用这些工具。 3. **Java语法...
-nodos -不要去检查class文件是否以dos方式写 (CR before NL, default: check) -nocast - 不要生成辅助文件 -nocode -不要生成方法的源代码 -noconv - 不要转换java的定义符 (default: do) -...
Java代码检查工具是一种重要的开发辅助软件,主要用于确保代码符合特定的编程规范和最佳实践,从而提高代码质量和可维护性。这些工具通常作为集成开发环境(IDE)如MyEclipse的插件存在,允许开发者在编写代码的过程...
Java中的异常主要分为两大类:检查性异常(Checked Exception)和非检查性异常(Unchecked Exception)。 - **检查性异常**:指的是那些可能发生的但可以被程序员通过代码逻辑避免或处理的异常。这类异常必须被明确...
本电子书合集是针对计算机专业基础理论中的编程语言部分,特别是聚焦于Java语言的深入学习,涵盖了多个方面的知识,包括JVM原理、反射机制、多线程等内容。以下是这些书籍涉及的一些关键知识点: 1. **Java编程语言...
### Java常见异常和错误总结 #### 1. java.lang.NullPointerException (空指针异常) - **定义**: 当尝试调用未经初始化的对象或是不存在的对象时触发的异常。 - **常见场景**: - 图片处理中,若图片未初始化或...
java图片处理工具类JAR包 java-image-scalingjar