改善java程序
1、用偶判断,不用奇判断。因为负数会出错。
// 不使用 String str = i + "->" + (i%2 == 1? "奇数": "偶数"); // 而使用 String str = i + "->" + (i%2 == 0? "偶数": "奇数");
2、使用整数类型处理货币
(1)使用BigDecimal,与数据库Decimal类型字段映射时。
(2)货币扩大100倍,使用整形。
3、不要让类型默默转换。使用主动声明式转换。
java是先运算,后自动转换类型的。
long dis = 1L * 60 * 8
4、数字越界使验证条件失效。
边界测试:0,正最大,负最小。
5、四舍五入问题。Math.round和银行家算法。
Math.round(10.5) 结果 10;
Math.round(-10.5) 结果 -10;
BigDecimal i = d.multipy(r).setScale(2, RoudingMode.HALF_EVEN); // 银行家算法
6、包装类型参与运算要做null值检验。否则会报错。
for (Integer i : list) { count += (i != null) ? i : 0; }
7、优先使用整形池。
Integer i = Integer.valueOf(27);
节省空间,提高性能。判断相等要用equals方法。
8、优先使用基本类型。
9、不要覆写静态方法。
public class Client { public static void main(String[] args) { Base base = new Sub(); base.doStatic(); // 覆写后,会调用父类静态方法 base.doSomething(); // 覆写后,会调用子类非静态方法 }
在子类中构建与父类相同的方法名、输入输出参数、访问权限(可扩大),且父类、子类都是静态方法的行为叫做隐藏。目的是隐藏父类静态方法,实现子类静态方法。
@Override注解可以用于覆写,不能用于隐藏。
10、构造函数尽量简化。不抛异常、不做复杂运算。
做简单的构造函数,并将复杂问题移到start方法中,等待调用。
11、不要在构造函数中声明初始化其他类。
12、使用构造函数精炼代码
public class Client { { System.out.println("执行构造代码块"); } public Client() { System.out.println("执行无参构造"); } public Client(String str) { System.out.println("执行有参构造"); } } // 以上代码等价于: public class Client { public Client() { System.out.println("执行构造代码块"); System.out.println("执行无参构造"); } public Client(String str) { System.out.println("执行构造代码块"); System.out.println("执行有参构造"); } }
编译器会将代码块插入到每个构造函数中,在super()之后;并且构造函数中调用this()时不会再次插入。
13、内部类模拟实现多重继承。
class Son extends FatherImpl implements Mother { @Override public int strong() { return super.strong() + 1; } @Override public kind() { return new MotherSpecial().kind(); } private class MotherSpecial extends MotherImpl { public int kind() { return super.kind() - 1; } } } class Daughter extends MotherImpl implements Father { @Override public int strong() { return new FatherImpl() { @Override public int strong() { return super.strong() - 2; } }.strong(); } }
14、让工具类不可实例化
public final Class Math { private Math() {} } //以上不允许实例化,但利用反射修改构造函数的权限还是可能的。更彻底的方法是: public class UtilsClass { private UtilsClass() { throw new Error("不要实例化我"); } }
15、避免对象的浅拷贝
拷贝在内存中进行,性能方面比直接new对象快很多,特别是在大对象生成上,提升性能非常显著。
16、推荐使用序列化实现对象的拷贝
17、覆写equals时考虑自反性、对称性(考虑null)、传递性。
18、在equals中使用getClass进行类型判断,而不使用instanceof。
19、覆写equals方法必须覆写hashCode方法。
20、推荐覆写toString方法,因为默认方法不友好,打印不出有用信息。
21、特殊类:package-info类。
22、不要主动调用垃圾回收。这会停止所有响应,让出资源检查每个对象。
23、推荐String直接赋值,而不是new String("")赋值。(原因:字符串池)
24、String, replace和replaceAll的区别:replaceAll传递的第一个参数是正则表达式。
25、字符串拼接:append最快(数组拷贝),concat次之(数组拷贝后,创建String对象),加号最慢(每次新建StringBuilder,append后,toString转为字符串)。
26、推荐在复杂字符串操作中使用正则表达式。
27、Arrays.asList陷阱。
泛型类型只支持包装类型,不支持基本类型。
// 避免使用: int data[] = {1,2,3} List list = Arrays.asList(data); // list.size() : 1 //应该使用: Integer data[] = {1,2,3}; List list = Arrays.asList(data); // list.size : 3
asList产生的list不可修改。
28、不同列表的不同遍历方法。
ArrayList实现了RandomAccess接口,使用下标访问更高效。LinkedList使用迭代访问效率更高。
public static int average(List<Integer> list) { int sum = 0; if (list instanceof RandomAccess) { for (int i = 0, size = list.size(); i < size; i++) { sum += list.get(i); } } else { for (int i : list) { sum += i; } } return sum / list.size(); }
29、频繁插入删除使用LinkedList。
30、列表相等只关心元素数据。
ArrayList和Vector只有元素相等,则equals为true。其他集合类型相似。
31、subList是原List的视图,subList的修改会反映到原List上。生成子列表后不要再操作原列表,子列表会报错。
32、compareTo与equals要同步。
33、Collections.shuffle(lists)打乱顺序。
34、非稳定性排序用List。
SortedSet接口只定义了集合加入元素时的排序,不保证修改元素后的排序结果,因此TreeSet适应于不变量的集合排序。建议使用List和Collection.sort()解决。
35、switch枚举值可能会出现空指针异常。
原因是switch通过枚举值的排序值判断。如Season.Spring.ordinal()。
36、枚举类型switch的default代码中加AssertionError报错,方便排查新增枚举值而逻辑没有同步改动的错误。
37、枚举使用valueOf要校验,加try-catch。
38、枚举实现工厂方法模式更简洁、高效、不易出错。
enum CarFactory { FordCar { public Car create() { return new FordCar(); } }, BuickCar { public Car create() { return new BuickCar(); } }; public abstract Car create(); } public static void main(String[] args) { Car car = CarFactory.BuickCar.create(); }
39、枚举类型使用EnumSet和EnumMap提高效率。
40、不要在finally块中处理返回值。
无论是在finally块中加入return语句,还是在修改return的值或引用值、再返回,都不可以。会覆盖try块中的return值,屏蔽异常,造成假象,增加代码复杂性。
41、不要在构造函数中抛出异常。
42、使用Throwable获得栈信息。
43、多线程继承Thread不要覆写start方法。
44、启动线程前的stop方法不可靠。
start之前调用stop方法会将线程置为不可启用状态,但start方法本身的缺陷导致会先启动后停止,因此应该自己判断线程是否启用。
45、不使用stop方法停止线程。
stop方法是一个过时的方法。无法保证完整性,会破坏原子逻辑。
此外,interrupt方法不能终止正在执行的线程,只改变终端标识。
应该自己编码实现,比如修改终止判断条件。或者使用线程池ThreadPoolExecutor的shutdown方法。
46、线程优先级只使用三个。
Java线程支持10个优先级(另外有一个不可设的只供jvm使用的优先级)。
但不同机器对优先级的支持不同,所以只推荐使用MIN_PRIORITY, NORM_PRIORITY, MAX_PRIORITY三个优先级,但也不一定完全保证顺序。
47、volatile不能保证数据同步,只能保证线程获取到最新值。
48、异步运算考虑使用Callable接口。
Runnable接口和继承Thread类的run方法都没有返回值,不能跑出异常。Executor可以解决该问题。
49、克隆对象不比直接生成对象效率高。
因为java对new对象进行了优化,所以不要轻易使用clone。