Android代码规约
写在前面的话
对软件来说,适当的规范和标准绝不是消灭代码内容的创造性、优雅性,而是限制过度个性化,以一种普遍认可的统一方式一起做事,提升协作效率,高效协作即降低协同成本。所谓无规矩不成方圆,无规范不能协作。车同轨,书同文,规约是我们高效合作的基础。代码的字里行间流淌的是软件生命中的血液,质量的提升是尽可能少踩坑,杜绝踩重复的坑,切实提升质量意识。
工程师对于代码,一定要“精益求精”,不论从性能,还是简洁优雅,都要具备“精益求精”的工匠精神,认真打磨自己的作品。一个优秀的工程师和一个普通工程师的区别,不是现在满天飞的架构图,他的功底就是体现在他写的每一行代码上。
- 命名风格
- 【强制】代码中的命名均不能以下划线或美元符号开始或结束。(仅有埋点命名避免数字开头时,可以下划线开始。)
- 【强制】命名严禁使用拼音,应使用拼写正确的单词,尽量做到见名知义。
正例:requestVideoLiveComment( ),defaultHead;
反例:requestVideoDanmu( ),defultHead,queryJizhaoEntrance()
- 【强制】类名须严格采用UpperCamelCase驼峰命名法,方法名、参数名、变量须严格采用lowerCamelCase 风格。
正例:mTitleBar,ExpectJobBean,isUnpublished;
反例:mtitleBar,ExpectjobBean,isUnPublished;
- 【强制】实体类须以Bean结尾,(注意 序列化 或 放在指定不混淆路径下)
正例:SearchReqBean,DeliverListBean,SearchExposurePositionBean;
反例:ConditionResVo,DeliverHistoryParams,ExposurePositionInfo,IDObject。
- 【强制】抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类
命名以它要测试的类的名称开始,以 Test 结尾。
- 【强制】包名统一使用小写,不建议使用下划线
正例:com.alpha.lagouapk.bean, com.alpha.lagouapk.ui.redpacket
反例:com.alpha.lagouapk.ui.login.guidePerfect, com.alpha.lagouapk.ui.rebuild_login
- 【强制】常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
正例:TYPE_COLLECTED = 1 ;
反例:TYPE_1 = 1 ;
- 【强制】分支命名规则如下:
类型 |
约定命名 |
不规范做法 |
个人开发分支 |
ylwang_764_effect_guarantee 开发者_版本号_需求英文名 含义:永良-764版本-效果保障 |
ylwang_xiaoguobaozhang ylwang_764 ylwang_02.24 |
单功能分支(protected) |
feature_764_effect_guarantee |
命名同上 严禁把无法运行代码合并到非个人开发分支 |
个人合并发版分支 |
ylwang_release_764 (release_764+ feature_764_effect_guarantee) |
不要把release合并到feature,用发版分支污染单功能分支 |
发版分支 |
release_764 |
|
sub分支 |
开发者/feature_版本号_需求英文名 |
sub分支 必须与其对应主分支同步合并。 |
注意:业务开发分支在远端保存时间不宜超过45天,(2周/迭代,超过两个迭代的业务分支建议及时清理,技术探索分支除外)
- 理论上每次MR >=2人review后才可以merge
- 每次mr之前必须先从对应的feature分支同步代码,处理完冲突再进行mr
- 要定期及时从feature分支同步代码到个人开发分支
流程示意图:
- 常量定义:
- 【强制】不允许魔法值直接与变量做对比,或者魔法值作为key
正例 :
public static final int TYPE_ONLINE_POSITION = 2;//在线职位类型
switch (type) {
case TYPE_ONLINE_POSITION:
break;
.....
default:
break;
}
反例:if (2 == type) {} ,//数值 ①语意不明,②定位和改动时代价较大。
getIntent().getStringExtra("positionIds")//key ①put/get均拼写易手误,②不宜追踪
- 【强制】long 或者 Long 初始赋值时,使用大写的 L,避免与小写l与数字1混淆。
- 【推荐】不要使用一个常量类维护所有常量,按常量功能进行归类,分开维护。
- 【参考】Java语言可用注解代替枚举,但Kotlin对注解支持并不好,可用enum / 密封类
- 【强制】常量与变量比较时,应调用常量方法
说明:Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用
equals。
正例:TYPE_HOME.equals(bean.getTab());
反例:bean.getCity().equals(BEI_JING);//city为空时,空指针异常
- Android 资源文件命名与使用
- 【推荐】layout 文件的命名方式。
Activity 的 layout 以 activity_module 开头
Fragment 的 layout 以 fragment_module 开头
Dialog 的 layout 以 dialog_module 开头
ListView/RecyclerView的行 layout 以 item_list_module开头
自定义View:DefaultEmptyLayout--> layout_default_empty.xml
- 【强制】drawable 资源名称以小写单词+下划线的方式命名,根据分辨率不同存放在不同的 drawable 目录下,建议只使用一套,例如 drawable-xxhdpi。
正例:
场景 |
示例图 |
约定命名 |
说明 |
不规范命名 |
文本框单色填充背景 |
shape_3300b38a_r4.xml |
1.文本框 不支持点击效果。 2.类型_颜色值_r圆角值.xml |
bg_green_cornor_4.xml item_list_click_bg.xml |
|
文本框空心线条背景 |
shape_stroke_8cedcf93_r2.xml |
类型_stroke_颜色值_r圆角值.xml |
bg_ffffff_stroke_corner5.xml |
|
按钮单色填充背景 |
selector_00b38a_r4.xml |
按钮支持点击效果:包含pressed,disable等状态 |
||
按钮双色背景 |
selector_f2fbf9_00b38a_r4.xml |
按照 default颜色 先内后外的顺序注明,类型_主色值_次色值_r圆角值.xml |
||
非全圆角背景 |
selector_ffffff_00b38a_tl_r4.xml |
左上tl,右上tr,左下bl,右下br |
||
shape_edcf93_tl_bl_r10.xml |
左上和左下 有圆角,右侧无圆角 |
- 【强制】color 资源使用#AARRGGBB 格式,写入 modul_colors.xml 文件中,命名格式采用以下规则:color_小写色值,(color_black,color_white,color_theme除外)
正例:<color name="color_009978">#009978</color>
反例:<color name="txt_disable_color">#005546</color>
- 【强制】dimen 资源以小写单词+下划线方式命名,写入 module_dimens.xml 文件中,采用以下规则:dp_数值 / sp_数值
正例:<dimen name="dp_15">15dp</dimen>
<dimen name="dp__5">-5dp</dimen>(负值时 两个下划线)
<dimen name="dp_21_05">21.5dp</dimen>(21.05dp 按照21dp处理)
<dimen name="sp_16">16sp</dimen>
- 反例:<dimen name="dialog_title_margin_top_bottom">25dp</dimen>
【推荐】Id 资源原则上以驼峰法命名,View 组件的资源 id 需要以 View 的缩写作为前缀。
组件缩写_页面名_职责,如职位详情页-职位名 tv_jd_position_name / tvPositionName
常用缩写表如下:
控件 |
资源命名 |
控件命名 |
TextView |
tv_jd_position_name tv_jd_hr_name |
tvPositionName tvHrName |
Button |
btn_collect_send btn_collect_cancel |
btnSend btnCancel |
ImageView |
iv_im_company_logo iv_im_hr_header |
ivCompanyLogo ivHrHeader |
ConstrantLayout |
cl_publish_root cl_deliver_info |
clRoot clInfo |
LinearLayout |
ll_publish_root |
llRoot |
EditText |
et_resume_experience |
etExperience |
ViewPager |
vp_position_list |
vpPositions |
RecyclerView |
rv_home_recommend |
recyclerViewRecommend |
其它控件的缩写推荐使用小写字母并用下划线进行分割,例如:
ProgressBar 对应的缩写为 progress_bar
DatePicker 对应的缩写为 date_picker
- 代码格式
- 【推荐】单行字符数限制不超过 120 个,超出需要换行,换行时遵循如下原则:
- 1) 第二行相对第一行缩进 4 个空格,从第三行开始,不再继续缩进,参考示例。
- 2) 运算符与下文一起换行。
- 3) 方法调用的点符号与下文一起换行。
- 4) 方法调用时,多个参数,需要换行时,在逗号后进行。
- 5) 在括号前不要换行,见反例。
//超长反例
CommonNetManager.requestPositionMark(String.valueOf(mData.getPositionId()), new OnAdapterCommonListenerImpl<PositionMarkResponse.PositionMarksBean, HttpResponse<PositionMarkResponse.PositionMarksBean>>(activity) {
if (recyclerView != null && recyclerView.getAdapter() != null && newState == RecyclerView.SCROLL_STATE_IDLE && lastVisibleItem + 1 == recyclerView.getAdapter().getItemCount() && !isFetchingData) {
正例:
StringBuffer sb = new StringBuffer();
// 超过 120 个字符的情况下,换行缩进 4 个空格,点号和方法名称一起换行
sb.append("https:").append("//")...
.append("lagou")...
.append(".")...
.append("com");
反例:
StringBuffer sb = new StringBuffer();
// 超过 120 个字符的情况下,不要在括号前换行
sb.append("剩余").append("25")...append
("个职位");
// 参数很多的方法调用可能超过 120 个字符,不要在逗号前换行
method(args1, args2, args3, ...
, argsX);
- 【推荐】XML布局文件嵌套不宜超过5层,如超过5层时,建议重新设计布局方案。改用 ConstraintLayout或RelativeLayout,可以有效降低嵌套数。灵活使用布局,推荐 Merge、ViewStub 来优化布局,尽可能多的减少 UI布局层级,推荐使用 FrameLayout,LinearLayout、RelativeLayout 次之。
说明:
Android 应用页面上任何一个 View 都需要经过 measure、layout、draw 三个步骤才能被正确的渲染。从 xml layout 的顶部节点开始进行 measure,每个子节点都需要向自己的父节点提供自己的尺寸来决定展示的位置,在此过程中可能还会重新measure(由此可能导致 measure 的时间消耗为原来的 2-3 倍)。节点所处位置越深,套嵌带来的 measure 越多,计算就会越费时。这就是为什么扁平的 View 结构会性能更好。同时,页面拥有的 View 越多,measure、layout、draw 所花费的时间就越久。要缩短这个时间,关键是保持 View 的树形结构尽量扁平,而且要移除所有不需要渲染的View。理想情况下,总共的 measure,layout,draw 时间应该被很好的控制在 16ms以内,以保证滑动屏幕时 UI 的流畅。要找到那些多余的 View(增加渲染延迟的 view),可以用 Android Studio Monitor里的 Hierarachy Viewer 工具,可视化的查看所有的 view。多重嵌套导致 measure 以及 layout 等步骤耗时过多。
- 【推荐】表达选择分支时,少用if...else...,可以
if (condition) {
……
return obj;
}
// 接着写 else 的业务逻辑代码;
躲不开if...else if...else 时,不宜超过3层,建议使用“卫语句”代替
- 【推荐】方法体内的执行语句组、变量的定义语句组、不同的业务逻辑之间或者不同的语义之间插入一个空行。相同业务逻辑和语义之间不需要插入空行。
说明:没有必要插入多个空行进行隔开。
- 【强制】不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator 方式,如果并发操作,需要对 Iterator 对象加锁。
- 【强制】for循环对集合做remove时,须采用倒序或者Iterator方式。
说明:AS中倒序快捷键 list.forr + 回车
- 【强制】使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。
说明:asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。注意:数组修改,集合也会跟着变化。
- 【推荐】使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。
说明:keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出 key 所对应的 value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效 率更高。如果是 JDK8,使用 Map.foreach 方法。
正例:values()返回的是 V 值集合,是一个 list 集合对象;keySet()返回的是 K 值集合,是
一个 Set 集合对象;entrySet()返回的是 K-V 值组合集合。
- 【推荐】集合初始化时,指定集合初始值大小。
说明:HashMap 使用 HashMap(int initialCapacity) 初始化, 正例:initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即 loader factor)默认为 0.75,如果暂时无法确定初始值大小,请设置为 16(即默认值)。
反例:HashMap 需要放置 1024 个元素,由于没有设置容量初始大小,随着元素不断增加,容量 7 次*扩大,resize 需要重建 hash 表,严重影响性能。
- 【推荐】数组,集合根据下标索引访问元素时(add / remove),应做越界判断。
正例
private void setData(int currIndex) {
int safeIndex = Math.max(0, currIndex) % positionList.size();
PositionBean positionBean = positionList.get(safeIndex);
}
反例:list.remove( index);
- 【强制】ArrayList <Integer> 按照元素值remove时,注意区分按下标移除;
int index;
int value;
……
ArrayList<Integer> idsList = bean.getIds();
idsList.remove(index % idsList.size());//按照下标删除
idsList.remove((Integer) value);//按照数值删除元素
- 【推荐】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。牵涉频繁创建销毁开销时要有池化意识。
- 【推荐】循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、获取数据库连接,进行不必要的 try-catch 操作(这个 try-catch 是否可以移至循环体外)。
- Android基本组件:
- 【强制】Activity 间的数据通信,对于数据量比较大的(>1M ),避免使用 Intent + Parcelable的方式,可以考虑 EventBus 等替代方案,以免造成 TransactionTooLargeException或者OOM。
- 【推荐】如非必须,避免使用嵌套的 Fragment。
说明:嵌套 Fragment 是在 Android API 17 添加到 SDK 以及 Support 库中的功能,
Fragment 嵌套使用会有一些坑,容易出现 bug,比较常见的问题有如下几种:
- onActivityResult()方法的处理错乱,内嵌的 Fragment 可能收不到该方法的回调,需要由宿主Fragment 进行转发处理;
- 突变动画效果;
- 被继承的 setRetainInstance(),导致在 Fragment 重建时多次触发不必要的逻辑。
非必须的场景尽可能避免使用嵌套 Fragment,如需使用请注意上述问题。
- 【推荐】对于只用于应用内的广播,优先使用 LocalBroadcastManager 来进行注册和发送,LocalBroadcastManager 安全性更好,同时拥有更高的运行效率。
- 【推荐】 添 加 Fragment 时 , 确 保 FragmentTransaction#commit() 在Activity#onPostResume()或者 FragmentActivity#onResumeFragments()内调用。不要随意使用 FragmentTransaction#commitAllowingStateLoss()来代替,任何commitAllowingStateLoss()的使用必须经过 code review,确保无负面影响。
说明:
Activity 可 能 因 为 各 种 原 因 被 销 毁 , Android 支 持 页 面 被 销 毁 前 通 过Activity#onSaveInstanceState() 保 存 自 己 的 状 态 。 但 如 果FragmentTransaction.commit()发生在 Activity 状态保存之后,就会导致 Activity 重建、恢复状态时无法还原页面状态,从而可能出错。为了避免给用户造成不好的体验,系统会抛出 IllegalStateExceptionStateLoss 异常。推荐的做法是在 Activity 的onPostResume() 或 onResumeFragments() ( 对 FragmentActivity )里执行FragmentTransaction.commit(),如有必要也可在 onCreate()里执行。不要随意改用FragmentTransaction.commitAllowingStateLoss() 或 者 直 接 使 用 try-catch 避 免crash,这不是问题的根本解决之道,当且仅当你确认 Activity 重建、恢复状态时,本次 commit 丢失不会造成影响时才可这么做。
- 【强制】Activity或者 Fragment 中动态注册BroadCastReceiver 时,registerReceiver()和 unregisterReceiver()要成对出现。同理,OkBus的注册和 解注册 也必须成对出现。
说明:
- 如果 registerReceiver()和 unregisterReceiver()不成对出现,则可能导致已经注册的receiver 没有在合适的时机注销,导致内存泄漏,占用内存空间,加重 SystemService负担。
- 部分华为的机型会对 receiver 进行资源管控,单个应用注册过多 receiver 会触发管控模块抛出异常,应用直接崩溃。
- Activity 的生命周期不对应,可能出现多次 onResume 造成 receiver 注册多个,但最终只注销一个,其余 receiver 产生内存泄漏。
- 【强制】不能使用 ScrollView 包裹 ListView/GridView/ExpandableListVIew;因为这样会把 ListView 的所有 Item 都加载到内存中,要消耗巨大的内存和 cpu 去绘制图面。
说明:
ScrollView 中嵌套 List 或 RecyclerView 的做法官方明确禁止。除了开发过程中遇到的各种视觉和交互问题,这种做法对性能也有较大损耗。ListView 等 UI 组件自身有垂直滚动功能,也没有必要再嵌套一层 ScrollView。目前为了较好的 UI 体验,更贴近 Material Design 的设计,推荐使用NestedScrollView。
- 【强制】不要在非 UI 线程中初始化 ViewStub,否则会返回 null。
- 【强制】在 Activity.onPause()或 Activity.onStop()回调中,关闭当前 activity 正在执行的的动画。
- 【推荐】 禁止在多进程之间用 SharedPreferences共享数据 ,虽然可以(MODE_MULTI_PROCESS),但官方已不推荐。
- 【推荐】不使用@Deprecated标记的方法,应了解最新方法的使用。
- 【强制】任何时候不要硬编码文件路径,请使用 Android 文件系统 API 访问。
说明:
Android 应用提供内部和外部存储,分别用于存放应用自身数据以及应用产生的用户数据。可以通过相关 API 接口获取对应的目录,进行文件操作。
android.os.Environment#getExternalStorageDirectory()
android.os.Environment#getExternalStoragePublicDirectory()
android.content.Context#getFilesDir()
android.content.Context#getCacheDir()
- 【推荐】当使用外部存储时,必须检查外部存储的可用性。
// 读/写检查
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
// 只读检查
public boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}
- 【参考】 SharedPreference 提交数据时 , 尽量使用 Editor#apply() ,而非Editor#commit()。一般来讲,仅当需要确定提交结果,并据此有后续操作时,才使用 Editor#commit()。
说明:
SharedPreference 相关修改使用 apply 方法进行提交会先写入内存,然后异步写入磁盘,commit 方法是直接写入磁盘。如果频繁操作的话 apply 的性能会优于 commit,apply 会将最后修改内容写入磁盘。但是如果希望立刻获取存储操作的结果,并据此做相应的其他操作,应当使用 commit。
- 【强制】执行 SQL 语句时,应使用 SQLiteDatabase#insert()、update()、delete(),不要使用 SQLiteDatabase#execSQL(),以免 SQL 注入风险。
- 【强制】对于内部使用的组件,显式设置组件的"android:exported"属性为 false。
说明:
Android 应用使用 Intent 机制在组件之间传递数据,如果应用在使用 getIntent(),getAction(),Intent.getXXXExtra()获取到空数据、异常或者畸形数据时没有进行异常捕获,应用就会发生 Crash,应用不可使用(本地拒绝服务)。恶意应用可通过向受害者应用发送此类空数据、异常或者畸形数据从而使应用产生本地拒绝服务。
- 【推荐】不要在 Activity#onDestroy()内执行释放资源的工作,例如一些工作线程的 销毁和停止,因为 onDestroy()执行的时机可能较晚。可根据实际需要,在 Activity#onPause()/onStop()中结合isFinishing()的判断来执行。
- 【推荐】总是使用显式Intent启动或者绑定Service,且不要为服务声明IntentFilter, 保证应用的安全性。如果确实需要使用隐式调用,则可为 Service 提供 Intent Filter 并从 Intent 中排除相应的组件名称,但必须搭配使用 Intent#setPackage()方法设置 Intent 的指定包名,这样可以充分消除目标服务的不确定性。
- 【推荐】当前 Activity 的 onPause 方法执行结束后才会创建(onCreate)或恢复 (onRestart)别的 Activity,所以在 onPause 方法中不适合做耗时较长的工作,这 会影响到页面之间的跳转效率。
- 【强制】不要在 Android 的 Application 对象中缓存数据。基础组件之间的数据共享请使用 Intent 等机制,也可使用 SharedPreferences 等数据持久化机制。
- 【推荐】在 Activity 中显示对话框或弹出浮层时,尽量使用 DialogFragment,而非Dialog/AlertDialog,这样便于随Activity生命周期管理对话框/弹出浮层的生命周期。
- 【强制】Log应使用统一工具类,Log 的 tag 不能是" ",Log中禁止打印敏感信息,如密码。
- 【推荐】在有强依赖 onAnimationEnd 回调的交互时,如动画播放完毕才能操作页面。onAnimationEnd 可能会因各种异常没被回调,建议加上超时保护或通过postDelay 替代onAnimationEnd。
final FadeUpAnimation anim = new FadeUpAnimation(v);
anim.setInterpolator(new AccelerateInterpolator());
anim.setDuration(1000);
anim.setFillAfter(true);
new Handler().postDelayed(new Runnable() {
public void run() {
v.clearAnimation();
//Extra work goes here
}
}, anim.getDuration());
v.startAnimation(anim);
- 内存
- 【强制】加载大图片或者一次性加载多张图片,应该在异步线程中进行。图片的加载,涉及到 IO 操作,以及 CPU 密集操作,很可能引起卡顿。
- 【强制】png 图片使用 tinypng 或者类似工具压缩处理,减少包体积。
- 【强制】不要通过 Msg 传递大的对象,会导致内存问题。
- 【推荐】使用 ARGB_565 代替 ARGB_888,在不明显降低视觉效果的前提下,减少内存占用。
说明:RGB_565 能够在保证图片质量的情况下大大减少内存的开销,是解决 oom 的一种方法。但是一定要注意 RGB_565 是没有透明度的,如果图片本身需要保留透明度,那么就不能使用 RGB_565。
- 【推荐】当 View Animation 执行结束时,调用 View.clearAnimation()释放相关资源。
- 【参考】尽量减少 Bitmap(BitmapDrawable)的使用,尽量使用纯色(ColorDrawable)、渐变色(GradientDrawable)、StateSelector(StateListDrawable)等与 Shape 结合的形式构建绘图。
- 【参考】谨慎使用 gif 图片,注意限制每个页面允许同时播放的 gif 图片,以及单个gif 图片的大小
- 【参考】大图片资源不要直接打包到 apk,可以考虑通过文件仓库远程下载,减小包体积。
- 注释规约
团队协作中注释和代码一样重要。除非你写的代码,无需解释,任何人一眼就能看明白。
- 【强制】类、类属性、类方法的注释必须使用 Javadoc 规范,使用/*内容/格式,不得使用// xxx 方式。
说明:在 IDE 编辑窗口中,Javadoc 方式会提示相关注释,生成 Javadoc 可以正确输出相应注释;在 IDE 中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高阅读效率。
- 【强制】所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。
说明:对子类的实现要求,或者调用注意事项,请一并说明。
- 【强制】所有的枚举类型字段必须要有注释,说明每个数据项的用途。
- 【强制】代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑等的修改。
说明:代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后,就失去了导航的意义。
- 【参考】对于注释的要求:第一、能够准确反应设计思想和代码逻辑;第二、能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。完全没有注释的大段代码对于阅读者形同天书,注释是给自己看的,即使隔很长时间,也能清晰理解当时的思路;注释也是给继任者看的,使其能够快速接替自己的工作。
- 【参考】好的命名、代码结构是自解释的,注释力求精简准确、表达到位。避免出现注释的一个极端:过多过滥的注释,代码的逻辑一旦修改,修改注释是相当大的负担。
另外业务逻辑杂糅时,注释应①②③分条说明。
正例:
- 【参考】 待办事宜(TODO)注释标记,请注明标记人与标记时间。注意及时处理这些TODO标记,通过标记扫描,经常清理此类标记。格式:( 标记人,标记时间,[预计处理时间]) 表示需要实现,但目前还未实现的功能。这实际上是一个 Javadoc 的标签,目前的 Javadoc还没有实现,但已经被广泛使用。只能应用于类,接口和方法(因为它是一个 Javadoc 标签)。
- 【参考】谨慎注释掉代码。在上方详细说明,而不是简单地注释掉。如果无用,则删除。
说明:代码被注释掉有两种可能性:1)后续会恢复此段代码逻辑。2)永久不用。前者如果没有备注信息,难以知晓注释动机。后者建议直接删掉(代码仓库保存了历史代码)。