一、Java开发中通用的方法和准则
1、不要在常量和变量中出现易混淆的字母
eg:long i=1l,这是11还是lL呢,建议使用大写的方式;
2、不要让常量变成变量
eg:public static final int RAND_CONST=new Random().nextInt();
3、三元符的类型务必一致
eg:String s=String.valueOf(i<100?90:100.0);
4、避免带有变长参数的方法重载
eg:calPrice(int price,int discount);
calPrice(int price,int... discounts);
5、别让null值和空值威胁到变长方法
eg:methodA(String str,Integer... is);
methodA(String str,String... strs);
Test:methodA(“China”);
methodA(“China”,null);
6、覆写变长方法也要循规蹈矩
Eg:parent:method(int price,int... discounts);
Children:method(int price,int[] discounts);
7、警惕自增陷阱
Eg:int count=0;for(int i=0;i<10;i++){count=count++};输出结果为0
8、不要让就语法困扰
Eg:goto语句、methodA:methodB();
9、少用静态导入
10、不要在本类中覆盖静态导入的变量和方法
11、养成良好的习惯,显式声明UID
Eg:序列化
12、避免用序列化类在构造函数中为不变量(final修饰)赋值
13、避免为final变量复杂赋值
Eg:public final String name=initName();
Public String initName(){return “hello”;}
反序列化时final变量在以下情况下不会被重新赋值:
1)通过构造函数为final变量赋值;
2)通过方法返回值为final变量赋值;
3)Final修饰的属性不是基本类型;
14、使用序列化类的私有方法巧妙解决部分属性持久化问题
15、Break万万不可忘
Switch....case中....
16、易变业务使用脚本语言编写
例如通过java中的ScriptEngine调用javascript中的方法
17、慎用动态编译:在运行期间直接编译.java文件,执行.class,将java类信息写入到字符串中,然后运行期间调用相关接口执行字符串中的java信息
18、避免instanceOf非预期结果,注意instanceOf成立的条件是操作数左右两边要有继承或者实现关系,否则编译会报错
Eg:’A’ instanceOf Character、new Date() instanceOf String,
19、断言绝对不是鸡肋:assert<布尔表达式>:<错误信息>
20、不要只替换一个类:发布应用系统时禁止使用类文件替换方式,整体war包发布才是完全之策
二、基本类型
21、判断奇偶数,用偶判断,不用奇判断
Eg:i%2==1?”奇数”:”偶数”;-1的时候会出现问题
正确使用:i%2==0?”偶数”:”奇数”;
22、用BigDecimal和整形类型处理货币
23、不要让类型默默转换
Eg:public static final int LENGTH_SPEED=30*10000*1000;
long v=LENGTH_SPEED*60*8会出现负数,因为计算的时候,先是运算再进行类型转换的,三个数都是int类型,计算后超出int的最大值,所以计算结果为负值;
正确的方式:long v=1L*LENGTH_SPEED*60*8;计算的时候后面的都会转换成为长整形
24、整形类型的边界值需要考虑,在进行单元测试的时候,进行int类型参数测试的时候,需要考虑下面几个值:0、正最大、负最小
25、不要让四舍五入亏了一方,例如在做银行、金融相关行业的系统的时候,要根据不同的场景,慎重选择不同的舍入模式,以提高项目的精准度,减少算法损失
26、提防包装类型的null值:包装类型参与运算时候,要做null值校验
27、谨慎包装类型的大小比较
Eg:Integer a=new Integer(100);
Integer b=new Integer(100);
a==b:false,a>b:false,a<b:false;
28、优先使用整形池:基本类型的装箱动作是通过valueOf方法来实现的,对于int类型转换为Integer对象,如果int值的范围在-128到127之间,那么它直接从cache数组中获得,不在该范围的int类型则通过new生成包装对象,通过包装类的valueOf生成包装实例可以显著提高空间和时间性能
29、优先选择基本类型
Eg:int i=0;
test(i);
test(Integer.valueOf(i));
public static void test(long i){}
public static void test(Long i){}
在进行自动装箱的过程中,基本类型可以先加宽,再转变成宽类型的包装类型,但是不能直接转变成宽类型的包装类型。Int可以加宽变成long类型,然后再转变成为Long对象,但是不能直接变成Long对象。
30、不要随便设置随机数的种子
Eg:Random random=new Random(1000);产生随机数的种子被固定了
Java中产生随机数:Random、Math.random();
三、类对象及方法
31、在接口中不要存在实现的代码
Interface S{public void doSomething();} interface B{S s=new S(){public void dos{}}}
32、静态变量一定要先声明后赋值
Eg:static{i=100;} public static int i=1;
33、不要覆写静态方法,当父类中定义了一个静态方法后,子类中应不要覆写静态方法,类中定义静态方法或属性应该通过类名直接访问,通过实例对象访问静态方法或属性不是好的习惯;
34、构造函数应尽量简化,否则可能会出现因初始化的时候变量的不一致;
35、避免在构造函数中初始化其他类
36、使用构造代码块精炼程序:构造代码块会在每个不同参数的构造方法中都执行一次,先执行构造代码块,再执行构造方法,它并不是在构造函数之前运行的,它依托于构造函数执行,我们可以把构造代码块运用到如下场景中:1:初始化实例变量2:初始化实例环境;
37、构造代码块会想你所想,在构造方法中使用this和super的时候,构造代码块已经替你考虑好了,如果在有参数构造方法中使用this(),是不会再重新执行构造代码块的,所以要灵活运用;
38、使用静态内部类加强类的封装性和提高了代码的可读性
39、使用匿名类的构造函数:new ArrayList(){};new ArrayList(){{}{}{}}
40、匿名类的构造函数很特殊;
41、灵活使用内部类,让多重继承成为现实;
42、让工具类不可实例化最好的方式是:这样也不能利用反射机制进行实例化
public class Utils{
private Utils(){
Throws new Error(“don’t intstance me!!”);
}}
43、避免对象的浅拷贝:
Object提供的clone方法只是一种浅拷贝方式,也就是说它并不会把对象的所有属性全部拷贝一份,而是有选择性的拷贝,其拷贝规则如下:
基本类型:则拷贝其值
对象:拷贝地址引用,也就是说新拷贝出的对象与原有对象共享该实例变量,不受访问权限的限制。
String字符串:拷贝的也是一个地址,是个引用,但是在修改时,它会从字符串池(String pool)中重新生成新的字符串,原有的字符串对象保持不变,在此我们可以认为String是一个基本类型。
44、推荐使用序列化的方式实现对象的拷贝:参考我的博文,java对象的浅拷贝和深拷贝:http://blog.csdn.net/harderxin/article/details/41694747
45、覆写equals方法时不要识别不出自己(见代码)
// public boolean equals(Object obj) { // if(obj instanceof Person3){ // Person3 p=(Person3)obj; //去掉trim() // return name.equalsIgnoreCase(p.getName().trim()); // } // return false; // }
46、equals应该考虑null值情景
// 第二个版本----建议46 // @Override // public boolean equals(Object obj) { // if(obj instanceof Person3){ // Person3 p=(Person3)obj; // if(p.getName()==null||name==null){ // return false; // }else{ // return name.equalsIgnoreCase(p.getName().trim()); // } // } // return false; // }
47、为了防止继承,建议在equals中使用getClass进行类型判断,而不是使用instanceOf
// 第三个版本----建议47 @Override public boolean equals(Object obj) { if(obj!=null&&obj.getClass()==this.getClass()){ Person3 p=(Person3)obj; if(p.getName()==null||name==null){ return false; }else{ return name.equalsIgnoreCase(p.getName().trim()); } } return false; }
48、覆写equals方法必须覆写hashcode方法:
HashMap:底层处理机制是以数组的方式保存Map条目(Map Entry)的,这其中的关键是这个数组下标的处理机制:依据传入元素hashCode方法的返回值决定其数组的下标,如果该数组位置上已经有了Map条目,且与传入的键值相等则不处理,若不相等则覆盖;如果数组位置没有条目,则插入,并加入到Map条目的链表中,同理,检查键是否存在也是根据哈希码确定位置,然后遍历查找键值的。HashCode的值我们可以使用org.apache.commons.lang.builder包下的一个哈希码生成工具HashCodeBuilder生成
49、推荐覆写toString方法:java提供默认的toString方法不友好,打印出来只有机器能看懂,其格式为类名+@+hashCode,所以建议覆写toString方法,便于我们进行调试
50、使用package-info类为包服务:在需要用到包的地方,可以考虑一下这个特殊的类
51、不要主动进行垃圾回收:在应用程序中不要直接使用System.gc()来主动对垃圾进行回收,即使经常出现内存溢出也不要调用