Java 基础知识(一些需要注意的点)
前言
文本已收录至我的 GitHub 仓库,欢迎 Star:https://github.com/bin392328206/six-finger
种一棵树最好的时间是十年前,其次是现在
絮叨
前面写过一篇基础面试的
正确使用 equals 方法
Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals。
举个例子:
这种肯定是会报 NEP 的,所以我们应该把不会用空的放在前面来避免空指针异常。
还有一个推荐
我们看一下 java.util.Objects#equals 的源码就知道原因了。
几点注意的点
每种原始类型都有默认值一样,如 int 默认值为 0,boolean 的默认值为 false,null 是任何引用类型的默认值,不严格的说是所有 - Object 类型的默认值。
可以使用 == 或者 != 操作来比较 null 值,但是不能使用其他算法或者逻辑操作。在 Java 中 null == null 将返回 true。
不能使用一个值为 null 的引用类型变量来调用非静态方法,否则会抛出异常
整型包装类值的比较
当使用自动装箱方式创建一个 Integer 对象时,当数值在-128 ~127 时,会将创建的 Integer 对象缓存起来,当下次再出现该数值时,直接从缓存中取出对应的 Integer 对象。所以上述代码中,x 和 y 引用的是相同的 Integer 对象。
BigDecimal
BigDecimal 的用处
《阿里巴巴 Java 开发手册》中提到:浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用 equals 来判断。 具体原理和浮点数的编码方式有关,这里就不多提了,我们下面直接上实例:
具有基本数学知识的我们很清楚的知道输出并不是我们想要的结果(精度丢失),我们如何解决这个问题呢?一种很常用的方法是:使用使用 BigDecimal 来定义浮点数的值,再进行浮点数的运算操作。
BigDecimal 的大小比较
a.compareTo(b) : 返回 -1 表示小于,0 表示 等于, 1 表示 大于。 其实你可以把他当作是 a-b 其实是一个意思
BigDecimal 保留几位小数
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 集合。
JDK 源码对于这个方法的说明:
《阿里巴巴 Java 开发手册》对其的描述
Arrays.asList()将数组转换为集合后,底层其实还是数组,《阿里巴巴 Java 开发手册》对于这个方法有如下描述:
Arrays.asList() 方法返回的并不是 java.util.ArrayList ,而是 java.util.Arrays 的一个内部类,这个内部类并没有实现集合的修改方法或者说并没有重写这些方法。
下图是 java.util.Arrays$ArrayList 的简易源码,我们可以看到这个类重写的方法有哪些。
如何正确的将数组转换为 ArrayList?
最简便的方法(推荐)
使用 Java8 的 Stream(推荐)
不要在 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/ 写的很不错哦
日常求赞
好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是真粉。
创作不易,各位的支持和认可,就是我创作的最大动力,我们下篇文章见
六脉神剑 | 文 【原创】如果本篇博客有任何错误,请批评指教,不胜感激 !
版权声明: 本文为 InfoQ 作者【自然】的原创文章。
原文链接:【http://xie.infoq.cn/article/f83feffac4060ec77d38cac2e】。文章转载请联系作者。
评论