今天来聊聊Java中跟数值处理相关的两个类型Integer和BigDecimal。 说起这两个类型,我们肯定都不陌生,但是其中有些容易踩到的坑需要注意避让。

Integer

整型我们应该每天都会用到,但是每种语言还是有自己的特性。从敬姐刚从.NET转过来的时候踩过的一个坑说起:话说在.NET世界中,数值的基本类型和包装类型是会自动转换的,所以数值比较很自然地就会使用 a==b,但是到java这却行不通了,顿时一脸懵。

数值比较及自动装箱

    @Test
public void Interger(){
Integer x = 127;
Integer y = 127;
Integer m = 99999;
Integer n = 99999;
System.out.println("x == y: " + (x==y));
System.out.println("m == n: " + (m==n));
System.out.println("x.equals(y): " + x.equals(y));
System.out.println("m.equals(n): " + m.equals(n));
//执行结果
// x == y: true
// m == n: false
// x.equals(y): true
// m.equals(n): true
}

仔细观察可以发现,==比较,较小的两个相同的Integer返回true,较大的两个相同的Integer返回false,这是为何呢?

一起看一下Integer类的源码,发现其中的IntegerCache类。这是Java为了节省空间、提升性能采取的优化机制,常量池的大小为一个字节(-128~127)。

private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer[] cache;
static Integer[] archivedCache; static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
h = Math.max(parseInt(integerCacheHighPropValue), 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h; // Load IntegerCache.archivedCache from archive, if possible
CDS.initializeFromArchive(IntegerCache.class);
int size = (high - low) + 1; // Use the archived cache if it exists and is large enough
if (archivedCache == null || size > archivedCache.length) {
Integer[] c = new Integer[size];
int j = low;
for(int i = 0; i < c.length; i++) {
c[i] = new Integer(j++);
}
archivedCache = c;
}
cache = archivedCache;
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
} private IntegerCache() {}
}

而对于valueOf(int i)方法,直接使用了常量池IntegerCache

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

所以当遇到 Integer x = 127; 时,会进行自动装箱,调用的是:

Integer x = Integer.valueOf(127);

为了节省内存,Integer.valueOf()对于较小的数,始终返回相同的实例,因此,比较“恰好”为true。但我们绝不能因为Java标准库的Integer内部有缓存优化就用比较,必须用equals()方法比较两个Integer。

创建实例

因为Integer.valueOf()可能始终返回同一个Integer实例,因此,在我们自己创建Integer的时候,以下两种方法:

方法1:Integer n = new Integer(100);
方法2:Integer n = Integer.valueOf(100);

方法2更好,因为方法1总是创建新的Integer实例,方法2把内部优化留给Integer的实现者去做,即使在当前版本没有优化,也有可能在下一个版本进行优化。

我们把能创建“新”对象的静态方法称为静态工厂方法。Integer.valueOf()就是静态工厂方法,它尽可能地返回缓存的实例以节省内存。简而言之:创建新对象时,优先选用静态工厂方法而不是new操作符。

上个看个有点特别的例子:

@Test
public void instance() {
//每次创建一个新实例
Integer a1 = new Integer(100);
Integer a2 = new Integer(100);
Assert.assertFalse(a1 == a2);
//add
Integer a3 = new Integer(200);
//注意这里喽!!
Assert.assertTrue(a1 + a2 == 200);
Assert.assertTrue(a1 + a2 == a3);
}

因为+这个操作符不适用于 Integer 对象,首先 a1 和 a2 进行自动拆箱操作,进行数值相加,即a3 == 40。

BigDecimal

BigDecimal适合商业计算场景,用来对超过16位有效位的数进行精确的运算。

Double转换为BigDecimal

我们在使用BigDecimal时,为了防止精度丢失,推荐使用它的 BigDecimal(String) 构造方法来创建对象。

    @Test
public void double2decimal() {
Double d = 0.1d;
System.out.println(new BigDecimal(d));//0.1000000000000000055511151231257827021181583404541015625
System.out.println(new BigDecimal(d.toString()));//0.1
System.out.println(BigDecimal.valueOf(d));//0.1
}

保留几位小数

通过 setScale方法设置保留几位小数以及保留规则。

@Test
public void decimalTest() {
BigDecimal a = new BigDecimal("1.2345");
System.out.println(a.toString());
//BigDecimal保留几位小数
BigDecimal b = a.setScale(3, RoundingMode.HALF_DOWN);
System.out.println(b.toString());
}

BigDecimal 值比较

BigDecimal的等值比较应该使用compareTo()方法,而不是equals()方法。

/**
* BigDecimal等值比较
* equals:既比较数值,又比较精度;
* compareTo:仅比较数值
*/
@Test
public void compare() {
BigDecimal a = BigDecimal.valueOf(1);
BigDecimal b = BigDecimal.valueOf(1.00);
Assert.assertFalse(a.equals(b));
Assert.assertEquals(0, a.compareTo(b));
}

调试一下BigDecimal的equals和compareTo方法,发现equals()方法会比较精度,但是compare()方法不会。



BigDecimal 除法

BigDecimal.divide(),除法运算注意要设置精度,否则在除不尽的情况下会抛异常。

    @Test
public void divide(){
BigDecimal a=BigDecimal.valueOf(1);
BigDecimal b=BigDecimal.valueOf(3);
//直接抛异常
// System.out.println(a.divide(b));
//正常返回 0.3333
System.out.println(a.divide(b,4,RoundingMode.HALF_EVEN));
}

代码示例

文示例代码参考:jing-yes-java (https://github.com/cathychen00/jing-yes-java/tree/master/jing-yes-j2se/src/test/java/com/jingyes/j2se/tests)

原来你是这样的JAVA--[07]聊聊Integer和BigDecimal的更多相关文章

  1. 死磕Java之聊聊HashSet源码(基于JDK1.8)

    HashSet的UML图 HashSet的成员变量及其含义 public class HashSet<E> extends AbstractSet<E> implements ...

  2. 死磕Java之聊聊HashMap源码(基于JDK1.8)

    死磕Java之聊聊HashMap源码(基于JDK1.8) http://cmsblogs.com/?p=4731 为什么面试要问hashmap 的原理

  3. 【转】java int与integer的区别

    java int与integer的区别 int与integer的区别从大的方面来说就是基本数据类型与其包装类的区别: int 是基本类型,直接存数值,而integer是对象,用一个引用指向这个对象 1 ...

  4. Java进阶(三十五)java int与integer的区别

    Java进阶(三十五)java int与Integer的区别 前言 int与Integer的区别从大的方面来说就是基本数据类型与其包装类的区别: int 是基本类型,直接存数值,而Integer是对象 ...

  5. Java中的Integer和int

    Java中的Integer是引用类型,而int是基本类型.Integer是int的包装器类型. java中的基本类型有布尔类型boolean;字符类型char;整数类型byte,int,long,sh ...

  6. java int and Integer

    本文转自:https://www.cnblogs.com/guodongdidi/p/6953217.html int和Integer的区别 1.Integer是int的包装类,int则是java的一 ...

  7. mybatis 在存储Integer、bigdecimal等java数据类型时,将0存成null

    我们的项目中,有关于金额的计算,所以,一般在java环境中我们使用bigdecimal来做运算和存储金额信息.数据库sqlServer2008用的float类型 问题是,当我将金额赋值成0时,很意外的 ...

  8. (转)Java进阶java int与Integer的区别

    Java进阶java int与Integer的区别 前言 int与Integer的区别从大的方面来说就是基本数据类型与其包装类的区别: int 是基本类型,直接存数值,而Integer是对象,用一个引 ...

  9. Java学习——BigInteger类和BigDecimal类

    Java学习——BigInteger类和BigDecimal类 摘要:本文主要学习了用于大数字运算的BigInteger类和BigDecimal类. 部分内容来自以下博客: https://www.c ...

  10. Java int和Integer包装类的区别和比较

    区别:                                                                                                  ...

随机推荐

  1. Chrony 的学习与使用

    Chrony 的学习与使用 背景 之前捯饬 ntp 发现很麻烦, 经常容易弄错了. 昨天处理文件精确时间时 想到了时间同步. 发现只有自己总结的ntpdate 但是还没有 chronyd相关的总结 本 ...

  2. [转帖]Linux常用的一些命令,看你知道多少?

    https://zhuanlan.zhihu.com/p/115279009 Linux中命令有很多,而Linux系统中使用命令也是它的一大特点.在Linux系统中使用命令处理问题灵活,高效,所以熟知 ...

  3. [转帖]Linux系统中的Page cache和Buffer cache

    Free命令显示内存 首先,我们来了解下内存的使用情况: Mem:表示物理内存统计 total:表示物理内存总量(total = used + free) used:表示总计分配给缓存(包含buffe ...

  4. Systemd设置ulimit的方式与方法

    Systemd设置ulimit的方式与方法 摘要 Linux安装完成之后前面几件事情一般是处理selinux 以及处理ulimit 其实处理文件打开数有多种方法,之前也总结过, 但是最近因为syste ...

  5. mysql 获取 今天是今年的第几天, 以及 还有多少天元旦的方法

    1. 获取今天是这一年的第几天 select dayofyear(curdate()); 或者是 select dayofyear(now()); 2. 获取还有多少天元旦的方法 select dat ...

  6. 兄弟组件互相传递值-this.$bus.$emit与this.$bus.$on

    B组件向C组件传递一个值. 一种组件间通信的方式, 适用于任意的组件间通信. 适用于任意的组件间通信. 适用于任意的组件间通信. 通过this.$bus.$emit('事件名',数据)进行提供数据 通 ...

  7. 【JS 逆向百例】HN某服务网登录逆向,验证码形同虚设

    声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 逆向目标 目标:某政务服务 ...

  8. 补发一篇tensorflow下MADDPG环境搭建配置

    相关文章: [一]MADDPG-单智能体|多智能体总结(理论.算法) [二]MADDPG多智能体算法实现(parl)[追逐游戏复现] 之前用的都是paddle,现在补一下tf的. github上ope ...

  9. 使用C++实现Range序列生成器

    在C++编程中,经常需要迭代一系列数字或其他可迭代对象.通常,这需要编写复杂的循环结构,但有一种精妙的方法可以使这一过程变得更加简单和可读.如果你使用过Python语言那么一定对Range语句非常的数 ...

  10. 7.3 通过API枚举进程

    首先实现枚举当前系统中所有进程信息,枚举该进程的核心点在于使用CreateToolhelp32Snapshot()函数,该函数用于创建系统进程和线程快照,它可以捕获当前系统中进程和线程相关的信息(如P ...