@ZeroGeek
2016-08-18T09:40:28.000000Z
字数 9343
阅读 1079
原则:保证命名后能准确表达业务。
// 示例public class Person {public static final int MAX_VALUE = 1000;private static double sShareMoney;private int mAge;// ...private void cost(double money) {double localMoney = money;// ...}// ...}
类名和接口使用类意义完整的英文描述,每个英文单词的首字母使用大写、其余字母使用小写的大小写混合法
public class UploadNickNameTask {// ...}public interface OnItemClickListener {// ...}
方法名使用类意义完整的英文描述:第一个单词的字母使用小写、剩余单词首字母大写其余字母小写的大小写混合法
public String getType();public boolean isFinished();public void setVisible(boolean isVisible);public void show();public void addKeyListener(Listener listener);
程序块要采用缩进风格编写,缩进的空格数为4个。
分界符(如大括号‘{’和‘}’)应各独占一行,同时与引用它们的语句左对齐。在函数体的开始、类和接口的定义、以及if、for、do、while、switch、case语句中的程序或者static、,synchronized等语句块中都要采用如上的缩进方式。
if (a > b) {doStart();}
较长的语句、表达式或参数(>80字符)要分成多行书写,长表达式要在低优先级操作符处划分新行,操作符放在新行之首(sql语句字符串拼接为了可读性除外),划分出的新行要进行适当的缩进,使排版整齐,语句可读。
if (logger.isDebugEnabled()){logger.debug("Session destroyed,call-id"+ event.getSession().getCallId());}
不允许把多个短语句写在一行中,即一行只写一条语句,包括声明变量。
// 不规范int a,b;a = b++;// 规范int a;int b;a = b;b++;
if, for, do, while, case, switch, default 等语句自占一行,且if, for, do, while,switch等语句的执行语句无论多少都要加括号{},case 的执行语句中如果定义变量必须加括号{}。
if (isShow()) {return true;}switch (type) {case 0: {a = b;b++;// ...}break;// ...}
在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符之前、之后或者前后要加空格;进行非对等操作时,如果是关系密切的立即操作符(如.),后不应加空格。
if (a == b) {objectA.doStart();}a *= 2;
某类 {
类的公有属性定义
类的保护属性定义
类的私有属性定义
类的公有方法定义
类的保护方法定义
类的私有方法定义
}
修饰词按照指定顺序书写:
public/protected/private abstract static final transient volatile synchronized native
// While语句的规则如下:while (condition) {statements;}// 禁用这种do while语句do {statements;} while (condition);
Switch语句风格如下。需要注意的是不要忘了default,break。另外分支和分支之间以空行隔开。
switch (condition) {case ABC:case DEF:statements;break;case XYZ: {statements;}break;default:statements;break;}
if (condition1) {statements;} else if (condition2) {statements;} else {statements;}
对于数组、list等的遍历,如果不是特殊要求(比如在循环遍历中删除某个元素,for中边循环边删除会ConcurrentModificationException异常,这种应该使用Iterator迭代器),使用for-each方式。
for (Item item:items) {statements;}
//禁止方式while (index < products.getCount()) {//每此都会执行一次getCount()方法,//若此方法耗时则会影响执行效率//而且可能带来同步问题,若有同步需求,请使用同步块或同步方法}for (int i = 0; i < products.getCount(); i++) {// ...}//推荐方式,将操作结构保存在临时变量里,减少方法调用次数final int count = products.getCount();while (index < count) {}// 或者for (int i = 0; i < count; i++) {// ...}
不要catch异常而不做任何处理,要么不catch异常(让异常抛出去),要么catch作特殊处理,或者生成一个新的异常抛出去。
这里有两个异常需要特别注意,NullPointerException原则上是不允许catch的,OutOfMemoryError在解码图片时或者经常出现内存使用紧张的字符串拼接时可catch,其他地方尽量不要catch。
以下这种方式是不规范的:
void setServerPort(String value) {try {serverPort = Integer.parseInt(value);} catch (NumberFormatException e) {//等于直接吃掉了异常}}
对所有异常做特殊处理,最起码在遇到异常的地方打印异常日志:
void setServerPort(String value) {try {serverPort = Integer.parseInt(value);} catch (NumberFormatException e) {Debug.exception(TAG, e);}}
不建议catch普通的异常,比如Exception、Throwable。而应catch具体的异常类型,比如IllegalArgumentException等。
下面的做法是不推荐的:
try {FileOutputStream outStream = new FileOutputStream(new File("tmp.txt"));outStream.write(bytes);} catch (Exception e) {statements;}
应该捕获特定的异常:
try {FileOutputStream outStream = new FileOutputStream(new File("tmp.txt"));outStream.write(bytes);} catch (FileNotFoundException e) {statements;} catch (IOException e) {statements;}
数组声明的时候使用 int[] index,而不要使用 int index[]。
尽量使变量的作用域降到最小范围,如果不是特殊要求,不要过早创建不需要的对象。
// 建议使用:String s = "Hello";// 而不是下面这样的形式:String s;// …s = "Hello";
如果一个包含二元运算符的表达式出现在三元运算符" ? : "的"?"之前,那么应该给表达式添上一对圆括号。
a = (x >= 0) ? x : -x
String str = "";if(TextUtil.isEmpty(str)){// ...}if("rect".equals(str)) {// ...}
防止溢出
// 不规范 结果值:float twoThirds = 2/3; // 0.0long millisInYear = 1000*3600*24*365; // 1471228928long bigNum = Integer.MAX_VALUE + 2; // -2147483647long bigNegNum = Integer.MIN_VALUE - 1; // 2147483647
// 规范 期望值:float twoThirds = 2f/3; // 0.6666667long millisInYear = 1000L*3600*24*365; // 31536000000long bigNum = Integer.MAX_VALUE + 2L; // 2147483649long bigNegNum = Integer.MIN_VALUE - 1L; // -2147483649// 或者float twoThirds = (float)2/3;long millisInYear = (long)1000*3600*24*365;long bigNum = (long)Integer.MAX_VALUE + 2;long bigNegNum = (long)Integer.MIN_VALUE - 1;
游标获取字符串的问题: String result = c.getString(c.getColumnIndex(columnName); 如果数据库表名字为
columnName的字段的值是null,则result不是null而是一个字符串”null”,在做服务器开发的时候jdbc驱动是会返回空串的.
统一使用CollectionUtil工具类
public class CollectionUtil {public static boolean isEmpty(Collection<?> collection){return collection == null || collection.isEmpty();}public static boolean isEmpty(Map map){return map == null || map.isEmpty();}}
可以使用IDE自动生成。如果不这样做,可能导致该类无法结合所有基于散列的集合一起正常运作,包括HashMap,HashSet和Hashtable。否则违反的Object.hashCode的约定:相等的对象必须具有相等的hashcode。
// 不规范public static void main( String[] args ) {String argStr = args.toString();int argHash = args.hashCode();// 规范public static void main( String[] args ) {String argStr = Arrays.toString(args);int argHash = Arrays.hashCode(args);
使用BigDecimal.valueOf(double)而不是new BigDecimal(double),使数据更加精确
public static void main(String[] args) {BigDecimal d1 = new BigDecimal(0.1); // 不推荐,可能导致某种错误BigDecimal d2 = BigDecimal.valueOf(0.1); // 规范// 打印出来的值// d1:0.1000000000000000055511151231257827021181583404541015625// d2:0.1}
public BigDecimal(double val) {this(val, MathContext.UNLIMITED); // 得到的是近似值}public static BigDecimal valueOf(double val) {return new BigDecimal(Double.toString(val));}public BigDecimal(String val) {this(val.toCharArray(), 0, val.length()); // 精确值}
执行效率更高。
public static native void arraycopy(Object src, int srcPos, Object dst, int dstPos, int length);
使用Integer.parseInt(String) 而不是Integer.valueOf(String)。
// 不规范String myNum = "12.2";float f = new Float(myNum).floatValue(); // 增加了开销// 规范String myNum = "12.2";float f = Float.parseFloat(myNum);
// 返回的是装箱基本类型,进行一次装箱和一次拆箱,导致高开销public static Integer valueOf(String string) throws NumberFormatException {return valueOf(parseInt(string));}// 直接返回基本类型public static int parseInt(String string) throws NumberFormatException {return parseInt(string, 10);}
优点:可读性,效率更高。
基本类型与装箱基本类型的主要区别:
1. 基本类型只有值,装箱基本类型具有与它们的值不用的同一性。
2. 装箱基本类型有非功能值null(类类型)。
3. 基本类型通常比装箱基本类型更节省时间和空间。
// 不规范int myInt = 4;String myIntString = new Integer(myInt).toString(); // 多创建了一个Integer对象myIntString = Integer.valueOf(myInt).toString();myIntString = 4 + "";// 规范int myInt = 4;String myIntString = Integer.toString(myInt);
用lastIndexOf(int c)替换lastIndexOf(String string),效率更高。
private static String getFileName(String url) {// 如果只有一个字符,使用 url.lastIndexOf('/')int index = url.lastIndexOf("/");String fileName = url;if (index > 0) {fileName = url.substring(index + 1);}return fileName;}
应该在编程中尽量避免些“坏味道”。
代码重复几乎是最常见的异味了。他也是Refactoring 的主要目标之一。代码重复往
往来自于copy-and-paste 的编程风格。
它是传统结构化的“遗毒“。一个方法应当具有自我独立的意图,不要把几个意图
放在一起。
大类就是你把太多的责任交给了一个类。这里的规则是One Class One Responsibility。
一个类里面的内容变化率不同。某些状态一个小时变一次,某些则几个月一年才变一次;某些状态因为这方面的原因发生变化,而另一些则因为其他方面的原因变一次。面向对象的抽象就是把相对不变的和相对变化相隔离。把问题变化的一方面和另一方面相隔离。这使得这些相对不变的可以重用。问题变化的每个方面都可以单独重用。这种相异变化的共存使得重用非常困难。
这正好和上面相反。对系统一个地方的改变涉及到其他许多地方的相关改变。这些变化率和变化内容相似的状态和行为通常应当放在同一个类中。
对象的目的就是封装状态以及与这些状态紧密相关的行为。如果一个类的方法频繁用get 方法存取其他类的状态进行计算,那么你要考虑把行为移到涉及状态数目最多的那个类。
某些数据通常像孩子一样成群玩耍:一起出现在很多类的成员变量中,一起出现在许多方法的参数中…..,这些数据或许应该自己独立形成对象。
面向对象的新手通常习惯使用几个原始类型的数据来表示一个概念。譬如对于范围,他们会使用两个数字。对于Money,他们会用一个浮点数来表示。因为你没有使用对象来表达问题中存在的概念,这使得代码变的难以理解,解决问题的难度大大增加。好的习惯是扩充语言所能提供原始类型,用小对象来表示范围、金额、转化率、邮政编码等等。
基于常量的开关语句是OO 的大敌,你应当把他变为子类、state 或strategy。
并行的继承层次是shotgun surgery 的特殊情况。因为当你改变一个层次中的某一个类时,你必须同时改变另外一个层次的并行子类。
一个干活不多的类。类的维护需要额外的开销,如果一个类承担了太少的责任,应当消除它。
一个类实现了从未用到的功能和通用性。通常这样的类或方法唯一的用户是testcase。不要犹豫,删除它。
一个对象的属性可能只在某些情况下才有意义。这样的代码将难以理解。专门建立一个对象来持有这样的孤儿属性,把只和他相关的行为移到该类。最常见的是一个特定的算法需要某些只有该算法才有用的变量。
消息链发生于当一个客户向一个对象要求另一个对象,然后客户又向这另一对象要求另一个对象,再向这另一个对象要求另一个对象,如此如此。这时,你需要隐藏分派。
对象的基本特性之一就是封装,而你经常会通过分派去实现封装。但是这一步不能走得太远,如果你发现一个类接口的一大半方法都在做分派,你可能需要移去这个中间人。
某些类相互之间太亲密,它们花费了太多的时间去砖研别人的私有部分。对人类而言,我们也许不应该太假正经,但我们应当让自己的类严格遵守禁欲主义。
做相同事情的方法有不同的函数signature,一致把它们往类层次上移,直至协议一致。
要建立一个好的类库非常困难。我们大量的程序工作都基于类库实现。然而,如此广泛而又相异的目标对库构建者提出了苛刻的要求。库构建者也不是万能的。有时候我们会发现库类无法实现我们需要的功能。而直接对库类的修改有非常困难。这时候就需要用各种手段进行Refactoring。
对象包括状态和行为。如果一个类只有状态没有行为,那么肯定有什么地方出问题了。
超类传下来很多行为和状态,而子类只是用了其中的很小一部分。这通常意味着你的类层次有问题。
经常觉得要写很多注释表示你的代码难以理解。如果这种感觉太多,表示你需要Refactoring。