113
社区成员
发帖
与我相关
我的任务
分享| 这个作业属于哪个课程 | https://bbs.csdn.net/forums/2401_CS_SE_FZU |
|---|---|
| 这个作业要求在哪里 | https://bbs.csdn.net/topics/619428294 |
| 团队名称 | 新苗Sprout |
| 这个作业的目标 | Beta冲刺总结 |
| 其他参考文献 | 无 |
Lang属性的取值应该遵循互联网工程任务组–IETF(The Internet Engineering Task Force)制定的关于语言标签的文档
因为 ISO-8859 中字符集大小是有限的,且在多语言环境中不兼容,所以 Unicode 联盟开发了 Unicode 标准。
Unicode 标准覆盖了(几乎)所有的字符、标点符号和符号。
Unicode 使文本的处理、存储和运输,独立于平台和语言。
HTML-5 中默认的字符编码是 UTF-8
HTML元素共有以下5种:
元素标签的闭合应遵循以下原则:
HTML标签名、类名、标签属性和大部分属性值统一用小写
推荐:
<div class="demo"></div>
不推荐:
<div class="DEMO"></div>
<DIV CLASS="DEMO"></DIV>
HTML文本、CDATA、JavaScript、meta标签某些属性等内容可大小写混合
<!-- 优先使用 IE 最新版本和 Chrome Frame -->
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<!-- HTML文本内容 -->
<h1>I AM WHAT I AM </h1>
<!-- JavaScript 内容 -->
<script type="text/javascript">
var demoName = 'demoName';
...
</script>
<!-- CDATA 内容 -->
<script type="text/javascript"></script>
不需要为 CSS、JS 指定类型属性,HTML5 中默认已包含
推荐:
<link rel="stylesheet" href="" >
<script src=""></script>
不推荐:
<link rel="stylesheet" type="text/css" href="" >
<script type="text/javascript" src="" ></script>
推荐:
<input type="text">
<input type="radio" name="name" checked="checked" >
不推荐:
<input type=text>
<input type='text'>
<input type="radio" name="name" checked >
文本可以和字符引用混合出现。这种方法可以用来转义在文本中不能合法出现的字符。
在 HTML 中不能使用小于号 “<” 和大于号 “>”特殊字符,浏览器会将它们作为标签解析,若要正确显示,在 HTML 源代码中使用字符实体
推荐:
<a href="#">more>></a>
不推荐:
<a href="#">more>></a>
使用 type="tel" 而不是 type="number"、
元素嵌套规范,每个块状元素独立一行,内联元素可选
推荐:
<div>
<h1></h1>
<p></p>
</div>
<p><span></span><span></span></p>
不推荐:
<div>
<h1></h1><p></p>
</div>
<p>
<span></span>
<span></span>
</p>
段落元素与标题元素只能嵌套内联元素
推荐:
<h1><span></span></h1>
<p><span></span><span></span></p>
不推荐:
<h1><div></div></h1>
<p><div></div><div></div></p>
一般用于简单的描述,如某些状态描述、属性描述等
注释内容前后各一个空格字符,注释位于要注释代码的上面,单独占一行
推荐:
<!-- Comment Text -->
<div>...</div>
不推荐:
<div>...</div><!-- Comment Text -->
<div><!-- Comment Text -->
...
</div>
一般用于描述模块的名称以及模块开始与结束的位置
注释内容前后各一个空格字符,<!-- S Comment Text --> 表示模块开始,<!-- E Comment Text --> 表示模块结束,模块与模块之间相隔一行
推荐写法:
<!-- S Comment Text A -->
<div class="mod_a">
...
</div>
<!-- E Comment Text A -->
<!-- S Comment Text B -->
<div class="mod_b">
...
</div>
<!-- E Comment Text B -->
不推荐写法:
<!-- S Comment Text A -->
<div class="mod_a">
...
</div>
<!-- E Comment Text A -->
<!-- S Comment Text B -->
<div class="mod_b">
...
</div>
<!-- E Comment Text B -->
当模块注释内再出现模块注释的时候,为了突出主要模块,嵌套模块不再使用
<!-- S Comment Text -->
<!-- E Comment Text -->
而改用
<!-- /Comment Text -->
注释写在模块结尾标签底部,单独一行。
<!-- S Comment Text A -->
<div class="mod_a">
<div class="mod_b">
...
</div>
<!-- /mod_b -->
<div class="mod_c">
...
</div>
<!-- /mod_c -->
</div>
<!-- E Comment Text A -->
内容图多以商品图等照片类图片形式存在,颜色较为丰富,文件体积较大
背景图多为图标等颜色比较简单、文件体积不大、起修饰作用的图片
中国普通家庭的宽带基本能达到8Mbps,实际速率大约为500—900KB/s,全国3G/4G用户占有比超过了50%,为了保证图片能更好地加载展示给用户看,团队约定:
HTML 中图片引入不需添加 width、height 属性,alt 属性应该写上
推荐:
<img src="" alt="" >
不推荐:
<img src="" width="" height="" >
CSS 中图片引入不需要引号
.jdc {
background-image: url(icon.png);
}
推荐:
@charset "UTF-8";
.jdc{}
不推荐:
/**
* @desc File Info
* @author Author Name
* @date 2015-10-10
*/
/* @charset规则不在文件首行首个字符开始 */
@charset "UTF-8";
.jdc{}
@CHARSET "UTF-8";
/* @charset规则没有用小写 */
.jdc{}
/* 无@charset规则 */
.jdc{}
样式书写一般有两种:一种是紧凑格式 (Compact)
.jdc{ display: block;width: 50px;}
一种是展开格式(Expanded)
.jdc{
display: block;
width: 50px;
}
样式选择器,属性名,属性值关键字全部使用小写字母书写,属性字符串允许使用大小写。
/* 推荐 */
.jdc{
display:block;
}
/* 不推荐 */
.JDC{
DISPLAY:BLOCK;
}
*/* 推荐 */
.jdc {}
.jdc li {}
.jdc li p{}
/* 不推荐 */
*{}
#jdc {}
.jdc div{}
统一使用四个空格进行代码缩进,使得各编辑器表现一致(各编辑器有相关配置)
.jdc {
width: 100%;
height: 100%;
}
每个属性声明末尾都要加分号;
.jdc {
width: 100%;
height: 100%;
}
css属性值需要用到引号时,统一使用单引号
/* 推荐 */
.jdc {
font-family: 'Hiragino Sans GB';
}
/* 不推荐 */
.jdc {
font-family: "Hiragino Sans GB";
}
注释内容第一个字符和最后一个字符都是一个空格字符,单独占一行,行与行之间相隔一行
推荐:
/* Comment Text */
.jdc{}
/* Comment Text */
.jdc{}
不推荐:
/*Comment Text*/
.jdc{
display: block;
}
.jdc{
display: block;/*Comment Text*/
}
注释内容第一个字符和最后一个字符都是一个空格字符,/* 与 模块信息描述占一行,多个横线分隔符-与*/占一行,行与行之间相隔两行
推荐:
* Module A
---------------------------------------------------------------- */
.mod_a {}
/* Module B
---------------------------------------------------------------- */
.mod_b {}
不推荐:
/* Module A ---------------------------------------------------- */
.mod_a {}
/* Module B ---------------------------------------------------- */
.mod_b {}
考虑到 SCSS 语法对 CSS 语法友好的兼容性和扩展性,我们在使用 SASS 编写样式的时候,统一使用 SCSS 语法
当在 Ruby1.9或更新的版本运行的时候,SASS 能识辨文档的字符编码。SASS 遵循 CSS 规范去确定样式文件的编码,进而转回 Ruby 字符串编码。这意味着SASS编译的时候会首先检测 BOM,然后到 @charset 声明,再到 Ruby 字符串编码,如果以上都没有设置,SASS 会认为文档的编码为 UTF-8
SCSS 文件内
/*! */ 注释方式标准的注释规范如下:
@charset "UTF-8";
/**
* @desc File Info
* @author liqinuo
* @date 2015-10-10
*/
/* Module A
----------------------------------------------------------------*/
.mod_a {}
/* module A logo */
.mod_a_logo {}
/* module A nav */
.mod_a_nav {}
/* Module B
----------------------------------------------------------------*/
.mod_b {}
/* module B logo */
.mod_b_logo {}
/* module B nav */
.mod_b_nav {}
可复用属性尽量抽离为页面变量,易于统一维护
// CSS
.jdc {
color: red;
border-color: red;
}
// SCSS
$color: red;
.jdc {
color: $color;
border-color: $color;
}
根据功能定义模块,然后在需要使用的地方通过 @include 调用,避免编码时重复输入代码段
// CSS
.jdc_1 {
-webkit-border-radius: 5px;
border-radius: 5px;
}
.jdc_2 {
-webkit-border-radius: 10px;
border-radius: 10px;
}
// SCSS
@mixin radius($radius:5px) {
-webkit-border-radius: $radius;
border-radius: $radius;
}
.jdc_1 {
@include radius; //参数使用默认值
}
.jdc_2 {
@include radius(10px);
}
// CSS
.jdc_1 {
background: url(/img/icon.png) no-repeat -10px 0;
}
.jdc_2 {
background: url(/img/icon.png) no-repeat -20px 0;
}
// SCSS
@mixin icon($x:0, $y:0) {
background: url(/img/icon.png) no-repeat $x, $y;
}
.jdc_1 {
@include icon(-10px, 0);
}
.jdc_2 {
@include icon(-20px, 0);
}
如果不调用则不会有任何多余的 css 文件,占位选择器以 % 标识定义,通过 @extend 调用
//scss
%borderbox {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.jdc {
@extend %borderbox;
}
// CSS
.jdc_1 {
font-size: 12px;
color: red;
}
.jdc_2 {
font-size: 12px;
color: red;
font-weight: bold;
}
// SCSS
.jdc_1 {
font-size: 12px;
color: red;
}
.jdc_2 {
@extend .jdc_1;
font-weight: bold;
}
// 或者
%font_red {
font-size: 12px;
color: red;
}
.jdc_1 {
@extend %font_red;
}
.jdc_2 {
@extend %font_red;
font-weight: bold;
}
// CSS
.jdc_1 {background-position: 0 -20px;}
.jdc_2 {background-position: 0 -40px;}
.jdc_3 {background-position: 0 -60px;}
// SCSS
@for $i from 1 through 3 {
.jdc_#{$i} {
background-position: 0 (-20px) * $i;
}
}
@function pxToRem($px) {
@return $px / 10px * 1rem;
}
.jdc {
font-size: pxToRem(12px);
}
图片命名建议以以下顺序命名:
图片业务(可选) +(mod_)图片功能类别(必选)+ 图片模块名称(可选) + 图片精度(可选)
业务交叉协作的时候,为了避免图片命名冲突,建议图片名加上业务和模块前辍,如拍拍侧和手Q侧的业务交叉合作时,侧栏导航icon雪碧图命名:
推荐:
pp_icon_mod_sidenav.png
不推荐:
icon_mod_sidenav.png
处理高清图片的时候,命名应该加上图片相应的精度说明
推荐:
jdc_logo@1x.png
jdc_logo@2x.png
不推荐:
jdc_logo.png
jdc_logo_retina.png
确保文件命名总是以字母开头而不是数字,且字母一律小写,以下划线连接且不带其他标点符号,如:
<!-- HTML -->
jdc.html
jdc_list.html
jdc_detail.html
<!-- SASS -->
jdc.scss
jdc_list.scss
jdc_detail.scss
祖先模块不能出现下划线,除了是全站公用模块,如 mod_ 系列的命名:
推荐:
<div class="modulename">
<div class="modulename_info">
<div class="modulename_son"></div>
<div class="modulename_son"></div>
...
</div>
</div>
<!-- 这个是全站公用模块,祖先模块允许直接出现下划线 -->
<div class="mod_info">
<div class="mod_info_son"></div>
<div class="mod_info_son"></div>
...
</div>
不推荐:
<div class="modulename_info">
<div class="modulename_info_son"></div>
<div class="modulename_info_son"></div>
...
</div>
在子孙模块数量可预测的情况下,严格继承祖先模块的命名前缀
<div class="modulename">
<div class="modulename_cover"></div>
<div class="modulename_info"></div>
</div>
当子孙模块超过4级或以上的时候,可以考虑在祖先模块内具有识辨性的独立缩写作为新的子孙模块
推荐:
<div class="modulename">
<div class="modulename_cover"></div>
<div class="modulename_info">
<div class="modulename_info_user">
<div class="modulename_info_user_img">
<img src="" alt="">
<!-- 这个时候 miui 为 modulename_info_user_img 首字母缩写-->
<div class="miui_tit"></div>
<div class="miui_txt"></div>
...
</div>
</div>
<div class="modulename_info_list"></div>
</div>
</div>
不推荐:
<div class="modulename">
<div class="modulename_cover"></div>
<div class="modulename_info">
<div class="modulename_info_user">
<div class="modulename_info_user_img">
<img src="" alt="">
<div class="modulename_info_user_img_tit"></div>
<div class="modulename_info_user_img_txt"></div>
...
</div>
</div>
<div class="modulename_info_list"></div>
</div>
</div>
全站公共模块:以 mod_ 开头
<div class="mod_yours"></div>
务公共模块:以 业务名_mod_ 开头
<div class="paipai_mod_yours"></div>
注意:ad、banner、gg、guanggao 等有机会和广告挂勾的字眠不建议直接用来做ClassName,因为有些浏览器插件(Chrome的广告拦截插件等)会直接过滤这些类名,因此
<div class="ad"></div>
这种广告的英文或拼音类名不应该出现
另外,敏感不和谐字眼也不应该出现,如:
<div class="fuck"></div>
<div class="jer"></div>
<div class="sm"></div>
<div class="gcd"></div>
<div class="ass"></div>
<div class="KMT"></div>
...
暂时无法在飞书文档外展示此内容
JavaScript 是一种客户端脚本语言,这里列出了编写 JavaScript 时需要遵守的规则。
const 和 let 都是块级作用域,var 是函数级作用域const,不要使用 var原因:这样做可以确保你无法重新分配引用,以避免出现错误和难以理解的代码
let 代替 var原因:
let是块级作用域的,而不像var属于函数级作用域
原因:因为这样做就可以让你在一个地方定义所有的对象属性
原因:这样更简短且描述更清楚
原因:这样更容易区分哪些属性用了简写的方式
Object.prototype 的方法, 例如 hasOwnProperty, propertyIsEnumerable 和 isPrototypeOf 方法原因:这些方法可能会被对象自身的同名属性覆盖 - 比如
{ hasOwnProperty: false }或者对象可能是一个null对象(Object.create(null))
... 来做对象浅拷贝而不是使用 Object.assign,使用对象剩余操作符来获得一个包含确定的剩余属性的新对象// bad
const items = new Array()
// good
const items = []
push 方法const items = []
// bad
items[items.length] = 'test'
// good
items.push('test')
... 复制数组... 而不是 Array.fromArray.from 来将一个类数组对象转换为数组Array.from 代替扩展运算符 ..., 因为这可以避免创建中间数组map 等方法时,请使用 return 声明,如果是单一声明语句的情况,可省略 return// good
[1, 2, 3].map(x => {
const y = x + 1
return x * y
})
// good
[1, 2, 3].map(x => x + 1)
// bad
const flat = {}
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
const flatten = memo.concat(item)
flat[index] = flatten
})
// good
const flat = {}
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
const flatten = memo.concat(item)
flat[index] = flatten
return flatten
})
// bad
inbox.filter((msg) => {
const { subject, author } = msg
if (subject === 'Mockingbird') {
return author === 'Harper Lee'
} else {
return false
}
})
// good
inbox.filter((msg) => {
const { subject, author } = msg
if (subject === 'Mockingbird') {
return author === 'Harper Lee'
}
return false
})
愿意:解构可以避免创建属性的临时引用
原因:可以非破坏性地随时增加或者改变属性顺序
\,而是使用 +原因:此方式创建函数和对字符串使用
eval()一样会产生漏洞
原因:这样做会导致函数声明被提升,这意味着很容易在文件中定义此函数之前引用它,不利于可读性和可维护性。如果你发现函数定义既庞大又复杂以至于不能理解文件的其他部分,或许你应该将它拆分成模块!别忘记要显式命名表达式,而不用管名字是否是从包含的变量(通常出现在现代浏览器中或者使用 Babel 编译器的时候)中推断的。这样会消除错误调用堆栈中的任何假设。 (讨论)
原因:一个立即执行匿名函数表达式是一个单一的单元,将其及其调用括号包装在括号中,能够清楚地表达这一点。注意,在到处都是模块的世界中几乎不需要 IIFE。
if , while 等)中声明函数arguments,会导致该参数的优先级高于每个函数作用域内原先存在的 arguments 对象arguments,使用 剩余运算符 ...arguments 只是一个类数组,而 ... 是一个真正的数组原因:操作作为参数传入的对象可能在原始调用中造成意想不到的变量副作用
原因:参数重新赋值可能会导致无法预期的行为,尤其是当操作
arguments对象时,也可能导致优化问题,尤其是在 V8 引擎中
....原因:显然你无需使用上下文,很难结合
new和apply
原因:它将创建在
this上下文中执行的函数版本,通常是您想要的,并且语法更简洁如果您有一个相当复杂的函数,则可以将该逻辑移到其自己的命名函数表达式中
return, 否则保留花括号并使用 return 语句class,避免直接操作 prototypeextends 来实现继承原因:这是一个不会破坏
instanceof的内建实现原型式继承的方式
原因:重复的类成员声明会默认使用最后声明的,通常会导致 bug
import 和 export原因:模块是未来,让我们现在开始使用未来的特性
import 的通配符 *,这样可以确保你只有一个默认的 exportimport 一次,有多个 import 请书写在一起原因:这样可以让代码更易于维护
import 语句放在文件最前方import 声明中禁止使用 Webpack 的 loader 语法,iterators,建议使用 JS 更高优先级的函数代替 for-in 或 for-of 循环,除非迫不得已generator原因:因为不能很好地翻译成 ES5 代码
. 来访问对象属性[]const、let 关键字,如果没有写关键字,变量就会暴露在全局上下文中,这样很可能会和现有变量冲突,另外,也很难明确该变量的作用域是什么。这里推荐使用 const 来声明变量,我们需要避免全局命名空间的污染。const 和 let 分组原因:变量链式赋值会创建隐藏的全局变量
原因:声明但未被使用的变量通常是不完全重构犯下的错误.这种变量在代码里浪费空间并会给读者造成困扰
var 存在变量提升的情况,即 var 声明会被提升至该作用域的顶部,但是他们的赋值并不会。而 const 和 let 并不存在这种情况=== 和 !== 而非 == 和 !=if 会用 ToBoolean 这个抽象方法将表达式转成布尔值并遵循如下规则Objects 等于 trueUndefined 等于 falseNull 等于 falseBooleans 等于 布尔值Numbers 在 +0, -0, 或者 NaN 的情况下等于 false, 其他情况是 trueStrings 为 '' 时等于 false, 否则是 trueStandard 的规范,不使用分号。
- 关于应不应该使用分号的讨论有很多,本规范认为非必要的时候,应该不使用分号,好的
JS程序员应该清楚场景下是一定要加分号的,相信你也是名好的开发者。
为了代码的可移植性和兼容性,我们应该最大化的使用标准方法,例如优先使用 string.charAt(3) 而不是 string[3]
由于 eval 方法比较 evil,所以我们约定禁止使用该方法
由于 with 方法会产生神奇的作用域,所以我们也是禁止使用该方法的
不要修改内置对象,如 Object 和 Array
在单行代码块中使用空格
不推荐
推荐
在编程过程中,大括号风格与缩进风格紧密联系,用来描述大括号相对代码块位置的方法有很多。在 JavaScript 中,主要有三种风格
我们团队约定使用 One True Brace Style 风格
当命名变量时,主流分为驼峰式命名(constiableName)和下划线命名(constiable_name)两大阵营。
团队约定使用驼峰式命名
在 ECMAScript5 里面,对象字面量中的拖尾逗号是合法的,但在 IE8(非 IE8 文档模式)下,当出现拖尾逗号,则会抛出错误。
拖尾逗号的例子:
拖尾逗号的好处是,简化了对象和数组添加或删除元素,我们只需要修改新增的行即可,并不会增加差异化的代码行数。
因为拖尾逗号有好也有不好,所以团队约定允许在最后一个元素或属性与闭括号
]或}在不同行时,可以(但不要求)使用拖尾逗号。当在同一行时,禁止使用拖尾逗号。
逗号前后的空格可以提高代码的可读性,团队约定在逗号后面使用空格,逗号前面不加空格。
逗号分隔列表时,在 JavaScript 中主要有两种逗号风格:
团队约定使用标准风格
不推荐
const foo = 1
,
bar = 2
const foo = 1
, bar = 2
const foo = ['name'
, 'age']
推荐
const foo = 1,
bar = 2
const foo = ['name',
'age']
团队约定在对象的计算属性内,禁止使用空格
在非空文件中,存在拖尾换行是一个常见的 UNIX 风格,它的好处是可以方便在串联和追加文件时不会打断 Shell 的提示。在日常的项目中,保留拖尾换行的好处是,可以减少版本控制时的代码冲突。
为了避免语法错误,团队约定在函数调用时,禁止使用空格
代码保持一致的缩进,是作为工程师的职业素养。但缩进用两个空格,还是四个空格,是用 Tab 还是空格呢?这样的争论太多了,也得不出答案。本规范结合了市面上优秀的开源项目,姑且约定使用 空格 来缩进,而且缩进使用两个空格。
那是不是不能使用 Tab 进行缩进了?我们可以通过配置 .editorconfig ,将 Tab 自动转换为空格。
团队约定对象字面量的键和值之间不能存在空格,且要求对象字面量的冒号和值之间存在一个空格
在 JavaScript 中 new 操作符用来创建某个特定类型的对象的一个实例,该类型的对象是由一个构造函数表示的。由于构造函数只是常规函数,唯一区别是使用 new 来调用。所以我们团队约定构造函数的首字母要大小,以此来区分构造函数和普通函数。
在 JavaScript 中,通过 new 调用构造函数时,如果不带参数,可以省略后面的圆括号。但这样会造成与整体的代码风格不一致,所以团队约定使用圆括号
链式调用如果放在同一行,往往会造成代码的可读性差,但有些时候,短的链式调用并不会影响美观。所以本规范约定一行最多只能有四个链式调用,超过就要求换行。
空白行对于分离代码逻辑有帮助,但过多的空行会占据屏幕的空间,影响可读性。团队约定最大连续空行数为 2
链式赋值容易造成代码的可读性差,所以团队约定禁止使用链式赋值
JavaScript 允许在一个声明中,声明多个变量。团队约定在声明变量时,一个声明只能有一个变量
JavaScript 在所有类 C 语言中是比较独特的,它不需要在每个语句的末尾有分号。在很多情况下,JavaScript 引擎可以确定一个分号应该在什么位置然后自动添加它。此特征被称为 自动分号插入 (ASI),被认为是 JavaScript 中较为有争议的特征。
团队中对于是否应该使用分号,也有许多争论,本规范推荐不使用分号,因为我们认为好的工程师应该知道什么时候该加,什么时候不该加。
一致性是任何风格指南的重要组成部分。虽然在哪里放置块的开括号纯属个人偏好,但在整个项目中应该保持一致。不一致的风格将会分散读者阅读代码的注意力。
当格式化一个函数,函数名或 function 关键字与左括号之间允许有空白。命名函数要求函数名和 function 关键字之间有空格,但是匿名函数要求不加空格。
团队约定函数括号前要加空格
团队约定操作符前后都需要添加空格
Unicode 字节顺序标记 (BOM) 用来指定代码单元是高字节序还是低字节序。也就是说,是高位在前还是低位在前。UTF-8 不需要 BOM 来表明字节顺序,因为单个字节并不影响字节顺序。
源文件以其最顶层的类名来命名,大小写敏感,文件扩展名为.java。
源文件编码格式为UTF-8。
对于具有特殊转义序列的任何字符(\b, \t, \n, \f, \r, \”, \’及),我们使用它的转义序列,而不是相应的八进制(比如\012)或Unicode(比如\u000a)转义。
对于剩余的非ASCII字符,是使用实际的Unicode字符(比如∞),还是使用等价的Unicode转义符(比如\u221e),取决于哪个能让代码更易于阅读和理解。
//最好的,即使没有注释也非常清晰
String unitAbbrev = "μs";
//允许,但没有理由要这样做
String unitAbbrev = "\u03bcs"; // "μs"
//允许,但这样做显得笨拙还容易出错
String unitAbbrev = "\u03bcs"; // Greek letter mu, "s"
//很糟,读者根本看不出这是什么
String unitAbbrev = "\u03bcs";
//Good,对于非打印字符,使用转义,并在必要时写上注释
return '\ufeff' + content; // byte order mark
如果一个文件包含许可证或版权信息,那么它应当被放在文件最前面。
package 语句不换行,列限制(4.4节)并不适用于package语句。(即package语句写在一行里)
即,不要出现类似这样的import语句:import java.util.*;
import语句不换行,列限制并不适用于import语句。
import语句可分为以下几组,按照这个顺序,每组由一个空行分隔:
所有的静态导入独立成组 com.google imports(仅当这个源文件是在com.google包下) 第三方的包。每个顶级包为一组,字典序。例如:android, com, junit, org, sun java imports5.javax imports组内不空行,按字典序排列。
类声明每个顶级类都在一个与它同名的源文件中(当然,还包含.java后缀)。 例外:package-info.java,该文件中可没有package-info类。
建议文件区块整理
当一个类有多个构造函数,或是多个同名方法,这些函数/方法应该按顺序出现在一起,中间不要放进其它函数/方法。
大括号与if, else, for, do, while语句一起使用,即使只有一条语句(或是空),也应该把大括号写上。
对于非空块和块状结构,大括号遵循 Kernighan 和 Ritchie 风格 (Egyptian brackets):
左大括号前不换行 左大括号后换行 右大括号前换行 如果右大括号是一个语句、函数体或类的终止,则右大括号后换行; 否则不换行。 例如,如果右大括号后面是else或逗号,则不换行。
示例:
return () -> {
while (condition()) {
method();
}
};
return new MyClass() {
@Override public void method() {
if (condition()) {
try {
something();
} catch (ProblemException e) {
recover();
}
} else if (otherCondition()) {
somethingElse();
} else {
lastThing();
}
}
};
一个空的块状结构里什么也不包含,大括号可以简洁地写成{},不需要换行。
例外:如果它是一个多块语句的一部分(if/else 或 try/catch/finally) ,即使大括号内没内容,右大括号也要换行。 示例:
//这是可以接受的
void doNothing() {}
// 这也可以接受
void doNothingElse() {
}
// 这样就接受不了了
try {
doSomething();
} catch (Exception e) {}
每当开始一个新的块,缩进增加4个空格,当块结束时,缩进返回先前的缩进级别。缩进级别适用于代码和注释。
每个语句后要换行。
一个项目可以选择一行80个字符或100个字符的列限制,除了下述例外,任何一行如果超过这个字符数限制,必须自动换行。
术语说明:一般情况下,一行长代码为了避免超出列限制(80或100个字符)而被分为多行,我们称之为自动换行(line-wrapping)。我们并没有全面,确定性的准则来决定在每一种情况下如何自动换行。很多时候,对于同一段代码会有好几种有效的自动换行方式。
自动换行的基本准则是:更倾向于在更高的语法级别处断开。
1.如果在非赋值运算符处断开,那么在该符号前断开(比如+,它将位于下一行)。
2.如果在赋值运算符处断开,通常的做法是在该符号后断开(比如=,它与前面的内容留在同一行)。
3.方法名或构造函数名与左括号留在同一行。
4.逗号(,)与其前面的内容留在同一行。
自动换行时,第一行后的每一行至少比第一行多缩进8个空格(注意:制表符不用于缩进。见2.3.1节)。当存在连续自动换行时,缩进可能会多缩进不只8个空格(语法元素存在多级时)。一般而言,两个连续行使用相同的缩进当且仅当它们开始于同级语法元素。
以下情况需要使用一个空行:
1.类内连续的成员之间:字段,构造函数,方法,嵌套类,静态初始化块,实例初始化块。
2.在函数体内,语句的逻辑分组间使用空行。
3.类内的第一个成员前或最后一个成员后的空行是可选的(既不鼓励也不反对这样做,视个人喜好而定)。
4.要满足本文档中其他节的空行要求(比如3.3节:import语句)
多个连续的空行是允许的,但没有必要这样做(我们也不鼓励这样做)。
除了语言需求和其它规则,并且除了文字,注释和Javadoc用到单个空格
术语说明:水平对齐指的是通过增加可变数量的空格来使某一行的字符与上一行的相应字符对齐。 这是允许的,但Google编程风格对此不做要求。即使对于已经使用水平对齐的代码,我们也不需要去保持这种风格。
以下示例先展示未对齐的代码,然后是对齐的代码:
private int x; // this is fine
private Color color; // this too
private int x; // permitted, but future edits
private Color color; // may leave it unaligned
Tip: 对齐可增加代码可读性,但它为日后的维护带来问题。
除非作者和reviewer都认为去掉小括号也不会使代码被误解,或是去掉小括号能让代码更易于阅读,否则我们不应该去掉小括号。
枚举常量间用逗号隔开,换行可选。
private enum Answer {
YES {
@Override public String toString() {
return "yes";
}
},
NO,
MAYBE
}
没有方法和文档的枚举类可写成数组初始化的格式:
private enum Suit { CLUBS, HEARTS, SPADES, DIAMONDS }
由于枚举类也是一个类,因此所有适用于其它类的格式规则也适用于枚举类。
不要使用组合声明,比如int a, b;。
不要在一个代码块的开头把局部变量一次性都声明了(这是c语言的做法),而是在第一次需要使用它时才声明。 局部变量在声明时最好就进行初始化,或者声明后尽快进行初始化。
数组初始化可以写成块状结构,比如,下面的写法都是OK的:
new int[] {
0, 1, 2, 3
}
new int[] {
0,
1,
2,
3
}
new int[] {
0, 1,
2, 3
}
new int[]
{0, 1, 2, 3}
中括号是类型的一部分:String[] args, 而非 String args[]。
术语说明:switch块的大括号内是一个或多个语句组。 每个语句组包含一个或多个switch标签(case FOO:或default:),后面跟着一条或多条语句。
与其它块状结构一致,switch块中的内容缩进为2个空格。每个switch标签后新起一行,再缩进2个空格,写下一条或多条语句。
在一个switch块内,每个语句组要么通过break, continue, return或抛出异常来终止,要么通过一条注释来说明程序将继续执行到下一个语句组, 任何能表达这个意思的注释都是OK的(典型的是用// fall through)。这个特殊的注释并不需要在最后一个语句组(一般是default)中出现。
示例:
switch (input) {
case 1:
case 2:
prepareOneOrTwo(); // fall throughcase 3:
handleOneTwoOrThree();
break;
default:
handleLargeNumber(input);
}
每个switch语句都包含一个default语句组,即使它什么代码也不包含。
注解紧跟在文档块后面,应用于类、方法和构造函数,一个注解独占一行。这些换行不属于自动换行,因此缩进级别不变。
例如:
@Override
@Nullablepublic
String getNameIfPresent() { ... }
例外:单个的注解可以和签名的第一行出现在同一行。 例如:
@Override public int hashCode() { … }
应用于字段的注解紧随文档块出现,应用于字段的多个注解允许与字段出现在同一行。 例如:
@Partial @Mock DataLoader loader;
参数和局部变量注解没有特定规则。
块注释与其周围的代码在同一缩进级别。它们可以是/*…*/风格,也可以是// …风格。对于多行的/*…*/注释,后续行必须从开始, 并且与前一行的对齐。
/*
* This is // And so /* Or you can
* okay. // is this. * even do this. */
*/
注释不要封闭在由星号或其它字符绘制的框架里。
Tip: 在写多行注释时,如果你希望在必要时能重新换行(即注释像段落风格一样),那么使用/…/。
如果类和成员存在多个限制符,则按Java语言规范中推荐的顺序出现。 public protected private abstract static final transient Volatile synchronized native strictfp
包名全部小写,连续的单词只是简单地连接起来,不使用下划线。 采用反域名命名规则,全部使用小写字母。一级包名为com,二级包名为xx(可以是公司或则个人的随便),三级包名根据应用进行命名,四级包名为模块名或层级名。
例如:com.zhiyicx.thinksns.ui
类名都以大驼峰(UpperCamelCase)风格编写。
类名通常是名词或名词短语,接口名称有时可能是形容词或形容词短语。现在还没有特定的规则或行之有效的约定来命名注解类型。
名词,采用大驼峰命名法,尽量避免缩写,除非该缩写是众所周知的, 比如HTML,URL,如果类名称中包含单词缩写,则单词缩写的每个字母均应大写。
暂时无法在飞书文档外展示此内容
注意: 如果项目采用MVP,所有Model、View、Presenter的接口都以I为前缀,不加后缀。
方法名都以 小驼峰(LowerCamelCase) 风格编写。
方法名通常是动词或动词短语。
暂时无法在飞书文档外展示此内容
下划线可能出现在JUnit测试方法名称中用以分隔名称的逻辑组件。一个典型的模式是:test_,例如testPop_emptyStack。
并不存在唯一正确的方式来命名测试方法。
常量名命名模式为CONSTANT_CASE,全部字母大写,用下划线分隔单词。这些名字通常是名词或名词短语.
// Constantsstatic final int NUMBER = 5;
static final ImmutableListNAMES = ImmutableList.of("Ed", "Ann");
static final Joiner COMMA_JOINER = Joiner.on(','); // because Joiner is immutable
static final SomeMutableType[] EMPTY_ARRAY = {};
enum SomeEnum { ENUM_CONSTANT }
// Not constantsstatic String nonFinal = "non-final";
final String nonStatic = "non-static";
static final SetmutableCollection = new HashSet();
static final ImmutableSetmutableElements = ImmutableSet.of(mutable);
static final Logger logger = Logger.getLogger(MyClass.getName());
static final String[] nonEmptyArray = {"these", "can", "change"};
非常量字段名以LowerCamelCase风格的基础上改造为如下风格:
基本结构为scopeVariableNameType,
scope:范围
非公有,非静态字段命名以m开头。
公有非静态字段命名以p开头。
静态字段命名以s开头。
公有静态字段(全局变量)命名以g开头。
public static final 字段(常量) 全部大写,并用下划线连起来。
例子:
public class MyClass {
public static final int SOME_CONSTANT = 42;
public int pField;
private static MyClass sSingleton;
int mPackagePrivate;
private int mPrivate;
protected int mProtected;
public static int gField;
}
Note: 使用1字符前缀来表示作用范围,1个字符的前缀必须小写,前缀后面是由表意性强的一个单词或多个单词组成的名字,而且每个单词的首写字母大写,其它字母小写,这样保证了对变量名能够进行正确的断句。
Type:类型
考虑到Android中使用很多UI控件,为避免控件和普通成员变量混淆以及更好达意,所有用来表示控件的成员变量统一加上控件缩写作为后缀(文末附有缩写表)。
对于普通变量一般不添加类型后缀,如果统一添加类型后缀,请参考文末的缩写表。
用统一的量词通过在结尾处放置一个量词,就可创建更加统一的变量,它们更容易理解,也更容易搜索。
Note: 如果项目中使用ButterKnife,则不添加m前缀,以LowerCamelCase风格命名。 例如,请使用 mCustomerStrFirst 和 mCustomerStrLast,而不要使用mFirstCustomerStr和mLastCustomerStr。
说明:
集合添加如下后缀:List、Map、Set
数组添加如下后缀:Arr
Note: 所有的VO(值对象)统一采用标准的lowerCamelCase风格编写,所有的DTO(数据传输对象)就按照接口文档中定义的字段名编写。
参数名以LowerCamelCase风格编写
局部变量名以LowerCamelCase风格编写,比起其它类型的名称,局部变量名可以有更为宽松的缩写。
虽然缩写更宽松,但还是要避免用单字符进行命名,除了临时变量和循环变量。
即使局部变量是final和不可改变的,也不应该把它视为常量,自然也不能用常量的规则去命名它。
临时变量
临时变量通常被取名为i,j,k,m和n,它们一般用于整型;c,d,e,它们一般用于字符型。 如: for (int i = 0; i < len ; i++),并且它和第一个单词间没有空格。
类型变量可用以下两种风格之一进行命名:
单个的大写字母,后面可以跟一个数字(如:E, T, X, T2)。 以类命名方式(5.2.2节),后面加个大写的T(如:RequestT, FooBarT)。
全部小写,采用下划线命名法,使用名词或名词词组。
全部小写,采用下划线命名法,加前缀区分
命名模式:可加后缀 _small 表示小图, _big表示大图,逻辑名称可由多个单词加下划线组成,采用以下规则: 用途_模块名_逻辑名称 用途_模块名_颜色 用途_逻辑名称 用途_颜色 说明:用途也指控件类型(具体见UI控件缩写表) 例如:
btn_main_home.png 按键 divider_maket_white.png 分割线 ic_edit.png 图标 bg_main.png 背景 btn_red.png 红色按键 btn_red_big.png 红色大按键 ic_head_small.png 小头像 bg_input.png 输入框背景 divider_white.png 白色分割线 //如果有多种形态如按钮等除外如 btn_xx.xml(selector)
暂时无法在飞书文档外展示此内容
全部小写,采用下划线命名法,加前缀区分。
具体动画采用以下规则: 模块名_逻辑名称 逻辑名称
refresh_progress.xml market_cart_add.xml market_cart_remove.xml普通的tween动画采用如下表格中的命名方式 // 前面为动画的类型,后面为方向
暂时无法在飞书文档外展示此内容
暂时无法在飞书文档外展示此内容
命名模式为:view缩写_view的逻辑名称 缩写做后缀,如:tv_username(展示用户名的TextView)
如果它确实是不需要在catch块中做任何响应,需要做注释加以说明(如下面的例子)。
try {
int i = Integer.parseInt(response);
return handleNumericResponse();
} catch (NumberFormatException ok) {
// it's not numeric; that's fine, just continue
}
return handleTextResponse(response);
例外:当确保所测试的方法会抛出一个期望中的异常时,就没有必要加注释。
try {
emptyStack.pop();
fail();
} catch (NoSuchElementException expected) {
}
Tip: 尽量避免使用finalize。如果你非要使用它,请先仔细阅读和理解Effective Java 第7条款:”Avoid Finalizers”。
Javadoc块的基本格式如下所示:
/*** Multiple lines of Javadoc text are written here,* wrapped normally...*/\
public int method(String p1) { ... }
或者是以下单行形式:
/** An especially short bit of Javadoc. */
当整个Javadoc块能容纳于一行时(且没有Javadoc标记@XXX),可以使用单行形式。
空行(即,只包含最左侧星号的行)会出现在段落之间和Javadoc标记(@XXX)之前(如果有的话)。
除了第一个段落,每个段落第一个单词前都有标签
,并且它和第一个单词间没有空格。
标准的Javadoc标记按以下顺序出现:@param, @return, @throws, @deprecated, 前面这4种标记如果出现,描述都不能为空。 当描述无法在一行中容纳,连续行需要至少再缩进4个空格。
每个类或成员的Javadoc以一个简短的摘要片段开始。这个片段是非常重要的,在某些情况下,它是唯一出现的文本,比如在类和方法索引中。
这只是一个小片段,可以是一个名词短语或动词短语,但不是一个完整的句子。它不会以 A {@code Foo} is a…或This method returns…开头,它也不会是一个完整的祈使句,如 Save the record…。然而,由于开头大写及被加了标点,它看起来就像是个完整的句子。
- Tip: 一个常见的错误是把简单的Javadoc写成 /** @return the customer ID */,这是不正确的。它应该写成/*Returns the customer ID. */。
至少在每个public类及它的每个public和protected成员处使用Javadoc,以下是一些例外:
对于简单明显的方法如getFoo,Javadoc是可选的(即,是可以不写的)。这种情况下除了写”Returns the foo”,确实也没有什么值得写了。
单元测试类中的测试方法可能是不言自明的最常见例子了,我们通常可以从这些方法的描述性命名中知道它是干什么的,因此不需要额外的文档说明。
Tip: 如果有一些相关信息是需要读者了解的,那么以上的例外不应作为忽视这些信息的理由。例如,对于方法名
getCanonicalName, 就不应该忽视文档说明,因为读者很可能不知道词语canonical name指的是什么。
如果一个方法重载了超类中的方法,那么Javadoc并非必需的。
对于包外不可见的类和方法,如有需要,也是要使用Javadoc的。如果一个注释是用来定义一个类,方法,字段的整体目的或行为,那么这个注释应该写成Javadoc,这样更统一更友好。
1.【强制】代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
反例:
_name/__name/$Object/name_/name$$/Object$$
2.【强制】代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。
说明: 正确的英文拼写和语法可以让阅读者易于理解,避免歧义。注意,即使纯拼音命名方式也要避免采用。 反例: DaZhePromotion[打折]/getPingfenByName()[评分]/int 某变量 = 3 正例: alibaba/ taobao/ youku/ hangzhou等国际通用的名称,可视同英文。
3.【强制】类名使用UpperCamelCase风格,必须遵从驼峰形式,但以下情形例外:
(领域模型的相关命名)DO/ BO / DTO/ VO等。
正例: MarcoPolo/ UserDO/ XmlService/ TcpUdpDeal/ TaPromotion 反例: macroPolo/ UserDo/ XMLService/ TCPUDPDeal/ TAPromotion
4.【强制】
方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase风格,必须遵从驼峰形式。
正例: localValue/ getHttpMessage()/ inputUserId
5.【强制】
常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
正例: MAX_STOCK_COUNT 反例: MAX_COUNT
6.【强制】
抽象类命名使用Abstract或Base开头;异常类命名使用Exception结尾;测试类命名以它要测试的类的名称开始,以Test结尾。
7.【强制】中括号是数组类型的一部分,数组定义如下:String[]args;
反例: 请勿使用String args[]的方式来定义。
8.【强制】POJO类中布尔类型的变量,都不要加is,否则部分框架解析会引起序列化错误。
反例: 定义为基本数据类型boolean isSuccess;的属性,它的方法也是isSuccess(),RPC框架在反向解析的时候,“以为”对应的属性名称是success,导致属性获取不到,进而抛出异常。
9.【强制】包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。
正例: 应用工具类包名为com.alibaba.open.util、类名为MessageUtils(此规则参考spring的框架结构)
10.【强制】杜绝完全不规范的缩写,避免望文不知义。
反例: AbstractClass“缩写”命名成AbsClass;condition“缩写”命名成condi,此类随意缩写严重降低了代码的可阅读性。
11.【推荐】如果使用到了设计模式,建议在类名中体现出具体模式。
说明: 将设计模式体现在名字中,有利于阅读者快速理解架构设计思想。 正例: public class OrderFactory; public class LoginProxy; public class ResourceObserver;
12.【强制】内部的实现类用Impl的后缀与接口区别。
正例: CacheServiceImpl实现CacheService接口。
13.【参考】枚举类名建议带上Enum后缀,枚举成员名称需要全大写,单词间用下划线隔开。
说明: 枚举其实就是特殊的常量类,且构造方法被默认强制是私有。 正例: 枚举名字:DealStatusEnum 成员名称:SUCCESS/ UNKOWN_REASON。
14.【参考】
1.【强制】不允许出现任何魔法值(即未经定义的常量)直接出现在代码中。
反例:
String key = "Id#taobao_"+tradeId;
cache.put(key, value);
2.【强制】long或者Long初始赋值时,必须使用大写的L,不能是小写的l,小写容易跟数字1混淆,造成误解。
说明:
Long a = 2l;
写的是数字的21,还是Long型的2?
3.【推荐】不要使用一个常量类维护所有常量,应该按常量功能进行归类,分开维护。如:缓存相关的常量放在类:CacheConsts下;系统配置相关的常量放在类:ConfigConsts下。
说明: 大而全的常量类,非得使用查找功能才能定位到修改的常量,不利于理解和维护。
4.【推荐】如果变量值仅在一个范围内变化用Enum类。如果还带有名称之外的延伸属性,必须使用Enum类,下面正例中的数字就是延伸信息,表示星期几。
正例:
public Enum{ MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6), SUNDAY(7);
}
1.【强制】大括号的使用约定。如果是大括号内为空,则简洁地写成{}即可,不需要换行;如果是非空代码块则:
2.【强制】左括号和后一个字符之间不出现空格;同样,右括号和前一个字符之间也不出现空格。
3.【强制】任何运算符左右必须加一个空格。
说明: 运算符包括赋值运算符=、逻辑运算符&&、加减乘除符号、三目运行符等。
4.【强制】针对这个规范,其实在开发工具中统一配置,不过扩展来看是否可以弄一个统一的配置文件进行开发工具导入,这样不需要每次都要配置,或者出现开发人员自行更改的情况。 单行字符数限制不超过120个,超出需要换行,换行时遵循如下原则:
正例:
StringBuffer sb = new StringBuffer();
//超过120个字符的情况下,换行缩进4个空格,并且方法前的点符号一起换行
sb.append("zi").append("xin")...
.append("huang")...
.append("huang")...
.append("huang");
反例:
StringBuffer sb = new StringBuffer();
//超过120个字符的情况下,不要在括号前换行
sb.append("zi").append("xin")...append ("huang");
//参数很多的方法调用可能超过120个字符,不要在逗号前换行
method(args1, args2, args3, ... , argsX);
5.【强制】方法参数在定义和传入时,多个参数逗号后边必须加空格。
正例: 下例中实参的"a",后边必须要有一个空格。method("a", "b", "c");
6.【强制】IDE的textfileencoding设置为UTF-8;IDE中文件的换行符使用Unix格式,不要使用windows格式。
7.【推荐】
没有必要增加若干空格来使某一行的字符与上一行的相应字符对齐。
正例:
int a = 3;
long b = 4L;
float c = 5F;
StringBuffer sb = new StringBuffer();
说明: 增加sb这个变量,如果需要对齐,则给a、b、c都要增加几个空格,在变量比较多的情况下,是一种累赘的事情。
8.【强制】
方法体内的执行语句组、变量的定义语句组、不同的业务逻辑之间或者不同的语义之间插入一个空行。相同业务逻辑和语义之间不需要插入空行。
说明: 没有必要插入多行空格进行隔开。
1.【强制】避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。
2.【强制】所有的覆写方法,必须加@Override注解。
反例: getObject()与get0bject()的问题。一个是字母的O,一个是数字的0,加@Override可以准确判断是否覆盖成功。另外,如果在抽象类中对方法签名进行修改,其实现类会马上编译报错。
3.【强制】相同参数类型,相同业务含义,才可以使用Java的可变参数,避免使用Object。
说明: 可变参数必须放置在参数列表的最后。(提倡同学们尽量不用可变参数编程) 正例: public User getUsers(String type, Integer... ids)
4.【强制】对外暴露的接口签名,原则上不允许修改方法签名,避免对接口调用方产生影响。接口过时必须加@Deprecated注解,并清晰地说明采用的新接口或者新服务是什么。
说明: ++这里需要注意,所有项目组开发或调用任何接口(其他三方API不算),不允许直接在现有的接口上进行修改,一定要按照这个规则进行,有个接口替换的过程,或者理解为接口并行,给调用方一个修改的时间。同样对于调用方而言要遵守下面的第五点要求。++
5.【强制】不能使用过时的类或方法。
说明: URLDecoder中的方法decode(String encodeStr)这个方法已经过时,应该使用双参数decode(String source, String encode)。接口提供方既然明确是过时接口,那么有义务同时提供新的接口;作为调用方来说,有义务去考证过时方法的新实现是什么。
6.【强制】Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals。
正例: "test".equals(object); 反例: object.equals("test"); 说明: 推荐使用java.util.Objects#equals(JDK7引入的工具类)
7.【强制】所有的相同类型的包装类对象之间值的比较,全部使用equals方法比较。
说明: 对于Integer var=?在-128至127之间的赋值,Integer对象是在IntegerCache.cache产生,会复用已有对象,这个区间内的Integer值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用equals方法进行判断。
8.【强制】关于基本数据类型与包装数据类型的使用标准如下:
说明: POJO类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何NPE问题,或者入库检查,都由使用者来保证。 正例: 数据库的查询结果可能是null,因为自动拆箱,用基本数据类型接收有NPE风险。 反例: 比如显示成交总额涨跌情况,即正负x%,x为基本数据类型,调用的RPC服务,调用不成功时,返回的是默认值,页面显示:0%,这是不合理的,应该显示成中划线-。所以包装数据类型的null值,能够表示额外的信息,如:远程调用失败,异常退出。
9.【强制】定义DO/DTO/VO等POJO类时,不要设定任何属性默认值。
反例: POJO类的gmtCreate默认值为new Date();但是这个属性在数据提取时并没有置入具体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间。
10.【强制】序列化类新增属性时,请不要修改serialVersionUID字段,避免反序列失败;如果完全不兼容升级,避免反序列化混乱,那么请修改serialVersionUID值。
说明: 注意serialVersionUID不一致会抛出序列化运行时异常。
11.【强制】构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在init方法中。
12.【强制】POJO类必须写toString方法。使用IDE的中工具:source> generatetoString时,如果继承了另一个POJO类,注意在前面加一下super.toString。
说明: 在方法执行抛出异常时,可以直接调用POJO的toString()方法打印其属性值,便于排查问题。
13.【推荐】使用索引访问用String的split方法得到的数组时,需做最后一个分隔符后有无内容的检查,否则会有抛IndexOutOfBoundsException的风险。
说明:
String str = "a,b,c,,";
Stringary = str.split(",");
//预期大于3,结果是3
System.out.println(ary.length);
14.【推荐】当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起,便于阅读。
说明: 针对之前的系统代码,有很多代码层次很乱,要找一个方法很难,所以不仅仅是重构的这种要排在一起,其他的方法要按照一定的意义有顺序排列。结合下面的方法顺序。
15.【强制】类内方法定义顺序依次是:公有方法或保护方法> 私有方法> getter/setter方法。
说明: 公有方法是类的调用者和维护者最关心的方法,首屏展示最好;保护方法虽然只是子类关心,也可能是“模板设计模式”下的核心方法;而私有方法外部一般不需要特别关心,是一个黑盒实现;因为方法信息价值较低,所有Service和DAO的getter/setter方法放在类体最后。
16.【推荐】setter方法中,参数名称与类成员变量名称一致,this.成员名=参数名。在getter/setter方法中,尽量不要增加业务逻辑,增加排查问题的难度。
反例:
public Integer getData(){ if(true) { return d
ata + 100;
} else {return data - 100;
}
}
注: 这个可能大部分人没有使用过,但是我之前在写直销回单打印的时候用过这种方式,当时是考虑调用的时候直接放入一个值,其他对应值根据一定的逻辑处理直接SET,以后这种方式要避免,不要在SETTER里面加上其他的逻辑处理。
17.【推荐】循环体内,字符串的连接方式,使用StringBuilder的append方法进行扩展。
反例:
String str = "start";
for(int i=0; i<100; i++){ str = str + "hello";
}
说明: 反编译出的字节码文件显示每次循环都会new出一个StringBuilder对象,然后进行append操作,最后通过toString方法返回String对象,造成内存资源浪费。
18.【推荐】final可提高程序响应效率,声明成final的情况:
19.【推荐】慎用Object的clone方法来拷贝对象。
说明: 对象的clone方法默认是浅拷贝,若想实现深拷贝需要重写clone方法实现属性对象的拷贝。
20.【推荐】
类成员与方法访问控制从严:
说明: 任何类、方法、参数、变量,严控访问范围。过宽泛的访问范围,不利于模块解耦。思考:如果是一个private的方法,想删除就删除,可是一个public的Service方法,或者一个public的成员变量,删除一下,不得手心冒点汗吗?变量像自己的小孩,尽量在自己的视线内,变量作用域太大,如果无限制的到处跑,那么你会担心的。
1.【强制】关于hashCode和equals的处理,遵循如下规则:
正例: String重写了hashCode和equals方法,所以我们可以非常愉快地使用String对象作为key来使用。
2.【强制】
ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException异常:java.util.RandomAccessSubListcannotbecasttojava.util.ArrayList;
说明: subList返回的是ArrayList的内部类SubList,并不是ArrayList,而是ArrayList的一个视图,对于SubList子列表的所有操作最终会反映到原列表上。 注: 这个可能不会用到,注意一下。
3.【强制】
在subList场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除均产生ConcurrentModificationException异常。
4.【强制】
使用集合转数组的方法,必须使用集合的toArray(T[] array),传入的是类型完全一样的数组,大小就是list.size()。
反例: 直接使用toArray无参方法存在问题,此方法返回值只能是Object[]类,若强转其它类型数组将出现ClassCastException错误。 正例:
List<Stringlist = new ArrayList<String>(2);
list.add("guan");
list.add("bao");
String[] array = new String[list.size()];
array = list.toArray(array);
说明: 使用toArray带参方法,入参分配的数组空间不够大时,toArray方法内部将重新分配内存空间,并返回新数组地址;如果数组元素大于实际所需,下标为[list.size()]的数组元素将被置为null,其它数组元素保持原值,因此最好将方法入参数组大小定义与集合元素个数一致。
5.【强制】使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear方法会抛出UnsupportedOperationException异常。
说明: asList的返回对象是一个Arrays内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。 String[] str = new String[] { "a", "b" }; List list = Arrays.asList(str); 第一种情况: list.add("c"); 运行时异常。 第二种情况: str[0]= "gujin"; 那么list.get(0)也会随之修改。
6.【强制】泛型通配符<? extends T>来接收返回的数据,此写法的泛型集合不能使用add方法。
说明: 苹果装箱后返回一个<? extends Fruits>对象,此对象就不能往里加任何水果,包括苹果。
7.【强制】不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对
Iterator对象加锁。
反例:
List<String> a = new ArrayList<String>();
a.add("1");
a.add("2");
for (String temp : a) { if("1".equals(temp)){ a.remove(temp);
}
}
说明: 以上代码的执行结果肯定会出乎大家的意料,那么试一下把“1”换成“2”,会是同样的结果吗? 正例:
Iterator<Stringit = a.iterator();
while(it.hasNext()){ String temp = it.next(); if(删除元素的条件){
it.remove();
}
}
8.【推荐】使用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值组合集合。
9.【参考】合理利用好集合的有序性(sort)和稳定性(order),避免集合的无序性(unsort)和不稳定性(unorder)带来的负面影响。
说明: 稳定性指集合每次遍历的元素次序是一定的。有序性是指遍历的结果是按某种比较规则依次排列的。如:ArrayList是order/unsort;HashMap是unorder/unsort;TreeSet是order/sort。
10.【参考】
利用Set元素唯一的特性,可以快速对一个集合进行去重操作,避免使用List的contains方法进行遍历、对比、去重操作。
1.【强制】在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case
为止;在一个switch块内,都必须包含一个default语句并且放在最后,即使它什么代码也没有。
2.【强制】在if/else/for/while/do语句中必须使用大括号,即使只有一行代码,避免使用下面的形式:if(condition)statements;
注: 这是一个统一规范的问题,虽然说符合JAVA语法但是控制语句一定要有成对的“{}”。
3.【强制】除常用方法(如getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。
说明: 很多if语句内的逻辑相当复杂,阅读者需要分析条件表达式的最终结果,才能明确什么样的条件执行什么样的语句,那么,如果阅读者分析逻辑表达式错误呢? 正例: //伪代码如下
boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
if (existed) {
...
}
反例:
if ((file.open(fileName, "w") != null) && (...) || (...)) {
...
}
注: 现有的很多判断逻辑都是一大堆,即使在开发环境下使用DEBUG也不宜读,这种方式会有一个落地值,方便定位问题。
4.【强制】循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、获取数据库连接,进行不必要的try-catch操作(这个try-catch是否可以移至循环体外)。
注: 所有人在开发时一定要考虑这个,或者在小组开发的过程中要统一定位try-catch的位置,不要随意抓捕,任何异常的信息都是有用的。
5.【强制】接口入参保护,这种场景常见的是用于做批量操作的接口。
注: 接口入参保护指的是要对参数进行校验,大量批量操作非常消耗时间,所以一定要在接口这里做一个数量级和参数准确性的把控。具体的场景可以参照下面两个规则。
6.【参考】方法中需要进行参数校验的场景:
7.【参考】方法中不需要参数校验的场景:
1.【强制】类、类属性、类方法的注释必须使用Javadoc规范,使用
/**
内容
*/
格式,不得使用
//xxx
方式。
说明: 在IDE编辑窗口中,Javadoc方式会提示相关注释,生成Javadoc可以正确输出相应注释;在IDE中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高阅读效率。
2.【强制】所有的抽象方法(包括接口中的方法)必须要用Javadoc注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。
说明: 对子类的实现要求,或者调用注意事项,请一并说明。
3.【强制】所有的类都必须添加创建者信息。
4.【强制】方法内部单行注释,在被注释语句上方另起一行。
使用//注释。方法内部多行注释
使用/* */注释,注意与代码对齐。
5.【强制】所有的枚举类型字段必须要有注释,说明每个数据项的用途。
6.【强制】与其“半吊子”英文来注释,不如用中文注释把问题说清楚。专有名词与关键字保持英文原文即可。
反例: “TCP连接超时”解释成“传输控制协议连接超时”,理解反而费脑筋。
7.【强制】代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑等的修改。
说明: 代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后,就失去了导航的意义。
8.【强制】注释掉的代码尽量要配合说明,而不是简单的注释掉。
说明:
9.【强制】
10.【强制】好的命名、代码结构是自解释的,注释力求精简准确、表达到位。避免出现注释的一个极端:过多过滥的注释,代码的逻辑一旦修改,修改注释是相当大的负担。
反例: // put elephant into fridge put(elephant, fridge); 方法名put,加上两个有意义的变量名elephant和fridge,已经说明了这是在干什么,语义清晰的代码不需要额外的注释。
11.【强制】特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描,经常清理此类标记。线上故障有时候就是来源于这些标记处的代码。
1.【强制】不要捕获Java类库中定义的继承自RuntimeException的运行时异常类,如:IndexOutOfBoundsException/ NullPointerException,这类异常由程序员预检查来规避,保证程序健壮性。
正例: if(obj != null) {...} 反例: try { obj.method() } catch(NullPointerException e){ ... }
2.【强制】对大段代码进行try-catch,这是不负责任的表现。catch时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的catch尽可能进行区分异常类型,再做对应的异常处理。
注: 这个在现有系统中是存在的,为了方便处理,后面每个业务系统开发小组自行决定一个统一(公用)的异常抓捕位置,这样既实现了这个规则也在一定情况下让代码变得整洁有序。
3.【强制】捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。
注: 这个在之前有提到过,任何异常信息都是有用的,不要随意进行一个抓捕,要配合上面的规则统一化管理。 @see:控制语句#第五点
4.【强制】有try块放到了事务代码中,catch异常后,如果需要回滚事务,一定要注意手动回滚事务。
5.【强制】finally块必须对资源对象、流对象进行关闭,有异常也要做try-catch。
说明: 如果JDK7,可以使用try-with-resources方式。
6.【强制】
不能在finally块中使用return,finally块中的return返回后方法结束执行,不会再执行try块中的return语句。
7.【强制】
捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。
说明: 如果预期对方抛的是绣球,实际接到的是铅球,就会产生意外情况。 注: 这个在TA的设计中有考虑,所有开发人员要考虑异常抓捕的范围,由小到大,最后有一个Exception最大的。
8.【强制】
方法的返回值可以为null,不强制返回空集合,或者空对象等,必须添加注释充分说明什么情况下会返回null值。调用方需要进行null判断防止NPE问题。
说明: 本规约明确防止NPE是调用者的责任。即使被调用方法返回空集合或者空对象,对调用者来说,也并非高枕无忧,必须考虑到远程调用失败,运行时异常等场景返回null的情况。 注: 这个在以后的开发过程中会经常遇到,之前看QF实现代码的时候就发现这个问题了,应该是刘泽坤那面写的,之前没有一个肯定的答复,现在参照这个规则实现,在注释上进行充分说明。
9.【推荐】
防止NPE,是程序员的基本修养,注意NPE产生的场景:
反例:
public int f(){ return Integer 对象
};
如果为null,自动解箱抛NPE。
注: 这个要结合上一个规则来看,我在这做一个整理(可以讨论一下),接口(方法)提供方无需考虑是否返回的是NULL值,接口(方法)调用方必须要考虑接口(方法)返回值为空的情况(这个在提供方的注释或API上要有详细的说明,在什么情况下会有NULL返回,餐勺上面的规则)。
10.【强制】
在代码中使用“抛异常”还是“返回错误码”,对于公司外的http/api开放接口必须使用“错误码”;而应用内部推荐异常抛出;跨应用间RPC调用优先考虑
使用Result方式,封装isSuccess、“错误码”、“错误简短信息”。
说明: 关于RPC方法返回方式使用Result方式的理由:
注: 错误代码的规范也要统一起来并形成错误代码表,作为日志跟踪、问题排查、系统交互等后期工作的前置条件,之后这个丰富起来才能说明系统的健壮性。 系统的健壮性不是实现了多少功能,也不是多么的牛,而是你可控的范围有多少,错误代码表就是系统健壮性的一个量级或文档化的实现。
11.【强制】定义时区分unchecked/ checked异常,避免直接使用RuntimeException抛出,更不允许抛出Exception或者Throwable,应使用有业务含义的自定义异常。推荐业界已定义过的
自定义异常,如:DAOException/ ServiceException等。
12.【参考】避免出现重复的代码(Don’t Repeat Yourself),即DRY原则。
说明: 随意复制和粘贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副本,容易遗漏。必要时抽取共性方法,或者抽象公共类,甚至是共用模块。 正例: 一个类中有多个public方法,都需要进行数行相同的参数校验操作,这个时候请抽取: private boolean checkParam(DTO dto)
注: 这个作为参考保留,是以后的一个方向,因为现有业务逻辑的原因,不能也没有时间进行细化,所以业务层面的代码还是要有冗余的,但是作为工具层面的代码一定要考虑这个DRY原则。
1.【强制】
表达是与否概念的字段,必须使用is_xxx的方式命名,数据类型是unsigned tinyint(1表示是,0表示否),此规则同样适用于odps建表。
说明: 任何字段如果为非负数,必须是unsigned。
2.【强制】表名、字段名必须使用小写字母或数字;禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
正例: getter_admin,task_config,level3_name 反例: GetterAdmin,taskConfig,level_3_name
3.【强制】表名不使用复数名词。
说明: 表名应该仅仅表示表里面的实体内容,不应该表示实体数量,对应于DO类名也是单数形式,符合表达习惯。
4.【强制】禁用保留字,如desc、range、match、delayed等,请参考MySQL官方保留字。
5.【强制】唯一索引名为uk_字段名;普通索引名则为idx_字段名。
说明: uk_ 即uniquekey;idx_ 即index的简称。
6.【强制】小数类型为decimal,禁止使用float和double。
说明: float和double在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不正确的结果。如果存储的数据范围超过decimal的范围,建议将数据拆成整数和小数分开存储。
7.【强制】如果存储的字符串长度几乎相等,使用char定长字符串类型。
8.【强制】varchar是可变长字符串,不预先分配存储空间,长度不要超过5000,如果存储长度大于此值,定义字段类型为text,独立出来一张表,用主键来对应,避免影响其它字段索引效率。
9.【强制】表必备三字段:id, gmt_create, gmt_modified。
说明: 其中id必为主键,类型为unsigned bigint、单表时自增、步长为1。gmt_ create, gmt_modified的类型均为date_time类型。
注: 这个规则需要详细讨论,就参照现有sofa那面的内容
10.【强制】表的命名最好是加上“业务名称_表的作用”。
正例: tiger_task/ tiger_reader/ mpp_config
11.【强制】库名与应用名称尽量一致。
12.【强制】如果修改字段含义或对字段表示的状态追加时,需要及时更新字段注释。
13.【推荐】字段允许适当冗余,以提高性能,但是必须考虑数据同步的情况。冗余字段应遵循:
正例: 商品类目名称使用频率高,字段长度短,名称基本一成不变,可在相关联的表中冗余存储类目名称,避免关联查询。
14.【推荐】单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表。
说明: 如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
15.【参考】合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速度。
正例: 人的年龄用unsigned tinyint(表示范围0-255,人的寿命不会超过255岁 );海龟就必须是smallint,但如果是太阳的年龄,就必须是int;如果是所有恒星的年龄都加起来,那么就必须使用bigint。
1.【强制】业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引。
说明: 不要以为唯一索引影响了insert速度,这个速度损耗可以忽略,但提高查找速度是明显的;另外,即使在应用层做了非常完善的校验和控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。
2.【强制】在varchar字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度。
说明: 索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为20的索引,区分度会高达90%以上,可以使用count(distinct left(列名, 索引长度))/count(*)的区分度来确定。
3.【强制】页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。
说明: 索引文件具有B-Tree的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。
注: ++这个规则建议不仅作为开发及数据库设计规则,也要作为需求产品设计规则,而且现有需求即使只支持右模糊也无任何影响。++
1.【强制】不要使用count(列名)或count(常量)来替代count(),count()就是SQL92定义的标准统计行数的语法,跟数据库无关,跟NULL和非NULL无关。
说明: count(*)会统计值为NULL的行,而count(列名)不会统计此列为NULL值的行。
2.【强制】count(distinct col)计算该列除NULL之外的不重复数量。注意count(distinct col1, col2)如果其中一列全为NULL,那么即使另一列有不同的值,也返回为0。
3.【强制】当某一列的值全是NULL时,count(col)的返回结果为0,但sum(col)的返回结果为NULL,因此使用sum()时需注意NPE问题。
正例: 可以使用如下方式来避免sum的NPE问题:
SELECT IF(ISNULL(SUM(g)),0,SUM(g)) FROM table;
4.【强制】使用ISNULL()来判断是否为NULL值。注意:NULL与任何值的直接比较都为NULL。
说明:
5.【强制】在代码中写分页查询逻辑时,若count为0应直接返回,避免执行后面的分页语句。
6.【强制】不得使用外键与级联,一切外键概念必须在应用层解决。
说明: (概念解释)学生表中的student_id是主键,那么成绩表中的student_id则为外键。如果更新学生表中的student_id,同时触发成绩表中的student_id更新,则为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。
7.【强制】
禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。
注: 这个后面做详细的考量,如果时间要求,那也没有办法。
8.【强制】数据订正时,删除和修改记录时,要先select,避免出现误删除,确认无误才能执行更新语句。
9.【强制】
in操作能避免则避免,若实在避免不了,需要仔细评估in后边的集合元素数量,控制在1000个之内。
10.【强制】
如果有全球化需要,所有的字符存储与表示,均以utf-8编码,那么字符计数方法注意:
说明:
SELECT LENGTH("轻松工作");返回为12SELECT CHARACTER_LENGTH("轻松工作");返回为4
如果要使用表情,那么使用utfmb4来进行存储,注意它与utf-8编码的区别。
注: 以后如无特殊要求,统一使用utf-8编码。
11.【强制】TRUNCATE TABLE比DELETE速度快,且使用的系统和事务日志资源少,但TRUNCATE无事务且不触发trigger,有可能造成事故,故不建议在开发代码中使用此语句。
说明: TRUNCATE TABLE在功能上与不带WHERE子句的DELETE语句相同。
1.【强制】在表查询中,一律不要使用* 作为查询的字段列表,需要哪些字段必须明确写明。
说明:
2.【强制】POJO类的boolean属性不能加is,而数据库字段必须加is_,要求在resultMap中进行字段与属性之间的映射。
说明: 参见定义POJO类以及数据库字段定义规定,在sql.xml增加映射,是必须的。
3.【强制】
不要用resultClass当返回参数,即使所有类属性名与数据库字段一一对应,也需要定义;反过来,每一个表也必然有一个与之对应。
说明: 配置映射关系,使字段与DO类解耦,方便维护。
4.【强制】xml配置中参数注意使用:#{},#param# 不要使用${} 此种方式容易出现SQL注入。
5.【强制】iBATIS自带的queryForList(String statementName,int start,int size)不推荐使用。
说明: 其实现方式是在数据库取到statementName对应的SQL语句的所有记录,再通过subList取start,size的子集合,线上因为这个原因曾经出现过OOM。 正例: 在sqlmap.xml中引入#start#, #size# Map<String, Object> map = new HashMap<String, Object>(); map.put("start", start); map.put("size", size);
注: 这个平台那面研究一下是否现在使用的mybatis版本也是这样,后面提供封装方法的时候要注意。
6.【强制】更新数据表记录时,必须同时更新记录对应的gmt_modified字段值为当前时间。
7.【强制】不要写一个大而全的数据更新接口,传入为POJO类,不管是不是自己的目标更新字段,都进行update table set c1=value1,c2=value2,c3=value3; 这是不对的。
执行SQL时,尽量不要更新无改动的字段
8.【强制】@Transactional事务不要滥用。事务会影响数据库的QPS,另外使用事务的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。
1.【强制】定义GAV遵从以下规则:
说明:{公司/BU} 例如:alibaba/taobao/tmall/aliexpress等BU一级;子业务线可选。 正例: com.taobao.jstorm或com.alibaba.dubbo.register
正例: dubbo-client/ fast JSON-api/ jstorm-tool
2.【强制】二方库版本号命名方式:主版本号.次版本号.修订号
说明: 起始版本号必须为:1.0.0,而不是0.0.1
3.【强制】依赖于一个二方库群时,必须定义一个统一版本变量,避免版本号不一致。
说明: 依赖springframework-core,-context,-beans,它们都是同一个版本,可以定义一个变量来保存版本:${spring.version},定义依赖的时候,引用该版本。
4.【强制】禁止在子项目的pom依赖中出现相同的GroupId,相同的ArtifactId,但是不同的Version。
说明: 在本地调试时会使用各子项目指定的版本号,但是合并成一个war,只能有一个版本号出现在最后的lib目录中。曾经出现过线下调试是正确的,发布到线上出故障的先例。
5.【参考】为避免应用二方库的依赖冲突问题,二方库发布者应当遵循以下原则: