第5章 CHAPTER 5 Java编码规范 俗话说: “没有规矩不成方圆”。编程工作往往都是一个团队协同进行,因而一致的编码规范非常有必要,这样写成的代码便于团队中的其他人员阅读,也便于编写者自己以后阅读。 微课视频 5.1命名规范 程序代码中到处都是标识符,因此取一个一致并且符合规范的名字非常重要。 命名方法很多,但是比较有名的且被广泛接受的命名方法包括如下两种: □匈牙利命名,一般只是命名变量,原则是: 变量名=类型前缀+描述,如bFoo表示布尔类型变量,pFoo表示指针类型变量。匈牙利命名还是有一定争议的,在Java编码规范中基本不被采用。 □驼峰命名(CamelCase),又称“骆驼命名法”,是指混合使用大小写字母来命名。驼峰命名又分为小驼峰法和大驼峰法。小驼峰法就是第一个单词是全部小写,后面的单词首字母大写,如myRoomCount; 大驼峰法是第一个单词的首字母也大写,如ClassRoom。 除了包和常量外,Java编码规范命名方法采用驼峰法,分类说明如下: □包名: 包名是全小写字母,中间可以由点分隔开。作为命名空间,包名应该具有唯一性,推荐采用公司或组织域名的倒置,如com.apple.quicktime.v2。但Java核心库包名不采用域名的倒置命名,如java.awt.event。 □类和接口名: 采用大驼峰法,如SplitViewController。 □文件名: 采用大驼峰法,如BlockOperation.java。 □变量: 采用小驼峰法,如studentNumber。 □常量名: 全大写,如果是由多个单词构成,可以用下画线隔开,如YEAR和WEEK_ OF_MONTH。 □方法名: 采用小驼峰法,如balanceAccount、isButtonPressed等。 命名规范示例代码如下: package com.zhijieketang; public class Date extends java.util.Date { private static final int DEFAULT_CAPACITY = 10; private int size; public static Date valueOf(String s) { final int YEAR_LENGTH = 4; final int MONTH_LENGTH = 2; int firstDash; int secondDash; ... } public String toString () { int year = super.getYear() + 1900; int month = super.getMonth() + 1; int day = super.getDate(); ... } } 微课视频 5.2注释规范 Java中注释的语法有三种: 单行注释(//)、多行注释(/*…*/)和文档注释(/**…*/)。本节通过文件注释、文档注释、代码注释来介绍如何规范使用这些注释语法。 5.2.1文件注释 文件注释就是在每个文件开头添加注释。文件注释通常包括如下信息: 版权信息、文件名、所在模块、作者信息、历史版本信息、文件内容和作用等。 示例代码如下: /* * 版权所有 2015 北京智捷东方科技有限公司 * 许可信息查看LICENSE.txt文件 * 描述: * 实现日期基本功能 * 历史版本: * 2015-7-22: 创建 关东升 * 2015-8-20: 添加socket库 * 2015-8-22: 添加math库 */ 上述注释只是提供了版权信息、文件内容和历史版本信息等,文件注释要根据本身的实际情况来包括相应的内容。 5.2.2文档注释 文档注释就是指这种注释内容能够生成API帮助文档,JDK中javadoc命令能够提取这些注释信息并生成HTML文件。文档注释主要对类(或接口)、实例变量、静态变量、实例方法和静态方法等进行注释。 提示文档是要给别人看的帮助文档,一般注释的实例变量、静态变量、实例方法和静态方法都应该是非私有的,那些只给自己看的内容可以不用文档注释。 示例代码如下: package com.zhijieketang; /** * 自定义的日期类,具有日期基本功能,继承java.util.Date * <p>实现日期对象和字符串之间的转换</p> * @author 关东升 */ public class Date extends java.util.Date { private static final int DEFAULT_CAPACITY = 10; /** * 容量 */ public int size; /** * 将字符串转换为Date日期对象 * @param s 要转换的字符串 * @return Date日期对象 */ public static Date valueOf(String s) { final int YEAR_LENGTH = 4; final int MONTH_LENGTH = 2; int firstDash; int secondDash; ... } /** * 将日期转换为yyyy-mm-dd格式的字符串 * @return yyyy-mm-dd格式的字符串 */ public String toString () { int year = super.getYear() + 1900; int month = super.getMonth() + 1; int day = super.getDate(); ... } } 由于文档注释最终会生成HTML文档,所以可以在文档注释中使用HTML标签,上述注释中的<p></p>是HTML段落标签。 另外,上述的文档注释中还用到了@author、@return和@param等文档注释标签,这些标签能够方便生成API帮助文档。表51所示是常用的文档注释标签。 表51文档注释标签 标签描述标签描述 @author说明类或接口的作者@see参考另一个主题的链接 @deprecated说明类、接口或成员已经废弃@exception说明方法所抛出的异常类 @param说明方法参数@throws同@exception标签 @return说明返回值@version类或接口的版本 如果想生成API帮助文档,则可以使用javadoc指令,如图51所示,在命令行中输入javadocencoding UTF8d apidoc Date.java指令,其中参数encoding UTF8是指定文件编码为UTF8中文乱码; d参数指明要生成文档的目录; apidoc是当前目录下面的apidoc目录,如果不存在apidoc,则会创建一个apidoc目录; Date.java是当前目录下的Java源文件。 图51生成API帮助文档 如果生成成功,则在当前apidoc目录下生成很多HTML文件,其中的index.html文件是文档的入口,双击此文件,打开如图52所示的页面,从页面中可见在Date.java中注释的内容会出现在HTML页面中。 图52API帮助文档 5.2.3代码注释 程序代码中处理文档注释还需要在一些关键的地方添加代码注释,文档注释一般是给一些看不到源代码的人看的帮助文档,而代码注释则是给阅读源代码的人参考的。代码注释一般采用单行注释(//)和多行注释(/*…*/)。 示例代码如下: public class Date extends java.util.Date { // 默认的容量,是一个常量① private static final int DEFAULT_CAPACITY = 10; /** * 容量 */ public int size; /** * 将字符串转换为Date日期对象 * @param s 要转换的字符串 * @return Date日期对象 */ public static Date valueOf(String s) { final int YEAR_LENGTH = 4; final int MONTH_LENGTH = 2; int firstDash; int secondDash; Date d = null; ... /* ② * 判断d是否为空, * 如果为空则抛出异常IllegalArgumentException,否则返回d */ if (d == null) { throw new java.lang.IllegalArgumentException(); } return d; } /** * 将日期转换为yyyy-mm-dd格式的字符串 * @return yyyy-mm-dd格式的字符串 */ public String toString () { int year = super.getYear() + 1900;//计算年份 ③ int month = super.getMonth() + 1;/*计算月份*/ ④ int day = super.getDate(); ... } } 上述代码第①行采用了单行注释,要求与其后的代码具有一样的缩进层级。如果注释的文字很多,则可以采用多行注释,见代码第②行。多行注释也要求与其后的代码具有一样的缩进层级。有时也会在代码的尾端进行注释,这要求注释内容极短,应该再有足够的空白来分开代码和注释,见代码第③行和第④行。 微课视频 5.3代码排版 代码排版包括空行、空格、断行和缩进等内容。代码排版内容比较多,工作量很大,也非常重要。 5.3.1空行 空行用以将逻辑相关的代码段分隔开,以提高可读性。空行使用规范如下: (1) 类声明和接口声明之间保留两个空行。见示例Date.java代码第⑧行和第⑨行。 (2) 两个方法之间保留一个空行。见示例Date.java代码第⑦行。 (3) 方法的第一条语句之前保留一个空行。见示例Date.java代码第⑤行。 (4) 代码注释(除尾端注释外)之前保留一个空行。见示例Date.java代码第①、②、③、④行。 (5) 一个方法内的两个逻辑段之间保留一个空行。见示例Date.java代码第⑥行。 示例Date.java代码如下: /* * 版权所有 2015 北京智捷东方科技有限公司 * 许可信息查看LICENSE.txt文件 * 描述: * 实现日期基本功能 * 历史版本: * 2015-7-22: 创建 关东升 * 2015-8-20: 添加socket库 * 2015-8-22: 添加math库 */ package com.zhijieketang; ① /** * 自定义的日期类,具有日期基本功能,继承java.util.Date * <p>实现日期对象和字符串之间的转换</p> * @author 关东升 */ public class Date extends java.util.Date { ② // 默认的容量,是一个常量 private static final int DEFAULT_CAPACITY = 10; ③ /** * 容量 */ public int size; ④ /** * 将字符串转换为Date日期对象 * @param s 要转换的字符串 * @return Date日期对象 */ public static Date valueOf(String s) { ⑤ final int YEAR_LENGTH = 4; final int MONTH_LENGTH = 2; int firstDash; int secondDash; ⑥ Date d = null; ... /* * 判断d是否为空, * 如果为空则抛出异常IllegalArgumentException,否则返回d。 */ if (d == null) { throw new java.lang.IllegalArgumentException(); } return d; } ⑦ /** * 将日期转换为yyyy-mm-dd格式的字符串 * @return yyyy-mm-dd格式的字符串 */ public String toString () { int year = super.getYear() + 1900; //计算年份 int month = super.getMonth() + 1; /*计算月份*/ int day = super.getDate(); ... } } ⑧ class A { } ⑨ class B { } 5.3.2空格 代码中的有些位置是需要有空格的,这个工作量也很大。下面是使用空格的规范。 (1) 赋值符号“=”前后各有一个空格。示例代码如下: int YEAR_LENGTH = 4; int day = super.getDate(); (2) 所有的二元运算符都应该使用空格与操作数分开。示例代码如下: a += c + d; prints("size is " + foo + "\n"); (3) 一元操作符,即负号“-”、自增“++”和自减“--”等,它们与操作数之间没有空格。示例代码如下: int a = -b; a++; --b; (4) 小左括号“(”之后,小右括号“)”之前不应有空格。示例代码如下: a = (a + b) / (c * d) (5) 大左括号“{”之前有一个空格。示例代码如下: while (a == d) { n += 1 } (6) 方法参数列表小左括号“(”之前没有空格,小右括号“)”之后有一个空格,参数列表中参数逗号“,”之后也有一个空格。示例代码如下: String format(Object obj, StringBuffer toAppendTo, FieldPosition fieldPosition) { ... } (7) 关键字之后紧跟着小左括号“(”,关键字之后应该有一个空格。如下示例中while之后有一个空格。 while (a == d) { ... } 5.3.3缩进 4个空格常被作为缩进排版的一个单位。虽然在开发时程序员使用制表符进行缩进,而默认情况下一个制表符等于8个空格,但是不同的IDE工具中一个制表符与空格对应个数会有不同。IntelliJ IDEA中默认是一个制表符对应4个空格。 缩进可以依据如下一般规范。 (1) 在方法、Lambda、控制语句等包含大括号“{}”的代码块中,代码块的内容相对于首行缩进一个级别(4个空格)。 (2) 如果是if语句中条件表达式的断行,那么新的一行应该相对于上一行缩进两个级别(8个空格),再往后的断行要与第一次的断行对齐。 示例代码如下: public class Date extends java.util.Date { … public String getString() { int year = super.getYear() + 1900; // 计算年份 int month = super.getMonth() + 1; /* 计算月份 */ int day = super.getDate(); if ((longName1 == longName2) || (longName3 == longName4) && (longName3 > longName4)① && (longName2 > longName5)) {② } return null; } } 上述代码第①行和第②行是if语句条件表达式的断行,代码第①行和第②行要对齐。 5.3.4断行 一行代码的长度应尽量不要超过80个字符,如果超过则需断行,可以依据下面的一般规范断开。 (1) 在一个逗号后面断开。 (2) 在一个操作符前面断开,要选择较高级别的运算符(而非较低级别的运算符)断开。 (3) 新的一行应该相对于上一行缩进两个级别(8个空格)。 下面通过一些示例加以说明。 longName1 = longName2 * (longName3 + longName4 - longName5) + 4 * longName6 ① longName1 = longName2 * (longName3 + longName4 - longName5) + 4 * longName6② private static DateFormat get(int timeStyle, int dateStyle, int flags, Locale loc) {③ ... } if ((longName1 == longName2) || (longName3 == longName4) && (longName3 > longName4) && (longName2 > longName5)) { ④ } boolName1 = (longName3 == longName4) ? (longName3 > longName4) : (longName2 > longName5); ⑤ 上述代码第①行和第②行是带有小括号运算的表示式,其中代码第①行的断开位置比第②行的断开位置要好。因为代码第①行断开处位于括号表达式的外边,这是一个较高级别运算符的断开。代码第③行是方法名断开,是在参数逗号之后。代码第④行是if判断语句,由于可能有很多长的条件表达式,断开的位置应在逻辑运算符处。代码第⑤行是三元运算符的断开。 微课视频 5.4其他规范 除了上述规范外,还有很多零散的规范。下面补充一些重要的规范。 (1) 在声明变量或常量时推荐一行一个声明。示例代码如下: // 推荐使用: int longName1 = 0 ; int longName2 = 0 ; // 不推荐使用: int longName1 = 0, longName2 = 0 ; (2) 左大括号“{”位于声明语句同行的末尾。右大括号“}”另起一行,与相应的声明语句对齐,除非是一个空语句,右大括号“}”应紧跟在左大括号“{”之后。示例如下: public class Date extends java.util.Date { int longName1 = 0; int longName2 = 0; boolean boolName1 = true; public String getString() { int year = super.getYear() + 1900;// 计算年份 int month = super.getMonth() + 1;/* 计算月份 */ int day = super.getDate(); return null; } public void setString() {} } (3) 每行至多包含一条语句。示例如下: // 推荐使用: argv++; argc--; // 不推荐使用: argv++; argc--; (4) 虽然Java语言允许if、for等控制语句在只有一行代码情况下省略左右两个大括号,但是编码规范并不推荐这样使用。示例如下: // 推荐使用: if (1 == 3) { x = 2.3; } // 不推荐使用: if (1 == 3) x = 2.3; 关于规范,事实上还有很多,不能穷尽,这里不再赘述。 5.5本章小结 通过对本章内容的学习,读者可以了解Java的编码规范,包括命名规范、注释规范、声明规范和代码排版等内容。 5.6同步练习 选择题 1. 下列选项中哪些Java类命名符合Java命名规范?() A. dummy_threading B. SplitViewController C. WEEK_OF_MONTH D. balance_account 2. 下列选项中哪些方法名符合Java命名规范?() A. dummy_threading B. SplitViewController C. WEEK_OF_MONTH D. balanceAccount 3. 下面哪种注释是Java支持的?() A. /*...*/ B. /**...*/ C. # D. 三重双引号“"""”包裹起来