Java基础(四)

框架是个好东西,可早晚有一天会过时,这世界上就没有亘古不变的东西,来学下Java基础吧

equals的正确使用

equals方法非常容易抛出空指针异常,如以下代码块

    public static void main(String[] args) {
        boolean b = compareToHello(null);
        System.out.println("比较结果为:"+b);
    }

//    将传入的字符串和helloworld字符串比较
    static boolean compareToHello(String a){
        return a.equals("helloworld");
    }

常用的改进方法为:

//    将传入的字符串和helloworld字符串比较
    static boolean compareToHello(String a){
        return "helloworld".equals(a);
    }

因为helloworld字符串取自常量池,必不为null,不用担心

如果是要传入两个字符串,比较他们,怎么办呢

常规思路:

​ 因为两个字符串都有可能是null,所以要先判断他们是否为空【只需要判断一个就够了】,然后调用非空对象的equals方法即可

不过更推荐Java7引进的工具类Objects,具体代码如下

    public static void main(String[] args) {
        boolean b = compareToHello(null, null);
    }

    //    将传入的字符串和helloworld字符串比较
    static boolean compareToHello(String a, String b) {
        return Objects.equals(a, b);
    }

Objects的equals方法的源码如下

public static boolean equals(Object a, Object b) {
        // 可以避免空指针异常。如果a==null的话此时a.equals(b)就不会得到执行,避免出现空指针异常。
        return (a == b) || (a != null && a.equals(b));
    }

⚠️:Objects.equals(null,null)的返回值为true,因为null=null成立

对于浮点数之间的比较,不能直接用equals方法去比较,原因可从下面代码块体现一二

    public static void main(String[] args) {
        float a = 1f - 0.9f;
        float b = 0.9f - 0.8f;
        System.out.println(a);
        System.out.println(b);
    }

<<<0.100000024
<<<0.099999964

了解C和计算机底层的朋友都应该知道出现这个的问题,和计算机对浮点数的存储有关,精度丢失,这时候用equals比较肯定出问题

常见解决方法一,提高精度,使用双精度数:

public static void main(String[] args) {
    double a = 1 - 0.9;
    double b = 0.9 - 0.8;
    System.out.println(a);
    System.out.println(b);
    System.out.println(a==b);
}

<<<0.09999999999999998
<<<0.09999999999999998
<<<true

解决方法二,使用BigDecimal类:

    public static void main(String[] args) {
        BigDecimal num1 = new BigDecimal("1.0");
        BigDecimal num2 = new BigDecimal("0.9");
        BigDecimal num3 = new BigDecimal("0.8");

//      相当于是1.0-0.9
        BigDecimal a = num1.subtract(num2);
        BigDecimal b = num2.subtract(num3);
        System.out.println(a);
        System.out.println(b);
        System.out.println(a.equals(b));
    }

<<<0.1
<<<0.1
<<<true

注意:

​ 为了防止精度丢失,《阿里巴巴Java开发手册》中推荐使用BigDecimal类的BigDecimal(String)构造方法

使用总结:

​ BigDecimal操作精度要求高的浮点数,BigInteger操作大的整数【用来操作long类型的整数】(用法和BigDecimal差不多,有兴趣的去翻翻源码),并且BigDecimal底层使用了BigInteger

如果对整型数比较有兴趣的朋友,可以去看看以前的博客 Java基础(一),关于常量池的介绍,说不定会有收获哦

基本数据类型和封装类型使用标准

参照《阿里巴巴Java开发手册》

  • 所有POJO类属性必须是包装类型
  • RPC方法的参数和返回值使用包装类型
  • 局部变量使用基本数据类型

POJO:实体类对象,类中的所有字段设置为private,提供get和set方法供外界访问的类

RPC方法:供远程调用的方法,在SpringCloud和Dubbo中会有所涉及

Arrays.asList方法

Arrays.asList是开发中常用的方法,具体作用是将数组转换成一个集合(有关Arrays类更多介绍可以查看Java基础(三))

简单用法:

    public static void main(String[] args) {
        String[] a = {"hello","world"};
        List<String> list = Arrays.asList(a);
    }

因为Java设计出的集合类就是为了优化数组,做了越界访问一系列拦截

⚠️:使用Arrays.list获取的数组注意事项

传递的数组里面存储的必然是对象,因为集合里面不能存储基本数据类型,只能存储对象

如果传递的是原生数据类型数组,如下代码所示:

    public static void main(String[] args) {
        int[] a = {1, 2, 3};
        List<int[]> list = Arrays.asList(a);
        System.out.println(list);
    }

<<<[[I@6b71769e]

会直接将int[]当成一个对象放到list[0]的位置上去

解决办法(使用包装类):

    public static void main(String[] args) {
        Integer[] a = {1, 2, 3};
        List<Integer> list = Arrays.asList(a);
        System.out.println(list);
    }

<<<[1, 2, 3]

我们可以看下Arrays.asList的源码

    @SafeVarargs
    public static <T> List<T> asList(T... a) {
        return new Arrays.ArrayList(a);
    }

返回的List是Arrays的内部类ArrayList,并不是我们熟悉的java.util.ArrayList

private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, Serializable {
}

之所以调用add,remove,clear方法会报错,因为该类没有重写这些方法,进入他的父类AbstractList

    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }

    public E remove(int index) {
        throw new UnsupportedOperationException();
    }

难怪抛UnsupportedOperationException异常,不抛出才奇怪呢

将其转换为我们熟悉的java.util.ArrayList

1.手动转换(不推荐,典型吃力不讨好,不过可以了解底层数据结构)

​ 只说思路:获取当前List,获取其长度,新建一个等长度的Arraylist,循环遍历list,将数据移动到ArrayList里面即可

2.最简单的办法(推荐)

    public static void main(String[] args) {
        Integer[] a = {1, 2, 3};
        ArrayList<Integer> list = new ArrayList<>(Arrays.asList(a));
        list.add(4);
        System.out.println(list);
    }

<<<[1, 2, 3, 4]
    public static void main(String[] args) {
        Integer[] a = {1, 2, 3};
        List<Integer> list = Arrays.stream(a).collect(Collectors.toList());
        list.add(4);
        System.out.println(list);
    }

<<<[1, 2, 3, 4]

依赖boxed的装箱操作还可以将基本数据类型实现数据转换(本质就是将里面的每个基本数据类型封装)

    public static void main(String[] args) {
        int[] a = {1, 2, 3};
        List<Integer> list = Arrays.stream(a).boxed().collect(Collectors.toList());
        list.add(4);
        System.out.println(list);
    }

<<<[1, 2, 3, 4]

Collection.toArray方法

正好与Arrays.asList方法的功能相反

具体使用如下

    public static void main(String[] args) {
//        将数组转换成集合
        Integer[] a = {1, 2, 3};
        ArrayList<Integer> list = new ArrayList<>(Arrays.asList(a));

//        将集合转换回数组
        Integer[] array = list.toArray(new Integer[0]);
        System.out.println(Arrays.toString(array));
    }

<<<[1, 2, 3]

说明:

​ 方法原型为public <T> T[] toArray(T[] a) {}需要在参数位置传入泛型,

​ 由于 JVM 优化,new Integer[0]作为Collection.toArray()方法的参数现在使用更好

​ 根据具体数据类型传参,并不一定都是Integer

不要在foreach中进行remove/add操作

如果要进行remove操作,使用迭代器的remove方法

因为用foreach本质上就是创建了一个迭代器,以任何方式除非通过迭代器自身remove/add方法,迭代器都将抛出一个ConcurrentModificationException(并发修改异常)这就是fail-fast机制

Java.util下的所有集合类都是fail-fast的,而java.util.concurrent下的所有的类都是fail-safe的

具体事例:

    public static void main(String[] args) {
//        将数组转换成集合
        Integer[] a = {1, 2, 3};
        ArrayList<Integer> list = new ArrayList<>(Arrays.asList(a));
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()){
            Integer next = iterator.next();
            if (next==3){
                iterator.remove();
            }
        }
        System.out.println(list);
    }

<<<[1, 2]

同样来自阿里的开发手册

具体原因还没搞懂,结论先给出来

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        for (String i : list){
            if ("1".equals(i)){
                list.remove(i);
            }
        }
        System.out.println(list);
    }

<<<[2]
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        for (String i : list){
            if ("2".equals(i)){
                list.remove(i);
            }
        }
        System.out.println(list);
    }

<<<Exception in thread "main" java.util.ConcurrentModificationException
<<<	at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1042)
<<<	at java.base/java.util.ArrayList$Itr.next(ArrayList.java:996)
<<<	at cn.luckycurve.demo7.Test.main(Test.java:22)

目前已实验结论:字符串类型的ArrayList集合,用foreach删除第一个元素不会出错,删除其他元素会出错

Integer类型的ArrayList集合,用foreach删除任意一个都会出错

尽管有这样的规律,但小伙伴们在项目和工作中千万不要这么做,出个Bug改到吐血,跟着规范走~

具体的原因还没搞清楚,喜欢探索的同学可以去挖一挖ArrayList和Iterator

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值