[关闭]
@ZeroGeek 2016-08-18T09:40:28.000000Z 字数 9343 阅读 698

代码风格文档

Java

命名

变量和常量

原则:保证命名后能准确表达业务。
  1. // 示例
  2. public class Person {
  3. public static final int MAX_VALUE = 1000;
  4. private static double sShareMoney;
  5. private int mAge;
  6. // ...
  7. private void cost(double money) {
  8. double localMoney = money;
  9. // ...
  10. }
  11. // ...
  12. }

类和接口

类名和接口使用类意义完整的英文描述,每个英文单词的首字母使用大写、其余字母使用小写的大小写混合法
  1. public class UploadNickNameTask {
  2. // ...
  3. }
  4. public interface OnItemClickListener {
  5. // ...
  6. }

方法

方法名使用类意义完整的英文描述:第一个单词的字母使用小写、剩余单词首字母大写其余字母小写的大小写混合法
  1. public String getType();
  2. public boolean isFinished();
  3. public void setVisible(boolean isVisible);
  4. public void show();
  5. public void addKeyListener(Listener listener);

排版

缩进

程序块要采用缩进风格编写,缩进的空格数为4个。

大括号

分界符(如大括号‘{’和‘}’)应各独占一行,同时与引用它们的语句左对齐。在函数体的开始、类和接口的定义、以及if、for、do、while、switch、case语句中的程序或者static、,synchronized等语句块中都要采用如上的缩进方式。
  1. if (a > b) {
  2. doStart();
  3. }

长语句划分

较长的语句、表达式或参数(>80字符)要分成多行书写,长表达式要在低优先级操作符处划分新行,操作符放在新行之首(sql语句字符串拼接为了可读性除外),划分出的新行要进行适当的缩进,使排版整齐,语句可读。
  1. if (logger.isDebugEnabled()){
  2. logger.debug("Session destroyed,call-id"
  3. + event.getSession().getCallId());
  4. }

单行单语句

不允许把多个短语句写在一行中,即一行只写一条语句,包括声明变量。
  1. // 不规范
  2. int ab
  3. a = b++;
  4. // 规范
  5. int a
  6. int b;
  7. a = b;
  8. b++;

加括号{}

if, for, do, while, case, switch, default 等语句自占一行,且if, for, do, while,switch等语句的执行语句无论多少都要加括号{},case 的执行语句中如果定义变量必须加括号{}。
  1. if (isShow()) {
  2. return true;
  3. }
  4. switch (type) {
  5. case 0: {
  6. a = b;
  7. b++;
  8. // ...
  9. }
  10. break;
  11. // ...
  12. }

空格

在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符之前、之后或者前后要加空格;进行非对等操作时,如果是关系密切的立即操作符(如.),后不应加空格。
  1. if (a == b) {
  2. objectA.doStart();
  3. }
  4. a *= 2;

类的定义顺序

某类 {
    类的公有属性定义
    类的保护属性定义
    类的私有属性定义
    类的公有方法定义
    类的保护方法定义
    类的私有方法定义
}

修饰词顺序

修饰词按照指定顺序书写:
public/protected/private  abstract  static  final  transient  volatile   synchronized  native 

注释

基本语句书写规范

While语句

  1. // While语句的规则如下:
  2. while (condition) {
  3. statements;
  4. }
  5. // 禁用这种do while语句
  6. do {
  7. statements;
  8. } while (condition);

Switch语句

Switch语句风格如下。需要注意的是不要忘了default,break。另外分支和分支之间以空行隔开。
  1. switch (condition) {
  2. case ABC:
  3. case DEF:
  4. statements;
  5. break;
  6. case XYZ: {
  7. statements;
  8. }
  9. break;
  10. default:
  11. statements;
  12. break;
  13. }

If-else语句

  1. if (condition1) {
  2. statements;
  3. } else if (condition2) {
  4. statements;
  5. } else {
  6. statements;
  7. }

循环语句

对于数组、list等的遍历,如果不是特殊要求(比如在循环遍历中删除某个元素,for中边循环边删除会ConcurrentModificationException异常,这种应该使用Iterator迭代器),使用for-each方式。
  1. for (Item item:items) {
  2. statements;
  3. }
  1. //禁止方式
  2. while (index < products.getCount()) {
  3. //每此都会执行一次getCount()方法,
  4. //若此方法耗时则会影响执行效率
  5. //而且可能带来同步问题,若有同步需求,请使用同步块或同步方法
  6. }
  7. for (int i = 0; i < products.getCount(); i++) {
  8. // ...
  9. }
  10. //推荐方式,将操作结构保存在临时变量里,减少方法调用次数
  11. final int count = products.getCount();
  12. while (index < count) {
  13. }
  14. // 或者
  15. for (int i = 0; i < count; i++) {
  16. // ...
  17. }

异常

不要catch异常而不做任何处理,要么不catch异常(让异常抛出去),要么catch作特殊处理,或者生成一个新的异常抛出去。

这里有两个异常需要特别注意,NullPointerException原则上是不允许catch的,OutOfMemoryError在解码图片时或者经常出现内存使用紧张的字符串拼接时可catch,其他地方尽量不要catch。

以下这种方式是不规范的:

  1. void setServerPort(String value) {
  2. try {
  3. serverPort = Integer.parseInt(value);
  4. } catch (NumberFormatException e) {
  5. //等于直接吃掉了异常
  6. }
  7. }

对所有异常做特殊处理,最起码在遇到异常的地方打印异常日志:

  1. void setServerPort(String value) {
  2. try {
  3. serverPort = Integer.parseInt(value);
  4. } catch (NumberFormatException e) {
  5. Debug.exception(TAG, e);
  6. }
  7. }

不建议catch普通的异常,比如Exception、Throwable。而应catch具体的异常类型,比如IllegalArgumentException等。

下面的做法是不推荐的:

  1. try {
  2. FileOutputStream outStream = new FileOutputStream(new File("tmp.txt"));
  3. outStream.write(bytes);
  4. } catch (Exception e) {
  5. statements;
  6. }

应该捕获特定的异常:

  1. try {
  2. FileOutputStream outStream = new FileOutputStream(new File("tmp.txt"));
  3. outStream.write(bytes);
  4. } catch (FileNotFoundException e) {
  5. statements;
  6. } catch (IOException e) {
  7. statements;
  8. }

编码规则

声明数组

数组声明的时候使用 int[] index,而不要使用 int index[]。

变量作用域

尽量使变量的作用域降到最小范围,如果不是特殊要求,不要过早创建不需要的对象。
  1. // 建议使用:
  2. String s = "Hello";
  3. // 而不是下面这样的形式:
  4. String s;
  5. // …
  6. s = "Hello";

条件运算符 "?"

如果一个包含二元运算符的表达式出现在三元运算符" ? : "的"?"之前,那么应该给表达式添上一对圆括号。  
  1. a = (x >= 0) ? x : -x

字符串String

  1. String str = "";
  2. if(TextUtil.isEmpty(str)){
  3. // ...
  4. }
  5. if("rect".equals(str)) {
  6. // ...
  7. }

基本数值类型的变量初始化

防止溢出
  1. // 不规范 结果值:
  2. float twoThirds = 2/3; // 0.0
  3. long millisInYear = 1000*3600*24*365; // 1471228928
  4. long bigNum = Integer.MAX_VALUE + 2; // -2147483647
  5. long bigNegNum = Integer.MIN_VALUE - 1; // 2147483647
  1. // 规范 期望值:
  2. float twoThirds = 2f/3; // 0.6666667
  3. long millisInYear = 1000L*3600*24*365; // 31536000000
  4. long bigNum = Integer.MAX_VALUE + 2L; // 2147483649
  5. long bigNegNum = Integer.MIN_VALUE - 1L; // -2147483649
  6. // 或者
  7. float twoThirds = (float)2/3;
  8. long millisInYear = (long)1000*3600*24*365;
  9. long bigNum = (long)Integer.MAX_VALUE + 2;
  10. long bigNegNum = (long)Integer.MIN_VALUE - 1;

游标Cursor

游标获取字符串的问题:  String result = c.getString(c.getColumnIndex(columnName); 如果数据库表名字为
columnName的字段的值是null,则result不是null而是一个字符串”null”,在做服务器开发的时候jdbc驱动是会返回空串的.

集合或者映射的非空判断

统一使用CollectionUtil工具类
  1. public class CollectionUtil {
  2. public static boolean isEmpty(Collection<?> collection){
  3. return collection == null || collection.isEmpty();
  4. }
  5. public static boolean isEmpty(Map map){
  6. return map == null || map.isEmpty();
  7. }
  8. }

重写equals() 一定要重写 hashCode()

可以使用IDE自动生成。如果不这样做,可能导致该类无法结合所有基于散列的集合一起正常运作,包括HashMap,HashSet和Hashtable。否则违反的Object.hashCode的约定:相等的对象必须具有相等的hashcode。

数组的"hashCode"和字符串

  1. // 不规范
  2. public static void main( String[] args ) {
  3. String argStr = args.toString();
  4. int argHash = args.hashCode();
  5. // 规范
  6. public static void main( String[] args ) {
  7. String argStr = Arrays.toString(args);
  8. int argHash = Arrays.hashCode(args);

BigDecimal的使用

使用BigDecimal.valueOf(double)而不是new BigDecimal(double),使数据更加精确
  1. public static void main(String[] args) {
  2. BigDecimal d1 = new BigDecimal(0.1); // 不推荐,可能导致某种错误
  3. BigDecimal d2 = BigDecimal.valueOf(0.1); // 规范
  4. // 打印出来的值
  5. // d1:0.1000000000000000055511151231257827021181583404541015625
  6. // d2:0.1
  7. }
  1. public BigDecimal(double val) {
  2. this(val, MathContext.UNLIMITED); // 得到的是近似值
  3. }
  4. public static BigDecimal valueOf(double val) {
  5. return new BigDecimal(Double.toString(val));
  6. }
  7. public BigDecimal(String val) {
  8. this(val.toCharArray(), 0, val.length()); // 精确值
  9. }

性能与可靠

数组的复制使用System.arraycopy(*)

执行效率更高。
public static native void arraycopy(Object src, int srcPos, Object dst, int dstPos, int length);

字符串转为基本数据类型

使用Integer.parseInt(String) 而不是Integer.valueOf(String)。
  1. // 不规范
  2. String myNum = "12.2";
  3. float f = new Float(myNum).floatValue(); // 增加了开销
  4. // 规范
  5. String myNum = "12.2";
  6. float f = Float.parseFloat(myNum);
  1. // 返回的是装箱基本类型,进行一次装箱和一次拆箱,导致高开销
  2. public static Integer valueOf(String string) throws NumberFormatException {
  3. return valueOf(parseInt(string));
  4. }
  5. // 直接返回基本类型
  6. public static int parseInt(String string) throws NumberFormatException {
  7. return parseInt(string, 10);
  8. }

优点:可读性,效率更高。
基本类型与装箱基本类型的主要区别:
1. 基本类型只有值,装箱基本类型具有与它们的值不用的同一性。
2. 装箱基本类型有非功能值null(类类型)。
3. 基本类型通常比装箱基本类型更节省时间和空间。

基本数据类型转为字符串

  1. // 不规范
  2. int myInt = 4;
  3. String myIntString = new Integer(myInt).toString(); // 多创建了一个Integer对象
  4. myIntString = Integer.valueOf(myInt).toString();
  5. myIntString = 4 + "";
  6. // 规范
  7. int myInt = 4;
  8. String myIntString = Integer.toString(myInt);

字符串索引

用lastIndexOf(int c)替换lastIndexOf(String string),效率更高。
  1. private static String getFileName(String url) {
  2. // 如果只有一个字符,使用 url.lastIndexOf('/')
  3. int index = url.lastIndexOf("/");
  4. String fileName = url;
  5. if (index > 0) {
  6. fileName = url.substring(index + 1);
  7. }
  8. return fileName;
  9. }

读和取方法成对使用synchronized

Android(待完成)

附录代码坏味道

应该在编程中尽量避免些“坏味道”。

1.Duplicated Code

代码重复几乎是最常见的异味了。他也是Refactoring 的主要目标之一。代码重复往

往来自于copy-and-paste 的编程风格。

2.Long method

它是传统结构化的“遗毒“。一个方法应当具有自我独立的意图,不要把几个意图

放在一起。

3.Large Class

大类就是你把太多的责任交给了一个类。这里的规则是One Class One Responsibility。

4.Divergent Change

一个类里面的内容变化率不同。某些状态一个小时变一次,某些则几个月一年才变一次;某些状态因为这方面的原因发生变化,而另一些则因为其他方面的原因变一次。面向对象的抽象就是把相对不变的和相对变化相隔离。把问题变化的一方面和另一方面相隔离。这使得这些相对不变的可以重用。问题变化的每个方面都可以单独重用。这种相异变化的共存使得重用非常困难。

5.Shotgun Surgery

这正好和上面相反。对系统一个地方的改变涉及到其他许多地方的相关改变。这些变化率和变化内容相似的状态和行为通常应当放在同一个类中。

6.Feature Envy

对象的目的就是封装状态以及与这些状态紧密相关的行为。如果一个类的方法频繁用get 方法存取其他类的状态进行计算,那么你要考虑把行为移到涉及状态数目最多的那个类。

7.Data Clumps

某些数据通常像孩子一样成群玩耍:一起出现在很多类的成员变量中,一起出现在许多方法的参数中…..,这些数据或许应该自己独立形成对象。

8.Primitive Obsession

面向对象的新手通常习惯使用几个原始类型的数据来表示一个概念。譬如对于范围,他们会使用两个数字。对于Money,他们会用一个浮点数来表示。因为你没有使用对象来表达问题中存在的概念,这使得代码变的难以理解,解决问题的难度大大增加。好的习惯是扩充语言所能提供原始类型,用小对象来表示范围、金额、转化率、邮政编码等等。

9.Switch Statement

基于常量的开关语句是OO 的大敌,你应当把他变为子类、state 或strategy。

10.Parallel Inheritance Hierarchies

并行的继承层次是shotgun surgery 的特殊情况。因为当你改变一个层次中的某一个类时,你必须同时改变另外一个层次的并行子类。

11.Lazy Class

一个干活不多的类。类的维护需要额外的开销,如果一个类承担了太少的责任,应当消除它。

12.Speculative Generality

一个类实现了从未用到的功能和通用性。通常这样的类或方法唯一的用户是testcase。不要犹豫,删除它。

13.Temporary Field

一个对象的属性可能只在某些情况下才有意义。这样的代码将难以理解。专门建立一个对象来持有这样的孤儿属性,把只和他相关的行为移到该类。最常见的是一个特定的算法需要某些只有该算法才有用的变量。

14.Message Chain

消息链发生于当一个客户向一个对象要求另一个对象,然后客户又向这另一对象要求另一个对象,再向这另一个对象要求另一个对象,如此如此。这时,你需要隐藏分派。

15.Middle Man

对象的基本特性之一就是封装,而你经常会通过分派去实现封装。但是这一步不能走得太远,如果你发现一个类接口的一大半方法都在做分派,你可能需要移去这个中间人。

16.Inappropriate Intimacy

某些类相互之间太亲密,它们花费了太多的时间去砖研别人的私有部分。对人类而言,我们也许不应该太假正经,但我们应当让自己的类严格遵守禁欲主义。

17.Alternative Classes with Different Interfaces

做相同事情的方法有不同的函数signature,一致把它们往类层次上移,直至协议一致。

18.Incomplete Library Class

要建立一个好的类库非常困难。我们大量的程序工作都基于类库实现。然而,如此广泛而又相异的目标对库构建者提出了苛刻的要求。库构建者也不是万能的。有时候我们会发现库类无法实现我们需要的功能。而直接对库类的修改有非常困难。这时候就需要用各种手段进行Refactoring。

19.Data Class

对象包括状态和行为。如果一个类只有状态没有行为,那么肯定有什么地方出问题了。

20.Refused Bequest

超类传下来很多行为和状态,而子类只是用了其中的很小一部分。这通常意味着你的类层次有问题。

21.Comments

经常觉得要写很多注释表示你的代码难以理解。如果这种感觉太多,表示你需要Refactoring。

总体思路

文档说明

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注