Java基础知识(一些须要注意的点)

前言

文本已收录至个人GitHub仓库,欢迎Star:https://github.com/bin392328206/six-finger
种一棵树最好的时间是十年前,其次是如今
我知道不少人不玩qq了,可是怀旧一下,欢迎加入六脉神剑Java菜鸟学习群,群聊号码:549684836 鼓励你们在技术的路上写博客java

絮叨

前面写过一篇基础面试的git

正确使用 equals 方法

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)); } 复制代码复制代码

几点注意的点数组

  • 每种原始类型都有默认值同样,如int默认值为 0,boolean 的默认值为 false,null 是任何引用类型的默认值,不严格的说是全部 - Object 类型的默认值。
  • 可使用 == 或者 != 操做来比较null值,可是不能使用其余算法或者逻辑操做。在Java中null == null将返回true。
  • 不能使用一个值为null的引用类型变量来调用非静态方法,不然会抛出异常

整型包装类值的比较

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对象。缓存

BigDecimal

BigDecimal 的用处

《阿里巴巴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 
复制代码

BigDecimal 的大小比较

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 保留几位小数

BigDecimal m = new BigDecimal("1.255433"); BigDecimal n = m.setScale(3,BigDecimal.ROUND_HALF_DOWN); System.out.println(n);// 1.255 复制代码复制代码

BigDecimal 的使用注意事项

注意:咱们在使用BigDecimal时,为了防止精度丢失,推荐使用它的 BigDecimal(String) 构造方法来建立对象。《阿里巴巴Java开发手册》对这部份内容也有提到以下图所示。

总结

BigDecimal 主要用来操做(大)浮点数,BigInteger 主要用来操做大整数(超过 long 类型)。

BigDecimal 的实现利用到了 BigInteger, 所不一样的是 BigDecimal 加入了小数位的概念

基本数据类型与包装数据类型的使用标准

Reference:《阿里巴巴Java开发手册》

  • 【强制】全部的 POJO 类属性必须使用包装数据类型。
  • 【强制】RPC 方法的返回值和参数必须使用包装数据类型。
  • 【推荐】全部的局部变量使用基本数据类型。 好比咱们若是自定义了一个Student类,其中有一个属性是成绩score,若是用Integer而不用int定义,一次考试,学生可能没考,值是null,也可能考了,但考了0分,值是0,这两个表达的状态明显不同.

说明 :POJO 类属性没有初值是提醒使用者在须要使用时,必须本身显式地进行赋值,任何 NPE 问题,或者入库检查,都由使用者来保证。

正例 : 数据库的查询结果多是 null,由于自动拆箱,用基本数据类型接收有 NPE 风险。

Arrays.asList()使用指南

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的简易源码,咱们能够看到这个类重写的方法有哪些。

如何正确的将数组转换为ArrayList?

  • 最简便的方法(推荐)
List list = new ArrayList<>(Arrays.asList("a", "b", "c"))
复制代码
  • 使用 Java8 的Stream(推荐)

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()); 复制代码复制代码

不要在 foreach 循环里进行元素的 remove/add 操做

若是要进行remove操做,能够调用迭代器的 remove方法而不是集合类的 remove 方法。由于若是列表在任什么时候间从结构上修改建立迭代器以后,以任何方式除非经过迭代器自身remove/add方法,迭代器都将抛出一个ConcurrentModificationException,这就是单线程状态下产生的 fail-fast 机制。

聊聊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/ 写的很不错哦

平常求赞

好了各位,以上就是这篇文章的所有内容了,能看到这里的人呀,都是真粉

创做不易,各位的支持和承认,就是我创做的最大动力,咱们下篇文章见

六脉神剑 | 文 【原创】若是本篇博客有任何错误,请批评指教,不胜感激 !

相关文章
相关标签/搜索