zoukankan      html  css  js  c++  java
  • Java基本数据类型和Integer缓存机制

    一、8种基本数据类型(4整,2浮,1符,1布)

    ​ 整型:byte(最小的数据类型)、short(短整型)、int(整型)、long(长整型);

    ​ 浮点型:float(浮点型)、double(双精度浮点型);

    ​ 字符型:char(字符型);

    ​ 布尔型:boolean(布尔型)。

    二、取值范围

     数据类型名称占用字节默认值最小值最大值对应包装类
    整数类型 byte 1 0 -128(-2^7)~ 127(2^7-1) Byte
    整数类型 short 2 0 -32768(-2^15)~ 32767(2^15 - 1) Short
    整数类型 int 4 0 -2,147,483,648(-2^31)~ 2,147,483,647(2^31 - 1) Integer
    整数类型 long 8 0.0L -9,223,372,036,854,775,808(-2^63)~ 9,223,372,036,854,775,807(2^63 -1) Long
    浮点类型 float 4 0.0f 1.4E-45 ~ 3.4028235E38 Float
    浮点类型 double 8 0.0 4.9E-324 ~ 1.7976931348623157E308 Double
    字符类型 char 2 u0000(即为0)~ uffff(即为65,535) Character
    布尔类型 boolean 1 flase true 和 false Boolean

    三、Integer 的缓存机制

    Integer 缓存是 Java 5 中引入的一个有助于节省内存、提高性能的特性。

    Integer的缓存机制: Integer是对小数据(-128~127)是有缓存的,再jvm初始化的时候,数据-128~127之间的数字便被缓存到了本地内存中,如果初始化-128~127之间的数字,会直接从内存中取出,不需要新建一个对象.

    首先看一个使用 Integer 的示例代码,展示了 Integer 的缓存行为。

    1. /**
    2. * 测试Integer的缓存 IntegerCache.cache
    3. */
    4. private static void testIntegerCache() {
    5. System.out.println("---int---");
    6. int a = 127, b = 127;
    7. System.out.println(a == b); //true
    8. a = 128;
    9. b = 128;
    10. System.out.println(a == b); //true
    11. System.out.println("---Integer---");
    12. Integer aa = 127, bb = 127;
    13. System.out.println(aa == bb); //true
    14. aa = 128;
    15. bb = 128;
    16. System.out.println(aa == bb); //false
    17. System.out.println(aa.equals(bb)); //true
    18. }

    在 Java 5 中,为 Integer 的操作引入了一个新的特性,用来节省内存和提高性能。整型对象在内部实现中通过使用相同的对象引用实现了缓存和重用。
    上面的规则适用于整数区间 -128 到 +127。

    Java 编译器把原始类型自动转换为封装类的过程称为自动装箱(autoboxing),这相当于调用 valueOf 方法。

    下面是 JDK 1.8.0 build 25 中的代码。

    1. /**
    2. * Returns an {@code Integer} instance representing the specified
    3. * {@code int} value. If a new {@code Integer} instance is not
    4. * required, this method should generally be used in preference to
    5. * the constructor {@link #Integer(int)}, as this method is likely
    6. * to yield significantly better space and time performance by
    7. * caching frequently requested values.
    8. *
    9. * This method will always cache values in the range -128 to 127,
    10. * inclusive, and may cache other values outside of this range.
    11. *
    12. * @param i an {@code int} value.
    13. * @return an {@code Integer} instance representing {@code i}.
    14. * @since 1.5
    15. */
    16. public static Integer valueOf(int i) {
    17. if (i >= IntegerCache.low && i <= IntegerCache.high)
    18. return IntegerCache.cache[i + (-IntegerCache.low)];
    19. return new Integer(i);
    20. }

    在创建新的 Integer 对象之前会先在 IntegerCache.cache (是个Integer类型的数组)中查找。

    IntegerCache 是 Integer 类中一个私有的静态类,负责 Integer 的缓存。

    1. /**
    2. * Cache to support the object identity semantics of autoboxing for values between
    3. * -128 and 127 (inclusive) as required by JLS.
    4. *
    5. * The cache is initialized on first usage. The size of the cache
    6. * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
    7. * During VM initialization, java.lang.Integer.IntegerCache.high property
    8. * may be set and saved in the private system properties in the
    9. * sun.misc.VM class.
    10. */
    11. private static class IntegerCache {
    12. static final int low = -128;
    13. static final int high;
    14. static final Integer cache[];
    15. static {
    16. // high value may be configured by property
    17. int h = 127;
    18. String integerCacheHighPropValue =
    19. sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
    20. if (integerCacheHighPropValue != null) {
    21. try {
    22. int i = parseInt(integerCacheHighPropValue);
    23. i = Math.max(i, 127);
    24. // Maximum array size is Integer.MAX_VALUE
    25. h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
    26. } catch( NumberFormatException nfe) {
    27. // If the property cannot be parsed into an int, ignore it.
    28. }
    29. }
    30. high = h;
    31. cache = new Integer[(high - low) + 1];
    32. int j = low;
    33. for(int k = 0; k < cache.length; k++)
    34. cache[k] = new Integer(j++);
    35. // range [-128, 127] must be interned (JLS7 5.1.7)
    36. assert IntegerCache.high >= 127;
    37. }
    38. private IntegerCache() {}
    39. }

    Javadoc 详细的说明这个类是用来实现缓存支持,并支持 -128 到 127 之间的自动装箱过程。最大值 127 可以通过 JVM 的启动参数 -XX:AutoBoxCacheMax=size 修改。 缓存通过一个 for 循环实现。从小到大的创建尽可能多的整数并存储在一个名为 cache 的整数数组中。这个缓存会在 Integer 类第一次被使用的时候被初始化出来。以后,就可以使用缓存中包含的实例对象,而不是创建一个新的实例(在自动装箱的情况下)。

    实际上在 Java 5 中引入这个特性的时候,范围是固定的 -128 至 +127。后来在 Java 6 中,最大值映射到 java.lang.Integer.IntegerCache.high,可以使用 JVM 的启动参数设置最大值。这使我们可以根据应用程序的实际情况灵活地调整来提高性能。是什么原因选择这个 -128 到 127 这个范围呢?因为这个范围的整数值是使用最广泛的。 在程序中第一次使用 Integer 的时候也需要一定的额外时间来初始化这个缓存。

    Java 语言规范中的缓存行为
    在 Boxing Conversion 部分的Java语言规范(JLS)规定如下:
    如果一个变量 p 的值属于:-128至127之间的整数(§3.10.1),true 和 false的布尔值 (§3.10.3),’u0000′ 至 ‘u007f’ 之间的字符(§3.10.4)中时,将 p 包装成 a 和 b 两个对象时,可以直接使用 a == b 判断 a 和 b 的值是否相等。
    其他缓存的对象
    这种缓存行为不仅适用于Integer对象。我们针对所有整数类型的类都有类似的缓存机制。
    有 ByteCache 用于缓存 Byte 对象
    有 ShortCache 用于缓存 Short 对象
    有 LongCache 用于缓存 Long 对象
    有 CharacterCache 用于缓存 Character 对象
    Byte,Short,Long 有固定范围: -128 到 127。对于 Character, 范围是 0 到 127。除了 Integer 可以通过参数改变范围外,其它的都不行。

    四、浮点型数据

    浮点类型是指用于表示小数的数据类型。

    单精度和双精度的区别:

    ​ 单精度浮点型float,用32位存储,1位为符号位, 指数8位, 尾数23位,即:float的精度是23位,能精确表达23位的数,超过就被截取。

    ​ 双精度浮点型double,用64位存储,1位符号位,11位指数,52位尾数,即:double的精度是52位,能精确表达52位的数,超过就被截取。

    ​ 双精度类型double比单精度类型float具有更高的精度,和更大的表示范围,常常用于科学计算等高精度场合。

    浮点数与小数的区别:

    ​ 1)在赋值或者存储中浮点类型的精度有限,float是23位,double是52位。

    ​ 2)在计算机实际处理和运算过程中,浮点数本质上是以二进制形式存在的。

    ​ 3)二进制所能表示的两个相邻的浮点值之间存在一定的间隙,浮点值越大,这个间隙也会越大。如果此时对较大的浮点数进行操作时,浮点数的精度问题就会产生,甚至出现一些“不正常”的现象。

    为什么不能用浮点数来表示金额

    先给出结论:金额用BigDecimal

    1)精度丢失问题

    ​ 从上面我们可以知道,float的精度是23位,double精度是63位。在存储或运算过程中,当超出精度时,超出部分会被截掉,由此就会造成误差。

    ​ 对于金额而言,舍去不能表示的部分,损失也就产生了。

    32位的浮点数由3部分组成:1比特的符号位,8比特的阶码(exponent,指数),23比特的尾数(Mantissa,尾数)。这个结构会表示成一个小数点左边为1,以底数为2的科学计数法表示的二进制小数。浮点数的能表示的数据大小范围由阶码决定,但是能够表示的精度完全取决于尾数的长度。long的最大值是2的64次方减1,需要63个二进制位表示,即便是double,52位的尾数也无法完整的表示long的最大值。不能表示的部分也就只能被舍去了。对于金额,舍去不能表示的部分,损失也就产生了。
      了解了浮点数表示机制后,丢失精度的现象也就不难理解了。但是,这只是浮点数不能表示金额的原因之一。还有一个深刻的原因与进制转换有关。十进制的0.1在二进制下将是一个无线循环小数。

    1. public class MyTest {
    2. public static void main(String[] args) {
    3. float increment = 0.1f;
    4. float expected = 1;
    5. float sum = 0;
    6. for (int i = 0; i < 10; i++) {
    7. sum += increment;
    8. System.out.println(sum);
    9. }
    10. if (expected == sum) {
    11. System.out.println("equal");
    12. } else {
    13. System.out.println("not equal ");
    14. }
    15. }
    16. }

    输出结果:

    1. 0.1
    2. 0.2
    3. 0.3
    4. 0.4
    5. 0.5
    6. 0.6
    7. 0.70000005
    8. 0.8000001
    9. 0.9000001
    10. 1.0000001
    11. not equal

    2)进制转换误差

    ​ 从上面我们可以知道,在计算机实际处理和运算过程中,浮点数本质上是以二进制形式存在的。

    ​ 而十进制的0.1在二进制下将是一个无限循环小数,这就会导致误差的出现。

    ​ 如果一个小数不是2的负整数次幂,用浮点数表示必然产生浮点误差。

    ​ 换言之:A进制下的有限小数,转换到B进制下极有可能是无限小数,误差也由此产生。

    ​ 浮点数不精确的根本原因在于:尾数部分的位数是固定的,一旦需要表示的数字的精度高于浮点数的精度,那么必然产生误差!

    ​ 解决这个问题的方法是BigDecimal的类,这个类可以表示任意精度的数字,其原理是:用字符串存储数字,转换为数组来模拟大数,实现两个数组的数学运算并将结果返回。

    BigDecimal的使用要点:

    ​ 1、BigDecimal变量初始化——必须用传入String的构造方法

    1. `BigDecimal num1 = ``new` `BigDecimal(0.005);``//用数值转换成大数,有误差``BigDecimal num12 = ``new` `BigDecimal(``"0.005"``);``//用字符串转换成大数,无误差`

    因为:不是所有的浮点数都能够被精确的表示成一个double 类型值,有些浮点数值不能够被精确的表示成 double 类型值时,它会被表示成与它最接近的 double 类型的值,此时用它来初始化一个大数,会“先造成了误差,再用产生了误差的值生成大数”,也就是“将错就错”。

    ​ 2、使用除法函数在divide的时候要设置各种参数,要精确的小数位数和舍入模式,其中有8种舍入模式:

    1. 1ROUND_UP
    2. 远离零的舍入模式。
    3. 在丢弃非零部分之前始终增加数字(始终对非零舍弃部分前面的数字加1)。
    4. 注意,此舍入模式始终不会减少计算值的大小。
    5. 2ROUND_DOWN
    6. 接近零的舍入模式。
    7. 在丢弃某部分之前始终不增加数字(从不对舍弃部分前面的数字加1,即截短)。
    8. 注意,此舍入模式始终不会增加计算值的大小。
    9. 3ROUND_CEILING
    10. 接近正无穷大的舍入模式。
    11. 如果 BigDecimal 为正,则舍入行为与 ROUND_UP 相同;
    12. 如果为负,则舍入行为与 ROUND_DOWN 相同。
    13. 注意,此舍入模式始终不会减少计算值。
    14. 4ROUND_FLOOR
    15. 接近负无穷大的舍入模式。
    16. 如果 BigDecimal 为正,则舍入行为与 ROUND_DOWN 相同;
    17. 如果为负,则舍入行为与 ROUND_UP 相同。
    18. 注意,此舍入模式始终不会增加计算值。
    19. 5ROUND_HALF_UP
    20. 向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。
    21. 如果舍弃部分 >= 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同。
    22. 注意,这是我们大多数人在小学时就学过的舍入模式(四舍五入)。
    23. 6ROUND_HALF_DOWN
    24. 向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为上舍入的舍入模式。
    25. 如果舍弃部分 > 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同(五舍六入)。
    26. 7ROUND_HALF_EVEN
    27. 向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。
    28. 如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP 相同;
    29. 如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。
    30. 注意,在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。
    31. 此舍入模式也称为“银行家舍入法”,主要在美国使用。
    32. 如果前一位为奇数,则入位,否则舍去。
    33. 以下例子为保留小数点1位,那么这种舍入方式下的结果。
    34. 1.15>1.2 1.25>1.2
    35. 8ROUND_UNNECESSARY
    36. 断言请求的操作具有精确的结果,因此不需要舍入。
    37. 如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException

    经典面试题

    1、short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 +=1;有什么错?

    答:对于short s1=1;s1=s1+1来说,在s1+1运算时会自动提升表达式的类型为int,那么将int赋予给short类型的变量s1会出现类型转换错误。

    对于short s1=1;s1+=1来说 +=是java语言规定的运算符,java编译器会对它进行特殊处理,因此可以正确编译。

    2、char类型变量能不能储存一个中文的汉子,为什么?

    char类型变量是用来储存Unicode编码的字符的,unicode字符集包含了汉字,所以char类型当然可以存储汉字的,还有一种特殊情况就是某个生僻字没有包含在unicode编码字符集中,那么就char类型就不能存储该生僻字。

    3、Integer和int的区别

    int是java的8种内置的原始数据类型。Java为每个原始类型都提供了一个封装类,Integer就是int的封装类。

    int变量的默认值为0,Integer变量的默认值为null,这一点说明Integer可以区分出未赋值和值为0的区别,比如说一名学生没来参加考试,另一名学生参加考试全答错了,那么第一名考生的成绩应该是null,第二名考生的成绩应该是0分。关于这一点Integer应用很大的。Integer类内提供了一些关于整数操作的一些方法,例如上文用到的表示整数的最大值和最小值。

    4、switch语句能否作用在byte上,能否作用在long上,能否作用在string上?

    byte的存储范围小于int,可以向int类型进行隐式转换,所以switch可以作用在byte上

    long的存储范围大于int,不能向int进行隐式转换,只能强制转换,所以switch不可以作用在long上

    string在1.7版本之前不可以,1.7版本之后switch就可以作用在string上了

    5.是否存在 x>x+1?为什么?

    这就是临界值,当x=最大值 时; 再加1(根据二进制运算+1)就超过了它的临界值,刚好会是它最小值。

    举个例子吧,byte 8位, -128 ~ 127

    127 二进制: 0111 1111

    1 二进制 : 0000 0001

    相加结果: 1000 0000

    byte 8位 有符号, 1000 0000 刚好 为 -128

    Reference

    java 基础—8 种基本数据类型:整型、浮点型、布尔型、字符型 整型中 byte、short、int、long 的取值范围 什么是浮点型?什么是单精度和双精度?为什么不能用浮点型表示金额?

    理解Java Integer的缓存策略

  • 相关阅读:
    IOC和DI的区别
    hdu 1217(Floyed)
    hdu 2112(字典树+最短路)
    hdu 4081(次小生成树)
    hdu 1811(缩点+拓扑排序+并查集)
    poj 3026(BFS+最小生成树)
    hdu 3635(并查集)
    hdu 3047(扩展并查集)
    hdu 1116(并查集+欧拉路径)
    poj 1679(次小生成树)
  • 原文地址:https://www.cnblogs.com/brewin/p/12681625.html
Copyright © 2011-2022 走看看