Java 代码优化(下)

  1. 使用数据库连接池和线程池

    这两个池都是用于重用对象的,前者可以避免频繁地打开和关闭连接,后者可以避免频繁地创建和销毁线程。

  2. 使用带缓冲的输入输出流进行IO操作

    带缓冲的输入输出流,即BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream,这可以极大地提升IO效率。

  3. 顺序插入和随机访问比较多的场景使用ArrayList,元素删除和中间插入比较多的场景使用LinkedList

    这个需要理解ArrayList和LinkedList的原理。

  4. 避免让public方法中有太多的形参

    public方法即对外提供的方法,如果这些方法有太多形参主要有两点坏处:

    • 违反面向对象的编程思想,Java讲究一切都是对象,太多形参和面向对象的编程思想不契合

    • 参数太多势必导致方法调用的出错率增加

    参数最好控制在两个以内,比如我们用jdbc写一个insertStudent方法,有10个学生信息字段需要插入Student表中,可以把这10个参数封装在一个实体类中,作为insertStudent方法的形参。

  5. 字符串变量和字符串常量equals的时候将字符串常量写在前面

    这个技巧比较常见,例如:

    String str = "123";
    if (str.equals("123")) {
        code...
    }

    建议修改为:

    String str = "123";
    if ("123".equals(str)) {
        code...
    }

    这么做主要可以避免空指针异常。

  6. 在java中if (i == 1)和if (1 == i)是没有区别的,但从阅读习惯上讲,建议使用前者

  7. 不要对数组使用toString()方法

    看一下对数组使用toString()打印出来的是什么:

    public static void main(String[] args) {
        int[] arr = new int[] {1, 2, 3};
        System.out.println(arr.toString());
    }

    结果是:

    [I@18a992f

    本意是想打印出数组内容,但却可能因为数组引用arr为空而导致空指针异常。不过虽然对数组使用toString()方法没有意义,但是对集合使用该方法可以打印出集合里面的内容的,因为集合的父类AbstractCollections<E>重写了Object的toString()方法。

  8. 不要对超出范围的基本数据类型做向下强制转型

    例如一下代码不会得到想要的结果:

    public static void main(String[] args) {
        long l = 12345678901234L;
        int i = (int) l;
        System.out.println(i);
    }

    我们可能期望得到其中的某几位,但结果却是:

    1942892530

    解释一下,在Java中long型数据是8个字节64位,所以12345678901234在计算机中的表示应该是:

    0000 0000 0000 0000 0000 1011 0011 1010 0111 0011 1100 1110 0010 1111 1111 0010

    一个int型数据时4个字节32位,从低位取出上面这串二进制数据的前32位是:

    0111 0011 1100 1110 0010 1111 1111 0010

    这串二进制表示为十进制1942892530,也就是我们上面控制台的输出内容。

    注:整型默认的数据类型为int,浮点型默认的数据类型为double。

  9. 公用的集合类中不使用的数据要及时remove掉

  10. 把一个基本数据类型转为字符串,基本数据类型.toString()是最快的方式,String.valueOf(数据)次之,数据+""最慢

    • String.valueOf()方法底层调用了Integer.toString()方法,但在调用前会做空判断

    • Integer.toString()方法直接调用

    • 数据 + ""的底层使用了StringBuilder实现,先用append()方法进行拼接,再用toString()方法获取字符串

  11. 使用最有效率的方式去遍历Map

    遍历Map的方式有很多,通常情景下我们需要的是遍历Map中的Key和value,以下推荐使用的效率最高的方式:

    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<String, String>();
        map.put("111", "222");
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        Iterator<Map.Entry<String, String>> iterator = entrySet.iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            Sysout.out.println(entry.getKey() + "\t" + entry.getValue());
        }
    }

    如果只是遍历这个Map的key值,则用"Set<String> keySet = map.keySet();"会比较合适。

  12. 对资源的close()建议分开操作

    比如有如下代码:

    try {
        XXX.close();
        YYY.close();
    } catch (Exception e) {
        code...
    }

    建议修改为:

    try {
        XXX.close();
    } catch (Exception e) {
        code...
    }
    try {
        YYY.close();
    } catch (Exception e) {
        code...
    }

    虽然麻烦点,却能避免资源泄露,试想,如果用没有修改过的代码,万一XXX.close()抛异常了,那么就进入catch块中了,YYY.close()则不会执行,YYY这块资源就不会回收了,一直占用着,这样的代码一多,可能引起资源句柄泄漏。而改为修改过的代码,就保证了无论如何XXX和YYY都会被close掉。

  13. 切忌以常量定义的方式替代魔鬼数字,魔鬼数字的存在将极大地降低代码可读性,字符串常量是否使用常量定义可以视情况而定

  14. long或者Long初始赋值时,使用大写的L而不是小写的l,因为字母l极易与数字1混淆,这个细节需要注意

  15. 所有重写的方法必须保留@Override注解

    这么做有三点原因:

    • 清楚地知道这个方法由父类继承而来

    • getObject()和get0bject()方法,前者第四个字母是"O",后者第四个字母是"0",加了@Override注解可以马上判断是否重写成功

    • 在抽象类中对方法签名进行修改,实现类会马上报出编译错误

  16. 推荐使用JDK7中新引入的Objects工具类来进行对象的equals比较,直接a.equals(b)有空指针异常的风险

  17. 循环体内不要使用“+”进行字符串的拼接,而直接使用StringBuilder不断append

    每次虚拟机碰到“+”这个操作符对字符串进行拼接的时候,会new出一个StringBuilder,然后调用append方法,最后调用toString方法转换为字符串,也就是循环多少次,就会new出多少个StringBuilder来,这对于内存来讲是一种浪费。

  18. 不捕获Java类库中定义的继承自RuntimeException的运行时异常类

    异常处理效率较低,RuntimeException的运行时异常,其中绝大多数完全可由程序员来规避,比如:

    • ArithmeticException 可通过判断除数是否为空来规避

    • NullPointerException 可通过判断对象是否为空来规避

    • IndexOutOfBoundsException 可通过判断数组/字符串长度来规避

    • ClassCastException 可通过instanceof关键字来规避

    • ConcurrentModificationException 可使用迭代器来规避

  19. 避免Random实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一seed导致性能下降,JDK7之后可以使用ThreadLocalRandom来获取随机数

  20. 静态类、单例类、工厂类将它们的构造函数置为private

    这是因为静态类、单例类、工厂类这种类本来就不需要我们从外部将它们new出来,将构造函数置为private之后,保证了这些类不会产生实例。

  21. 对于TreadLocal使用前或者使用后一定要先remove

    线程池技术做的是一个线程重用,意味着,在代码运行过程中,一条线程使用完毕,并不会销毁而是等待下一次的使用,在Thread类中,持有ThreadLocal.ThreadLocalMap的引用,线程不销毁意味着上条线程set的ThreadLocal.ThreadLocalMap中的数据依然存在,那么在下一条线程重用这个Thread的时候,很可能get到的是上条线程set的数据而不是自己想要的内容。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值