zoukankan      html  css  js  c++  java
  • Java开发最容易踩的十个大坑,每条都是血的教训!

    本文为Java程序员们准备了一系列广为流传的Java最佳编程实践,请认真读几遍(尤其是小白)。

    认真看完后,必有收获,日后开发可以少踩坑!

    文末干货:Java业务开发常见错误100例

    1.优先返回空对象而非null

    如果程序要返回一个对象,确保返回的对象有值而不是null。这能节省大量的"if else"检查。

    public String getLocationName {    return (null == cityName ? "": cityName);}

    2.谨慎操作字符串

    如果两个字符串在for循环中使用+操作符进行拼接,那么每次循环都会产生一个新的字符串对象。这不仅浪费内存空间同时还会影响性能。类似的,如果初始化字符串对象,尽量不要使用构造方法,而应该直接初始化。比方说:

    //Slower InstantiationString java = new String("百度搜 程序源码论坛 获精品");     //Faster InstantiationString soGood = "获更多最新精品资源"

    3.避免无用对象

    创建对象是Java中最昂贵的操作之一。因此最好在有需要的时候再进行对象的创建/初始化。如下:

    import java.util.ArrayList;import java.util.List;public class Employees {    private List Employees;    public List getEmployees() {        //initialize only when required        if(null == Employees) {            Employees = new ArrayList();        }        return Employees;    }}

    4.数组与ArrayList之争

    开发人员经常会发现很难在数组和ArrayList间做选择。它们二者互有优劣。如何选择应该视情况而定。

    • 数组是定长的,而ArrayList是变长的。由于数组长度是固定的,因此在声明数组时就已经分配好内存了。而数组的操作则会更快一些。另一方面,如果我们不知道数据的大小,那么过多的数据便会导致- - ArrayOutOfBoundException,而少了又会浪费存储空间。
    • ArrayList在增删元素方面要比数组简单。
    • 数组可以是多维的,但ArrayList只能是一维的。
    import java.util.ArrayList;public class arrayVsArrayList {    public static void main(String[] args) {        int[] myArray = new int[6];        myArray[7]= 10; // ArraysOutOfBoundException        //Declaration of ArrayList. Add and Remove of elements is easy.        ArrayList<Integer> myArrayList = new ArrayList<>();        myArrayList.add(1);        myArrayList.add(2);        myArrayList.add(3);        myArrayList.add(4);        myArrayList.add(5);        myArrayList.remove(0);                for(int i = 0; i < myArrayList.size(); i++) {        System.out.println("Element: " + myArrayList.get(i));        }                //Multi-dimensional Array         int[][][] multiArray = new int [3][3][3];     }}

    5.try块的finally块没有被执行

    看下下面这段代码:

    public class shutDownHooksDemo {    public static void main(String[] args) {        for(int i=0;i<5;i++)        {            try {                if(i==4) {                    System.out.println("程序源码论坛 www.cx1314.cn");                    System.exit(0);                }            }            finally {                System.out.println("最流行最优质的IT资源社区!");            }        }    }}

    从代码来看,貌似finally块中的println语句应该会被执行5次。但当程序运行后,你会发现finally块只执行了4次。第5次迭代的时候会触发exit函数的调用,于是这第5次的finally便永远也触发不到了。原因便是——System.exit会挂起所有线程的执行,包括当前线程。即便是try语句后的finally块,只要是执行了exit,便也无力回天了。

    在调用System.exit时,JVM会在关闭前执行两个结束任务:

    首先,它会执行完所有通过Runtime.addShutdownHook注册进来的终止的钩子程序。这一点很关键,因为它会释放JVM外部的资源。

    接下来的便是Finalizer了。可能是System.runFinalizersOnExit也可能是Runtime.runFinalizersOnExit。finalizer的使用已经被废弃有很长一段时间了。finalizer可以在存活对象上进行调用,即便是这些对象仍在被其它线程所使用。而这会导致不可预期的结果甚至是死锁。

    public class shutDownHooksDemo {    public static void main(String[] args) {            for(int i=0;i<5;i++)            {                    final int final_i = i;                    try {                         Runtime.getRuntime().addShutdownHook(                          new Thread() {                           public void run() {                            if(final_i==4) {                             System.out.println("Inside Try Block.Exiting without executing Finally block.");                             System.exit(0);                            }                           }                          });                    }                    finally {                            System.out.println("Inside Finally Block.");                    }            }    }}

    6.奇数判断

    看下这几行代码,看看它们是否能用来准确地判断一个数是奇数?

    public boolean oddOrNot(int num) {    return num % 2 == 1;}

    看似是对的,但是每执行四便会有一个错误的结果(用数据说话)。考虑到负奇数的情况,它除以2的结果就不会是1。因此,返回值是false,而这样是不对的。

    代码可以修改成这样:

    public boolean oddOrNot(int num) {    return (num & 1) != 0;}

    这么写不光是负奇数的问题解决了,并且还是经过充分优化过的。因为算术运算和逻辑运行要比乘除运算更高效,计算的结果也会更快。

    7.单引号与双引号的区别

    public class Haha {    public static void main(String args[]) {    System.out.print("H" + "a");    System.out.print('H' + 'a');    }}

    看起来这段代码会返回"Haha",但实际返回的是Ha169。原因就是用了双引号的时候,字符会被当作字符串处理,而如果是单引号的话,字符值会通过一个叫做基础类型拓宽的操作来转换成整型值。然后再将值相加得到169。

    8.一些防止内存泄露的小技巧

    内存泄露会导致软件的性能降级。由于Java是自动管理内存的,因此开发人员并没有太多办法介入。不过还是有一些方法能够用来防止内存泄露的。

    • 查询完数据后立即释放数据库连接
    • 尽可能使用finally块
    • 释放静态变量中的实例

    9.避免死锁

    死锁出现的原因有很多。避免死锁不是一句话就能解决的。通常来说,当某个同步对象在等待另一个同步对象所拥有的资源上的锁时,便会产生死锁。

    试着运行下下面的程序。它会告诉你什么是死锁。这个死锁是由于两个线程都在等待对方所拥有的资源,因此会产生死锁。它们会一直等待,没有谁会先放手。

    public class DeadlockDemo {   public static Object addLock = new Object();   public static Object subLock = new Object();   public static void main(String args[]) {      MyAdditionThread add = new MyAdditionThread();      MySubtractionThread sub = new MySubtractionThread();      add.start();      sub.start(); // 更多最新精品资源 www.cx1314.cn     }private static class MyAdditionThread extends Thread {      public void run() {         synchronized (addLock) {        int a = 10, b = 3;         int c = a + b;               System.out.println("Addition Thread: " + c);            System.out.println("Holding First Lock...");            try { Thread.sleep(10); }            catch (InterruptedException e) {}            System.out.println("Addition Thread: Waiting for AddLock...");            synchronized (subLock) {               System.out.println("Threads: Holding Add and Sub Locks...");            }         }      }   }   private static class MySubtractionThread extends Thread {      public void run() {         synchronized (subLock) {        int a = 10, b = 3;        int c = a - b;            System.out.println("Subtraction Thread: " + c);            System.out.println("Holding Second Lock...");            try { Thread.sleep(10); }            catch (InterruptedException e) {}            System.out.println("Subtraction  Thread: Waiting for SubLock...");            synchronized (addLock) {               System.out.println("Threads: Holding Add and Sub Locks...");            }         }      }   }}

    输出:

    Addition Thread: 13

    Subtraction Thread: 7

    Holding First Lock...

    Holding Second Lock...

    Addition Thread: Waiting for AddLock...

    Subtraction Thread: Waiting for SubLock...

    但如果调用的顺序变一下的话,死锁的问题就解决了。

    public class DeadlockSolutionDemo {   public static Object addLock = new Object();   public static Object subLock = new Object();   public static void main(String args[]) {      MyAdditionThread add = new MyAdditionThread();      MySubtractionThread sub = new MySubtractionThread();      add.start();      sub.start();   }private static class MyAdditionThread extends Thread {      public void run() {         synchronized (addLock) {        int a = 10, b = 3;        int c = a + b;            System.out.println("Addition Thread: " + c);            System.out.println("Holding First Lock...");            try { Thread.sleep(10); }            catch (InterruptedException e) {}            System.out.println("Addition Thread: Waiting for AddLock...");            synchronized (subLock) {               System.out.println("Threads: Holding Add and Sub Locks...");            }         }  // 更多最新精品资源 www.cx1314.cn         }   }      private static class MySubtractionThread extends Thread {      public void run() {         synchronized (addLock) {        int a = 10, b = 3;        int c = a - b;            System.out.println("Subtraction Thread: " + c);            System.out.println("Holding Second Lock...");            try { Thread.sleep(10); }            catch (InterruptedException e) {}            System.out.println("Subtraction  Thread: Waiting for SubLock...");            synchronized (subLock) {               System.out.println("Threads: Holding Add and Sub Locks...");            }         }      }   }}

    输出:

    Addition Thread: 13

    Holding First Lock...

    Addition Thread: Waiting for AddLock...

    Threads: Holding Add and Sub Locks...

    Subtraction Thread: 7

    Holding Second Lock...

    Subtraction Thread: Waiting for SubLock...

    Threads: Holding Add and Sub Locks...

    10.替Java省点内存

    某些Java程序是CPU密集型的,但它们会需要大量的内存。这类程序通常运行得很缓慢,因为它们对内存的需求很大。为了能提升这类应用的性能,可得给它们多留点内存。因此,假设我们有一台拥有10G内存的Tomcat服务器。在这台机器上,我们可以用如下的这条命令来分配内存:

    export JAVA_OPTS="$JAVA_OPTS -Xms5000m -Xmx6000m -XX:PermSize=1024m -XX:MaxPermSize=2048m"

    • Xms = 最小内存分配
    • Xmx = 最大内存分配
    • XX:PermSize = JVM启动时的初始大小
    • XX:MaxPermSize = JVM启动后可分配的最大空间

     

    分享学习资料:Java业务开发常见错误合集

    下载学习:http://www.cx1314.cn/thread-3182-1-1.html

    让你Get如下技能:

    • 130 个代码坑点及其解决方案

    • 100 个场景化案例解读

    • 25 次源码深度解析使用

    • 10 个工具定位问题根因

    • .........

  • 相关阅读:
    Hibernate-查询缓存
    Hibernate-二级缓存 sessionFactory
    Hibernate-二级缓存策略
    Hibernate-一级缓存session
    缓存和连接池的区别
    Hibernate-一对多的关系维护
    Hibernate-缓存
    Java基础-jdk动态代理与cglib动态代理区别
    Java基础-CGLIB动态代理
    Java基础-静态代理与动态代理比较
  • 原文地址:https://www.cnblogs.com/cxmanito/p/14149145.html
Copyright © 2011-2022 走看看