文本已收录至个人GitHub仓库,欢迎Star:https://github.com/bin392328206/six-finger
种一棵树最好的时间是十年前,其次是如今
我知道不少人不玩qq了,可是怀旧一下,欢迎加入六脉神剑Java菜鸟学习群,群聊号码:549684836 鼓励你们在技术的路上写博客java
前面写过一篇基础面试的git
Object的equals方法容易抛空指针异常,应使用常量或肯定有值的对象来调用 equals。github
举个例子:web
null.equals("小六六")
复制代码
这种确定是会报NEP的,因此咱们应该把不会用空的放在前面来避免空指针异常。面试
还有一个推荐算法
Objects.equals(null,"小六六");// false
复制代码
咱们看一下java.util.Objects#equals的源码就知道缘由了。数据库
编程
public static boolean equals(Object a, Object b) { // 能够避免空指针异常。若是a==null的话此时a.equals(b)就不会获得执行,避免出现空指针异常。 return (a == b) || (a != null && a.equals(b)); } 复制代码复制代码
几点注意的点数组
Integer x = 3;
Integer y = 3;
System.out.println(x == y);// true
Integer a = new Integer(3);
Integer b = new Integer(3);
System.out.println(a == b);//false
System.out.println(a.equals(b));//true
复制代码
当使用自动装箱方式建立一个Integer对象时,当数值在-128 ~127时,会将建立的 Integer 对象缓存起来,当下次再出现该数值时,直接从缓存中取出对应的Integer对象。因此上述代码中,x和y引用的是相同的Integer对象。缓存
《阿里巴巴Java开发手册》中提到:浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用 equals 来判断。 具体原理和浮点数的编码方式有关,这里就很少提了,咱们下面直接上实例:
float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
System.out.println(a);// 0.100000024
System.out.println(b);// 0.099999964
System.out.println(a == b);// false
复制代码
具备基本数学知识的咱们很清楚的知道输出并非咱们想要的结果(精度丢失),咱们如何解决这个问题呢?一种很经常使用的方法是:使用使用 BigDecimal 来定义浮点数的值,再进行浮点数的运算操做。
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);// 0.1
BigDecimal y = b.subtract(c);// 0.1
System.out.println(x.equals(y));// true
复制代码
a.compareTo(b) : 返回 -1 表示小于,0 表示 等于, 1表示 大于。 其实你能够把他看成是 a-b 实际上是一个意思
BigDecimal a = new BigDecimal("1.0"); BigDecimal b = new BigDecimal("0.9"); System.out.println(a.compareTo(b));// 1 复制代码复制代码
BigDecimal m = new BigDecimal("1.255433"); BigDecimal n = m.setScale(3,BigDecimal.ROUND_HALF_DOWN); System.out.println(n);// 1.255 复制代码复制代码
注意:咱们在使用BigDecimal时,为了防止精度丢失,推荐使用它的 BigDecimal(String) 构造方法来建立对象。《阿里巴巴Java开发手册》对这部份内容也有提到以下图所示。
BigDecimal 主要用来操做(大)浮点数,BigInteger 主要用来操做大整数(超过 long 类型)。
BigDecimal 的实现利用到了 BigInteger, 所不一样的是 BigDecimal 加入了小数位的概念
Reference:《阿里巴巴Java开发手册》
说明 :POJO 类属性没有初值是提醒使用者在须要使用时,必须本身显式地进行赋值,任何 NPE 问题,或者入库检查,都由使用者来保证。
正例 : 数据库的查询结果多是 null,由于自动拆箱,用基本数据类型接收有 NPE 风险。
Arrays.asList()在平时开发中仍是比较常见的,咱们可使用它将一个数组转换为一个List集合。
String[] myArray = { "Apple", "Banana", "Orange" };
List<String> myList = Arrays.asList(myArray);
//上面两个语句等价于下面一条语句
List<String> myList = Arrays.asList("Apple","Banana", "Orange");
复制代码
JDK 源码对于这个方法的说明:
/** *返回由指定数组支持的固定大小的列表。此方法做为基于数组和基于集合的API之间的桥梁,与 Collection.toArray()结合使用。返回的List是可序列化并实现RandomAccess接口。 */ public static <T> List<T> asList(T... a) { return new ArrayList<>(a); } 复制代码复制代码
《阿里巴巴Java 开发手册》对其的描述
Arrays.asList()将数组转换为集合后,底层其实仍是数组,《阿里巴巴Java 开发手册》对于这个方法有以下描述:
Arrays.asList() 方法返回的并非 java.util.ArrayList ,而是 java.util.Arrays 的一个内部类,这个内部类并无实现集合的修改方法或者说并无重写这些方法。
List myList = Arrays.asList(1, 2, 3); System.out.println(myList.getClass());//class java.util.Arrays$ArrayList 复制代码复制代码
下图是java.util.Arrays$ArrayList的简易源码,咱们能够看到这个类重写的方法有哪些。
List list = new ArrayList<>(Arrays.asList("a", "b", "c"))
复制代码
Integer [] myArray = { 1, 2, 3 }; List myList = Arrays.stream(myArray).collect(Collectors.toList()); //基本类型也能够实现转换(依赖boxed的装箱操做) int [] myArray2 = { 1, 2, 3 }; List myList = Arrays.stream(myArray2).boxed().collect(Collectors.toList()); 复制代码复制代码
若是要进行remove操做,能够调用迭代器的 remove方法而不是集合类的 remove 方法。由于若是列表在任什么时候间从结构上修改建立迭代器以后,以任何方式除非经过迭代器自身remove/add方法,迭代器都将抛出一个ConcurrentModificationException,这就是单线程状态下产生的 fail-fast 机制。
在用迭代器遍历一个集合对象时,若是遍历过程当中对集合对象的内容进行了修改(增长、删除、修改),则会抛出Concurrent Modification Exception。
原理:迭代器在遍历时直接访问集合中的内容,而且在遍历过程当中使用一个 modCount 变量。集合在被遍历期间若是内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素以前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;不然抛出异常,终止遍历。
注意:这里异常的抛出条件是检测到 modCount!=expectedmodCount 这个条件。若是集合发生变化时修改modCount值恰好又设置为了expectedmodCount值,则异常不会抛出。所以,不能依赖于这个异常是否抛出而进行并发操做的编程,这个异常只建议用于检测并发修改的bug。
场景:java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程当中被修改)。
从上面咱们能够看出,只要是涉及了改变ArrayList元素的个数的方法都会致使modCount的改变。因此咱们这里能够初步判断因为expectedModCount 与modCount的改变不一样步,致使二者之间不等,从而产生fail-fast机制。
那么日常咱们如何去规避这种状况呢?这里有两种解决方案:
使用CopyOnWriteArrayList来替换ArrayList。 CopyOnWriteArrayList为何能解决这个问题呢?CopyOnWrite容器即写时复制的容器。通俗的理解是当咱们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,而后新的容器里添加元素,添加完元素以后,再将原容器的引用指向新的容器。CopyOnWriteArrayList中add/remove等写方法是须要加锁的,目的是为了不Copy出N个副本出来,致使并发写。可是。CopyOnWriteArrayList中的读方法是没有加锁的。
咱们只须要记住一句话,那就是CopyOnWriteArrayList是线程安全的,因此咱们在多线程的环境下面须要去使用这个就能够了。关于CopyOnWriteArrayList更加深刻的用法,会在之后的章节中去解释说明。
文章出自 https://snailclimb.gitee.io/ 写的很不错哦
好了各位,以上就是这篇文章的所有内容了,能看到这里的人呀,都是真粉。
创做不易,各位的支持和承认,就是我创做的最大动力,咱们下篇文章见
六脉神剑 | 文 【原创】若是本篇博客有任何错误,请批评指教,不胜感激 !