代码精进之代码规范(三)

异常处理

异常的类别

  • 在 Java 语言里,异常状况分为三类。
    • 非正常异常(Error):这类异常的命名以 Error 结尾,比如 OutOfMemoryError,NoSuchMethodError。这类异常,编译器编译时不检查,应用程序不需要处理,接口不需要声明,接口规范也不需要纪录;
    • 运行时异常(RuntimeException):这类异常的命名通常以 Exception 结尾,比如 IllegalArgumentException,NullPointerException。这类异常,编译器编译时不检查,接口不需要声明,但是应用程序可能需要处理,因此接口规范需要记录清楚;
    • 非运行时异常:除了运行时异常之外的其他的正常异常都是非运行时异常,比如 InterruptedException,GeneralSecurityException。和运行时异常一样,命名通常以 Exception 结尾。这类异常,编译器编译时会检查异常是否已经处理或者可以抛出,接口需要声明,应用程序需要处理,接口规范需要记录清楚。
  • 非运行时异常还有一个别名,叫作检查型异常(CheckedException)。对应地,运行时异常的别名是非检查型异常(UncheckedException)。 为了便于识别,我们通常使用检查型异常指代非运行时异常,使用运行时异常指代非检查型异常。
    在这里插入图片描述
    在这里插入图片描述

Java 异常的四个要素:

  • 异常类名(IllegalArgumentException, FileNotFoundException)
  • 异常描述(“Invalid file path”)
  • 异常堆栈(at sun.security.ssl.InputRecord.read(InputRecord.java:504))
  • 异常转换(Caused by: javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?)

异常类名解决了“出了什么错”的问题;异常描述解决了“为什么会出错”的问题;异常堆栈解决了“什么地方出了错”的问题;而异常转换记录了不同场景对这三个问题的不同理解和不同处理。

其中 JVM 自动帮我们处理异常堆栈,我们需要特别关注的就只有三点了。

  • 对于异常类名,我们要准确地选择异常类。
    • Exception 类是一个包罗万象的超级异常类,如果我们使用 Exception 作为声明和抛出的异常,就不方便用户精准定位,从而解读和判断“出了什么错”。 类似的超级异常类还有 RuntimeException、IOException 等。 除非是超级的接口,否则我们应该尽量减少超级异常类的使用,而是选择那些意义明确、覆盖面小的异常类,比如 FileNotFoundException。
  • 对于异常描述,我们要清晰地描述异常信息。
    • 虽然 Java 异常允许使用没有具体异常信息的异常,但是这种使用却容易丢失用户对于“为什么会出错”这个问题更精准的解读。 所以我不推荐使用没有描述信息的异常。
      在这里插入图片描述
  • 对于异常转换,我们要恰当地转换异常场景。
    • 随着应用场景的转换,我们还需要转换异常的类型和描述。 比如,SQLException 这种涉及具体实现细节的异常类就不太适合直接抛给最终的用户应用。 用户关心的是商业的逻辑,并不是实现的细节,这就需要我们随着使用场景调整异常。如果一股脑儿地把所有的异常抛到底,业务逻辑就会很混乱,用户体验也不好。但是随着场景调整异常也不是没有代价的。这是一个妥协的选择,会带来一些负面的情况。
      在这里插入图片描述

尽量不要使用断言。断言的设计是为了提高代码质量,可是断言一般有三个状态:断言是否启用;如果启用,断言是否成立。这让编码的逻辑很混乱,阅读的时候我们不一定能够把三个逻辑考虑周全,增加了编码错误的几率,降低了代码质量。

组织好代码段

代码文件头部结构

  • 一般来说,一个源代码文件的开始部分包含三项内容。 按照出现顺序,分别为:
    • 版权和许可声明;
    • 命名空间(package);
    • 外部依赖(import)。
      在这里插入图片描述
  • 在版权部分中,“2003, 2013”中的 2003 表示的是代码的诞生日期,2013 表示的是代码的最近更改日期。需要注意的是,每次代码变更,都要更改代码的最近更改日期。

代码文件对象结构

  • 紧随着源代码的头部,是对目标类的定义,这一部分包含三个部分。 按照出现顺序,分别为:
    • 类的规范;
    • 类的声明;
    • 类的属性和方法。
  • 需要注意的是,对于公共类,需要使用 since 标签,标明从哪一个版本开始定义了这个类。 这样的标明,方便我们对类进行版本管理,减少我们进行代码变更时的工作量。
    在这里插入图片描述

类的内部代码结构

  • 类的属性和方法,一般是一个代码文件的主要组成部分。类的结构,推荐的编码顺序依次为:
    • 类的属性;
    • 构造方法;
    • 工厂方法;
    • 其他方法。
      在这里插入图片描述
  • 类似于构造方法,工厂方法也是用来构造一个类的实例。不同的是,工厂方法有具体的返回值。它可以是静态方法,也可以是实例方法。
  • 如果是实例方法,工厂方法还可以被子类重写。这是工厂方法和构造方法的另外一个重要的区别。由于工厂方法也扮演着构造类实例的功能,我们一般把它放在构造方法的后面,其他方法的前面。
    在这里插入图片描述

方法的代码结构

  • 方法的代码结构。一般来说,一个方法需要包含三项内容:
    • 方法的规范;
    • 方法的声明;
    • 方法的实现。
  • 内部类的内部方法,可以没有第一部分。但对于公开类的公开方法,方法的规范一定不能缺失。
  • 一个典型的规范,应该包含以下十个部分
    • 方法的简短介绍;
    • 方法的详细介绍(可选项);
    • 规范的注意事项 (使用 apiNote 标签,可选项);
    • 实现方法的要求 (使用 implSpec 标签,可选项);
    • 实现的注意事项 (使用 implNote 标签,可选项);
    • 方法参数的描述;
    • 返回值的描述;
    • 抛出异常的描述:需要注意的是,抛出异常的描述部分,不仅要描述检查型异常,还要描述运行时异常;
    • 参考接口索引(可选项);
    • 创始版本(可选项)。
      在这里插入图片描述

按顺序使用限定词

  • 在声明一个类、方法或者方法属性时,为了更准确地描述和理解声明的适用场景,我们通常要使用修饰性的关键词。这些修饰性的关键词,我们通常称它们是修饰符或者限定词。 一个声明,可以使用多个限定词。
  • Java 的语法中,限定词的使用顺序没有强制性规定。但是,限定词的使用顺序有一个约定俗成的规则。按照这个规则使用限定词,一般来说,我们最关切的修饰符最先进入我们的视野,和标识符最密切的位置最靠近标识符。使用一致性的顺序,我们就能更快速地理解一个声明。
  • 限定词推荐使用顺序:
    • public/private/protected (访问控制限定词,制定访问权限)
    • abstract (抽象类或者抽象方法,具体实现由子类完成)
    • static (静态类、方法或者类属性)
    • final (定义不能被修改的类、方法或者类属性)
    • transient(定义不能被序列化的类属性)
    • volatile(定义使用主内存的变量)
    • default(声明缺省的方法)
    • synchronized(声明同步的方法)
    • native(声明本地的方法,也就是 Java 以外的语言实现的方法)
    • strictfp(声明使用精确浮点运算)
      在这里插入图片描述

使用空行分割代码块

  • 怎么整理代码,一个重要的原则就是“给代码分块”,通过空格、缩进、空行实现这个目的。空行的作用,空行用于垂直分割,用于分开同级别的不同代码块。
  • 可以使用空行分割如下的代码块:
    • 版权和许可声明代码块;
    • 命名空间代码块;
    • 外部依赖代码块
    • 类的代码块;
    • 类的属性与方法之间;
    • 类的方法之间;
    • 方法实现的信息块之间。
      在这里插入图片描述
      你知道的越多,你不知道的越多。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值