建议1:不要在常量和变量中出现易混淆的字母
1、包名全小写,类名首字母大写,常量全部大写并用下划线分隔,变量才有驼峰命名法
2、举例(long类型数值后面小写l 和大写L的影响)
public class TestDemo {
public static void main(String[] args) {
long i = 1l;
// long i = 1L;
System.out.print("i * 2 = " + (i + i))
}
}
此处,容易得出结果是22,真正结果是 2。
比如字母“o” 和 数字 0。
建议2:莫让常量蜕变成 变量
常量一般加了 final 和 static 修饰符,基本不可能二次赋值。但下面有个特殊例子:
public class TestDemo {
public static void main(String[] args) {
System.out.print("变化的常量: " + Const.RAND_CONST)
}
}
// 接口常量
interface Const {
// 变化的常量
public final static int RAND_CONST = new Random().nextInt();
}
注意:务必让常量的值在运行期保持不变。
建议3:三元操作符的类型务必一致
举例:
public class TestDemo {
public static void main(String[] args) {
int i = 80;
String s1 = String.valueOf(i < 100 ? 90 : 100); // s1:90
String s2 = String.valueOf(i < 100 ? 90 : 100.0); // s2:90.0
System.out.print("s1 等于 s2: " + s1.equals(s2)); // false
}
}
此处包含了 隐式类型转换(int -> float),所以二者 不相等。
建议4:避免带有变长参数的方法重载
方法重载也叫(overload):就是在同一个类中,方法的名字相同,参数列表不同(顺序不同、个数不同、类型不同),实现相似的功能,与修饰符、返回值类型无关。我们会觉得方法调用的时候就像调用一个方法一样。
方法重写也叫方法覆盖(override):首先存在继承的关系中,子类继承父类并重写父类的属性、方法。方法名字相同,参数列表一致、返回值类型一致或父类返回类型的子类类型、修饰符不能缩小范围。子类不能重写构造方法、属性、静态方法、私有方法。
举例:
public class TestDemo {
// 简单折扣
public void calPrice(int price, int discount) {
float money = price * discount / 100.0F;
System.out.println("简单折扣后价格:" + formatCurrency(money));
}
// 复杂折扣
public void calPrice(int price, int... discount) {
float money = price;
for(int count : discount) {
money = money * count / 100;
}
System.out.println("复杂折扣后价格:" + formatCurrency(money));
}
private String formatCurrency(float price) {
return NumberFormat.getCurrencyInstance().format(price / 100);
}
public static void main(String[] args) {
TestDemo test = new TestDemo();
// 75 折
test.calPrice(49900, 75);
}
}
此处,重载方法也是可以运行的。实际调用的方法是第一个。
建议5:别让 null 值 和 空值威胁到 变长方法
举例:
public class TestDemo {
public void methodA(String str, Integer... value){}
public void methodA(String str, String... value){}
public static void main(String[] args) {
TestDemo test = new TestDemo();
test.methodA("test", 0);
test.methodA("test", "haha");
test.methodA("test"); // 编译报错,因为2个方法均满足,编译器无法决定使用哪一个
test.methodA("test", null); // 编译报错,因为2个方法均满足,编译器无法决定使用哪一个
}
}
可以修改为这样调用:
String[] arr = new String[1];
arr[0] = null;
test.methodA("test", arr);
建议6:覆写变长方法也循规蹈矩
public class TestDemo {
public static void main(String[] args) {
// 向上转型
Base base = new Sub();
base.fun(100, 50);
// 不转型
Sub sub = new Sub();
sub.fun(100, 50); // 编译报错
}
}
// 基类
class Base {
void fun(int price, int... discounts){
System.out.println("Base fun");
}
}
// 子类
class Sub extend Base {
@Override
void fun(int price, int[] discounts){
System.out.println("Sub fun");
}
}
注意:方法重写(覆写)的方法参数需与父类相同,不仅仅是类型、数量、顺序,还包括显示形式。
建议7:警惕自增的陷阱
public class TestDemo {
public static void main(String[] args) {
int count = 0;
for(int i = 0; i< 10; i++) {
count = count++;
}
System.out.println("count= " + count);
}
}
此处输出count=0。
首先将count的值拷贝到临时变量区,然后对count加1,最后返回临时变量区的值,将count重置为0。
建议9:少用静态导入
import static java.lang.Double.*;
import static java.lang.Math.*;
import static java.lang.Integer.*;
import static java.text.NumberFormat.*;
public class Test {
public static void main(String[] args) {
double s = PI * parseDouble(1);
NumberFormat nf = getInstance();//此处可阅读性差,应改为 NumberFormat.getInstance()
}
}
注意:
- 不建议使用 *(星号)来导入静态元素;
- 方法名是具有明确、清晰表象意义的工具类。
建议10:不要在本类中覆盖静态导入的变量和方法
import static java.lang.Math.PI;
import static java.lang.Math.abs;
public class Test {
// 常量名与静态导入的 PI 相同
public final static String PI = "祖冲之";
// 方法名 与 静态导入的方法相同
public static int abs(int value) {
return 0;
}
public static void main(String[] args) {
System.out.println("PI=" + PI); // 输出“祖冲之”
System.out.println("abs()=" + abs(-100)); // 输出 0
}
}
此处并没有调用 Math的方法,而是调用 本类(Test)的常量和方法。
因为编译器有一个“最短路径”原则:如果能够在本类中找到变量、常量和方法,就不会到其他包或父类、接口中查找,以确保本类的属性、方法优先。
建议11:养成良好习惯,显示声明 UID
我们将一个类 实现 Serializable 接口时,常常编译器会提醒我们加上 serialVersionUID。如果我们不指定,则会在编译时自动生成。此后,我们进行反序列化时,程序会比较 serialVersionUID的值。相同则可以反序列化,不同则抛出异常(InvaildClassException)。
所以,请显示声明 serialVersionUID。
建议12:避免用序列化类在构造函数中为不变量赋值
不知道怎么说,那就记住它吧。
注意:
- 反序列化时构造函数不会被执行。
- 在序列化类中,不要使用构造函数为 final 变量赋值。
建议13:避免为 final 变量复杂赋值
注意:此建议可以结合建议12来看,是其拓展。总结如下:
- 在序列化类中,不要使用构造函数为 final 变量赋值。
- 在序列化类中,不要使用方法返回值为 final 变量赋值。
建议14:使用序列化类的私有方法巧妙解决部分属性持久化问题
部分属性持久化问题,常见的解决方法是在不需要持久化的的字段前加 transient 关键字即可。
建议15:break万不可忘
未完,待续。。。(PS:欢迎大家补充)