目录
一、Java的基本数据类型了解吗?
二、基本类型和包装类型的区别
三、包装类型的缓存机制了解吗?
四、自动拆箱和自动装箱了解吗?
五、为什么浮点数在运算的时候会有精度损失的风险?
六、如何解决浮点数在运算时存在的精度损失问题?
七、超过long的数据如何保存
数值型:
整数型:byte、short、int、long
浮点型:float、double
布尔型:true、false
字符型:char
那么各种类型所占字节,能表示的范围了解吗?
byte:1 byte -128 - 127
short: 2 byte -2^15 - 2^15-1
int : 4byte -2^31 - 2^31-1
long : 8byte -2^63 - 2^63-1
布尔型的话 一般一个字节 但可能JVM会存在优化不一定是一个字节
char : 2byte 0 - 2^16-1
五个方面
1. 就用途来说
包装类符合面向对象的特征 可以用于泛型 里面可以定义属性和方法来更好的操作数据
2. 存储上
基本类型基本都存在栈上面 当然如果是成员变量非static的就存在于堆上 如果是static的就存在于方法区中
而对应包装类型 因为是对象类型 那么都基本存在于堆中 即使方法的形参是包装类型与基本类型有些区别
3. 占有内存大小
基本类型一般占用空间较小,而包装类型则占用空间较大
4. 默认值
基本类型的默认值有多种 而包装类型的默认值就为null
5. 比较方式
基本类型可以使用==进行比较 而包装类型需要使用 equals方法进行比较
对于一些数值型的包装类型存在缓存机制
其基本作用是为了提高性能,避免重复创建对象
比如说 Byte、Short、Integer、Long都有其缓存值 (-128 - 127)
Character (0 - 127)
注意:
1. Float和Double没有实现缓存机制
2. 对于其包装类型的比较使用equals方法进行比较
例子:
Integer i1 = 40;
Integer i2 = new Integer(40);
System.out.println(i1==i2);
这个的结果返回 false 原因是因为上面是在缓存的范围内 可以直接使用缓存对象 而下面是用的自己new出来的 全新的 因此不一样
而如果改成下面这样:
Integer i1 = 40;
Integer i2 = 40;
System.out.println(i1 == i2);// 输出 true
结果为true 这是因为都是缓存对象
这也就告诉我们其实也就是使用 上面的那一个例子使用 ==比较是正确的,其他都不行
因此包装类之间的比较不要使用==进行比较 而需要使用equals进行比较
自动拆箱和自动装箱 是包装类和基本类型之间的相互转换
拆箱的底层是调用 对象.xxxValue()方法 Integer i = new Integer(3) int i1= i.intValue()
装箱的底层是调用 XXX.valueOf方法 Integer.valueOf(123)
因为这是底层在存储浮点数的时候,只能将得到的二进制进行截取
就比如说下面这个例子:
// 0.2 转换为二进制数的过程为,不断乘以 2,直到不存在小数为止,
// 在这个计算过程中,得到的整数部分从上到下排列就是二进制的结果。
0.2 * 2 = 0.4 -> 0
0.4 * 2 = 0.8 -> 0
0.8 * 2 = 1.6 -> 1
0.6 * 2 = 1.2 -> 1
0.2 * 2 = 0.4 -> 0(发生循环)
...
发生了循环,所以没办法只能截断 而截断的后果就是精度的损失
使用 BigDecimal这个类来进行运算
使用它需要注意以下几点:
1. 创建对象不要使用 double构造器的方式 而是使用 字符串构造器的方式或者是 BigDecimal.valueOf()的方式 底层调用了Double的toString方法,对double实际能表达的精度值进行了截断
2. 比较不要使用equals方法
因为这个还会比较精度 0.1 和 0.10结果为false 应该使用compareTo方法
3. 对于将浮点数进行保留几位小数
可以使用setScale方法 一般来说保留的规则使用 BigDecimal.ROUND_HALF_UP规则 这是四舍六入五看奇偶规则 5的前一位是偶数那么就入
另外还有常见的几种规则:
ROUND_UP 就是常规的四舍五入 ROUND_DOWN 就是取小的 负数还是取大的
CEILING 天花板 反正取大的 FLOOR 反正取小的
使用BigInteger 但是其效率会相对较低