JAVA自动拆箱装箱

1,236 阅读5分钟

1. 数据类型

  1. 为什么会有基本数据类型
  • 在java中new一个对象是存储在堆里的,对于我们经常操作的数据类型,每次创建对象这样太消耗资源,因此java提供了8个基本数据类型,存储在栈里。用起来更方便。
  1. 有了基本数据类型为什么还要有包装类型
  • java是一个面向对象的编程语言,基本类型并不具有对象的性质,为了让基本类型也具有对象的特性,就出现了包装类型,它将基本类型包装起来,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。
  • 当需要往集合(如ArrayList)中存放数据时,基本类型是放不进去的,因为容器都是装object的,这时就需要包装类型了。

2. 基本类型和包装类型的区别

  • 声明方式不同:基本类型不使用new关键字,而包装类型需要使用new关键字来在堆中分配存储空间
  • 存储方式及位置不同:基本类型是直接将变量值存储在栈中,而包装类型是将对象放在堆中,然后通过引用来使用
  • 初始值不同:基本类型的初始值如int为0,boolean为false,而包装类型的初始值为null
  • 使用方式不同:基本类型直接赋值直接使用就好,而包装类型在集合如Collection、Map时会使用到

3. 数据基础知识回顾

计算机中都是使用二进制的补码进行运算的,首先,我们知道1字节=8bit,那么8bit可以表示的数字范围是[-128,127]。注意首位的1表示负数,0表示正数

1000 0000:-128 或 -2^7-1
0111 1111: 127 或 2^7-1

4. 类型转换的条件

  • 转换前的数据类型的位数低于转换后的数据类型
byte,short,char—> int —> long—> float —> double 
  • 不能对boolean进行类型转换
  • 不能把对象类型转换成不相关的对象
  • 由大到小会丢失精度:在把容量大的类型转换为容量小的类型时必须使用强制类型转换
    • 条件是转换的数据类型必须是兼容的。
    • 格式:(type)value type是要强制类型转换后的数据类型
  • 转换过程中可能导致溢出或损失精度,例如:
int i =128;   
byte b = (byte)i;//-128

// 因为 byte 类型是 8 位,最大值为127,所以当 int 强制转换为 byte 类型时,值 128 时候就会导致溢出。
  • 浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入,例如:
(int)23.7 == 23;        
(int)-45.89f == -45

5. 基本类型和包装类对应关系

8种基本数据类型对应包装类:可以看到除了int和char类型,其它都是首字母大写

基本数据类型 包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean

6. 自动装箱和拆箱 autoBoxing and unBoxing

  • 定义:
    • 装箱就是自动将基本数据类型转为包装类型 Integer integer = Integer.valueOf(1)
    • 拆箱就是自动将包装类型转为基本数据类型 int i = integer.intValue()
      image
  • 装箱的过程会创建对应的对象,这个会消耗内存,所以装箱的过程会增加内存的消耗,影响性能
  1. int类型
public static Integer valueOf(int i) {
    return  i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];
}
  • 如果i小于-128或者大于等于128,就创建一个Integer对象,否则执行SMALL_VALUES[i + 128]
private static final Integer[] SMALL_VALUES = new Integer[256];
  • SMALL_VALUES本来已经被创建好,也就是说在i >= 128 || i < -128是会创建不同的对象,在i < 128 && i >= -128会根据i的值返回已经创建好的指定的对象
  1. 在Double里面的做法很直接,就是直接创建一个对象,所以每次创建的对象都不一样。
public static Double valueOf(double d) {
    return new Double(d);
}

image
3. 布尔类型

public class Main {
    public static void main(String[] args) {
        Boolean i1 = false;
        Boolean i2 = false;
        Boolean i3 = true;
        Boolean i4 = true;

        System.out.println(i1==i2);//true
        System.out.println(i3==i4);//true
    }
}
public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE;
}

可以看到返回的都是true,也就是它们执行valueOf返回的都是相同的对象

public static final Boolean TRUE = new Boolean(true);

public static final Boolean FALSE = new Boolean(false)

可以看到它并没有创建对象,因为在内部已经提前创建好两个对象,因为它只有两种情况,这样也是为了避免重复创建太多的对象

7. 类型间转换

  1. 基本数据类型转String类`
    • String类的valueOf()方法:String fs = String.valueOf(2.34f);
    • 更直接的方式5+""
  2. String类转基本数据类型
    • 通过包装类的构造器实现
      int a = new Integer("1");
      int a = new Integer("aa"); // NumberFormatException
      
    • 调用包装类的转换静态方法
      Integer a = Integer.parseInt("1");
      
  3. 包装类转String类型
    • 包装类对象的toString方法
      Integer a = new Integer(1);
      String str = a.toString();
      
    • 包装类的toString方法
      String s = Integer.toString(1);
      
  4. String类转包装类
    • 通过字符串参数
      Integer a = new Integer("1");
      

8. Integer 和 int 之间的比较

int 和 Integer在进行比较的时候,Integer会进行拆箱,转为int值与int进行比较

Integer i1 = 1;
int i2 = 1;
System.out.println(i1 == i2); //true

Integer i3 = 128;
int i4 = 128;
System.out.println(i2 == i3); //true

9. Integer与Integer比较

Integer 直接赋值的时候会进行自动装箱

  • -128<= x<=127的整数,将会直接缓存在IntegerCache中,那么当赋值在这个区间的时候,不会创建新的Integer对象,而是从缓存中获取已经创建好的Integer对象
  • 大于这个范围的时候,直接new Integer来创建Integer对象

Integer i1 = 1;
Integer i2 = 1;
System.out.println(i1 == i2); // true

Integer i3 = 128;
Integer i4 = 128;
System.out.println(i3 == i4); // false

Integer i5 = new Integer(127);
Integer i6 = 127;
System.out.println(i5 == i6); // false
// i6进行自动装箱使用的是IntegerCache中的对象,i5是自己new的对象,地址不同

Integer i7 = new Integer(128);
Integer i8 = 128;
System.out.println(i7 == i8); // false
// i7,i8都是自己创建的新的对象,在堆中的地址不同