• Java开发过程中的避坑点(一)


    1. 典型空指针问题

    • 包装类型的空指针问题

    • 级联调用的空指针问题

    • Equals方法左边的空指针问题

    • ConcurrentHashMap 这样的容器不支持 Key 和 Value 为 null。

    • 集合,数组直接获取元素

    • 对象直接获取属性

    1.1包装类型的空指针问题
    public class NullPointTest {
    ​
        public static void main(String[] args) throws InterruptedException {
            System.out.println(testInteger(null));
        }
    ​
        private static Integer testInteger(Integer i) {
            return i + 1;  //包装类型,传参可能为null,直接计算,则会导致空指针问题
        }
    }
    1.2 级联调用的空指针问题
    public class NullPointTest {
        public static void main(String[] args) {
           //fruitService.getAppleService() 可能为空,会导致空指针问题
            fruitService.getAppleService().getWeight().equals("OK");
        }
    }
    1.3 Equals方法左边的空指针问题
    public class NullPointTest {
        public static void main(String[] args) {
            String s = null;
            if (s.equals("666")) { //s可能为空,会导致空指针问题
                System.out.println("666");
            }
        }
    }
    1.4 ConcurrentHashMap 这样的容器不支持 Key,Value 为 null。
    public class NullPointTest {
        public static void main(String[] args) {
            Map map = new ConcurrentHashMap<>();
            String key = null;
            String value = null;
            map.put(key, value);
        }
    }
    1.5 集合,数组直接获取元素
    public class NullPointTest {
        public static void main(String[] args) {
            int [] array=null;
            List list = null;
            System.out.println(array[0]); //空指针异常
            System.out.println(list.get(0)); //空指针一场
        }
    }
    1.6 对象直接获取属性
    public class NullPointTest {
        public static void main(String[] args) {
            User user=null;
            System.out.println(user.getAge()); //空指针异常
        }
    }

    2. 日期YYYY格式设置的坑

    日常开发,经常需要对日期格式化,但是呢,年份设置为YYYY大写的时候,是有坑的哦。

    反例:

    Calendar calendar = Calendar.getInstance();
    calendar.set(2019, Calendar.DECEMBER, 31);
    ​
    Date testDate = calendar.getTime();
    ​
    SimpleDateFormat dtf = new SimpleDateFormat("YYYY-MM-dd");
    System.out.println("2019-12-31 转 YYYY-MM-dd 格式后 " + dtf.format(testDate));
    ​

    运行结果:

    2019-12-31 转 YYYY-MM-dd 格式后 2020-12-31

    解析:

    为什么明明是2019年12月31号,就转了一下格式,就变成了2020年12月31号了?因为YYYY是基于周来计算年的,它指向当天所在周属于的年份,一周从周日开始算起,周六结束,只要本周跨年,那么这一周就算下一年的了。正确姿势是使用yyyy格式。

    正例:

    Calendar calendar = Calendar.getInstance();
    calendar.set(2019, Calendar.DECEMBER, 31);
    ​
    Date testDate = calendar.getTime();
    ​
    SimpleDateFormat dtf = new SimpleDateFormat("yyyy-MM-dd");
    System.out.println("2019-12-31 转 yyyy-MM-dd 格式后 " + dtf.format(testDate));
    ​

    3.金额数值计算精度的坑

    看下这个浮点数计算的例子吧:

    public class DoubleTest {
        public static void main(String[] args) {
            System.out.println(0.1+0.2);
            System.out.println(1.0-0.8);
            System.out.println(4.015*100);
            System.out.println(123.3/100);
    ​
            double amount1 = 3.15;
            double amount2 = 2.10;
            if (amount1 - amount2 == 1.05){
                System.out.println("OK");
            }
        }
    }

    运行结果:

    0.30000000000000004
    0.19999999999999996
    401.49999999999994
    1.2329999999999999

    可以发现,结算结果跟我们预期不一致,其实是因为计算机是以二进制存储数值的,对于浮点数也是。对于计算机而言,0.1无法精确表达,这就是为什么浮点数会导致精确度缺失的。因此,金额计算,一般都是用BigDecimal 类型

    对于以上例子,我们改为BigDecimal,再看看运行效果:

    System.out.println(new BigDecimal(0.1).add(new BigDecimal(0.2)));
    System.out.println(new BigDecimal(1.0).subtract(new BigDecimal(0.8)));
    System.out.println(new BigDecimal(4.015).multiply(new BigDecimal(100)));
    System.out.println(new BigDecimal(123.3).divide(new BigDecimal(100)));

    运行结果:

    0.3000000000000000166533453693773481063544750213623046875
    0.1999999999999999555910790149937383830547332763671875
    401.49999999999996802557689079549163579940795898437500
    1.232999999999999971578290569595992565155029296875

    发现结果还是不对,其实,使用 BigDecimal 表示和计算浮点数,必须使用字符串的构造方法来初始化 BigDecimal,正例如下:

    public class DoubleTest {
        public static void main(String[] args) {
            System.out.println(new BigDecimal("0.1").add(new BigDecimal("0.2")));
            System.out.println(new BigDecimal("1.0").subtract(new BigDecimal("0.8")));
            System.out.println(new BigDecimal("4.015").multiply(new BigDecimal("100")));
            System.out.println(new BigDecimal("123.3").divide(new BigDecimal("100")));
        }
    }

    在进行金额计算,使用BigDecimal的时候,我们还需要注意BigDecimal的几位小数点,还有它的八种舍入模式哈

    4. FileReader默认编码导致乱码问题

    看下这个例子:

    public class FileReaderTest {
        public static void main(String[] args) throws IOException {
    ​
            Files.deleteIfExists(Paths.get("jay.txt"));
            Files.write(Paths.get("jay.txt"), "你好".getBytes(Charset.forName("GBK")));
            System.out.println("系统默认编码:"+Charset.defaultCharset());
    ​
            char[] chars = new char[10];
            String content = "";
            try (FileReader fileReader = new FileReader("jay.txt")) {
                int count;
                while ((count = fileReader.read(chars)) != -1) {
                    content += new String(chars, 0, count);
                }
            }
            System.out.println(content);
        }
    }

    运行结果:

    系统默认编码:UTF-8
    ���,�����ݵ�С�к�

    从运行结果,可以知道,系统默认编码是utf8,demo中读取出来,出现乱码了。为什么呢?

    FileReader 是以当前机器的默认字符集来读取文件的,如果希望指定字符集的话,需要直接使用 InputStreamReader 和 FileInputStream。

    正例如下:

    public class FileReaderTest {
        public static void main(String[] args) throws IOException {
    ​
            Files.deleteIfExists(Paths.get("jay.txt"));
            Files.write(Paths.get("jay.txt"), "你好".getBytes(Charset.forName("GBK")));
            System.out.println("系统默认编码:"+Charset.defaultCharset());
    ​
            char[] chars = new char[10];
            String content = "";
            try (FileInputStream fileInputStream = new FileInputStream("jay.txt");
                 InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, Charset.forName("GBK"))) {
                int count;
                while ((count = inputStreamReader.read(chars)) != -1) {
                    content += new String(chars, 0, count);
                }
            }
            System.out.println(content);
        }
    }

    5. Integer缓存的坑

    public class IntegerTest {
    ​
        public static void main(String[] args) {
            Integer a = 127;
            Integer b = 127;
            System.out.println("a==b:"+ (a == b));
            
            Integer c = 128;
            Integer d = 128;
            System.out.println("c==d:"+ (c == d));
        }
    }

    运行结果:

    a==b:true
    c==d:false

    为什么Integer值如果是128就不相等了呢?编译器会把 Integer a = 127 转换为 Integer.valueOf(127)。 我们看下源码。

    public static Integer valueOf(int i) {
          if (i >= IntegerCache.low && i <= IntegerCache.high)
              return IntegerCache.cache[i + (-IntegerCache.low)];
          return new Integer(i);
     }

    可以发现,i在一定范围内,是会返回缓存的。

    默认情况下呢,这个缓存区间就是[-128, 127],所以我们业务日常开发中,如果涉及Integer值的比较,需要注意这个坑哈。还有呢,设置 JVM 参数加上 -XX:AutoBoxCacheMax=1000,是可以调整这个区间参数的,大家可以自己试一下哈

  • 相关阅读:
    Mathcad Prime 7.0软件下载及安装教程
    Heartbleed Vulnerability-心脏滴血漏洞
    Go语言中的Panic和高阶Func详细教程案例
    Sleuth+Zipkin 链路追踪
    逆向-attack之数组越界赋值函数地址
    ubuntu20部署3d高斯
    docker-rabbitMQ
    如何写好测试用例
    C语言典范编程
    java计算机毕业设计数据分析星辰网智能手机销售网站(附源码、数据库)
  • 原文地址:https://blog.csdn.net/qq_44981598/article/details/132907535