20191227-java中的自动拆装箱

基本数据类型

内置类型,是Java中不同于类(Class)的特殊类型

Java基本类型共有八种,基本类型可以分为三类:

字符类型 char
布尔类型 boolean
整数类型 btype\short\int\long
浮点数类型 float\double
  • Java中还存在另外一种基本类型void

对应的包装类 java.lang.Void,不过我们无法直接对它们进行操作

基本数据类型的优点:

Java语言中,new一个对象是存储在堆里的,我们通过栈中的引用来使用这些对象;所以,对象本身来说是比较消耗资源的。

  • Java提供了基本数据类型,这种数据的变量不需要使用new创建,他们不会在堆上创建,而是直接在栈内存中存储,因此会更加高效

注意: 基本数据类型超出一定的表示范围,发生溢出时并不会抛异常

包装类

包装类均位于java.lang包

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

为什么需要包装类:

为了让基本类型也具有对象的特征,就出现了包装类型,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作

拆箱与装箱

装箱:把基本数据类型转换成包装类的过程就是打包装 ,boxing

拆箱: 把包装类转换成基本数据类型的过程就是拆包装,unboxing

Java SE5之前,要进行装箱,可以通过以下代码:

Integer i = new Integer(10);

在Java SE5中,Java提供了自动拆箱与自动装箱功能

  • 自动装箱: 就是将基本数据类型自动转换成对应的包装类。
  • 自动拆箱:就是将包装类自动转换成对应的基本数据类型。
Integer i =10;  //自动装箱
int b= i;     //自动拆箱

Integer i=10 可以替代 Integer i = new Integer(10);

  • 不需要手动去new一个Integer对象

自动装箱与自动拆箱的实现原理:

自动拆装箱的代码:

public static  void main(String[]args){
    
    
    Integer integer=1; //装箱
    int i=integer; //拆箱
}

对以上代码进行反编译后可以得到以下代码:

public static  void main(String[]args){
    
    
    Integer integer=Integer.valueOf(1); 
    int i=integer.intValue(); 
}
  • int的自动装箱都是通过**Integer.valueOf()**方法来实现的
  • Integer的自动拆箱都是通过integer.intValue来实现的
自动装箱都是通过包装类的valueOf()方法来实现的

自动拆箱都是通过包装类对象的xxxValue()来实现的。

哪些地方会自动拆装箱:

  1. 将基本数据类型放入集合类
List<Integer> li = new ArrayList<>();
for (int i = 1; i < 50; i ++){
    
    
    li.add(i);
}

将上面代码进行反编译,可以得到以下代码:

List<Integer> li = new ArrayList<>();
for (int i = 1; i < 50; i += 2){
    
    
    li.add(Integer.valueOf(i));
}

当把基本数据类型放入集合类中的时候,会进行自动装箱

  1. 包装类型和基本类型的大小比较
Integer a=1;
System.out.println(a==1?"等于":"不等于");
Boolean bool=false;
System.out.println(bool?"真":"假");

对以上代码进行反编译,得到以下代码:

Integer a=1;
System.out.println(a.intValue()==1?"等于":"不等于");
Boolean bool=false;
System.out.println(bool.booleanValue?"真":"假");

包装类与基本数据类型进行比较运算,是先将包装类进行拆箱成基本数据类型,然后进行比较的

  1. 包装类型的运算
Integer i = 10;
Integer j = 20;
System.out.println(i+j);

反编译后代码如下:

Integer i = Integer.valueOf(10);
Integer j = Integer.valueOf(20);
System.out.println(i.intValue() + j.intValue());

两个包装类型之间的运算,会被自动拆箱成基本类型进行

  1. 三元运算符的使用
boolean flag = true;
Integer i = 0;
int j = 1;
int k = flag ? i : j;

反编译后代码如下:

boolean flag = true;
Integer i = Integer.valueOf(0);
int j = 1;
int k = flag ? i.intValue() : j;

这其实是三元运算符的语法规范:当第二,第三位操作数分别为基本类型和对象时,其中的对象就会拆箱为基本类型进行操作

如果这个时候i的值为null,那么就会发生NPE。(自动拆箱导致空指针异常)

例如:

public static void main(String[] args) {
    Map<String, Boolean> map = new HashMap<>();
    Boolean b = map != null ? map.get("test") : false;
    System.out.println(b);
}

==》Exception in thread "main" java.lang.NullPointerException

反编译一下该代码:

public static void main(String args[]){
   Map map = new HashMap();
   Boolean b = Boolean.valueOf(map == null ? false : ((Boolean)map.get("test")).booleanValue());
   System.out.println(b);
}

hashmap.get(“test”)->null;
(Boolean)null->null;
null.booleanValue()->报错

解决:

Map<String,Boolean> map =  new HashMap<String, Boolean>();
Boolean b = (map!=null ? map.get("test") : Boolean.FALSE);

保证了三元运算符的 第二第三位操作数都为对象类型 。这样就不会发生自动拆箱操作,以上代码得到的b的结果为null

  1. 函数参数与返回值
//自动拆箱
public int getNum1(Integer num) {
    
    
 return num;
}
//自动装箱
public Integer getNum2(int num) {
    
    
 return num;
}

自动拆装箱与缓存:

Java SE的自动拆装箱还提供了一个和缓存有关的功能

public static void main(String... strings) {
    
    

    Integer integer1 = 3;
    Integer integer2 = 3;

    if (integer1 == integer2){
    
    
        System.out.println("integer1 == integer2");
    }else{
    
    
        System.out.println("integer1 != integer2");
    }
    
    Integer integer3 = 300;
    Integer integer4 = 300;

    if (integer3 == integer4){
    
    
        System.out.println("integer3 == integer4");
    }else{
    
    
        System.out.println("integer3 != integer4");
     }
}

结果:

integer1 == integer2
integer3 != integer4

在Java中,==比较的是对象应用,而equals比较的是值

所以,在这个例子中,不同的对象有不同的引用,所以在进行比较的时候都将返回false。

但,这里两个类似的if条件判断返回不同的布尔值

原因就和Integer中的缓存机制有关

在Java 5中,在Integer的操作上引入了一个新功能来节省内存和提高性能:
整型对象通过使用相同的对象引用实现了缓存和重用

适用于整数值区间-128 至 +127。

只适用于自动装箱。使用构造函数创建对象不适用。

当需要进行自动装箱时,如果数字在-128至127之间时,会直接使用缓存中的对象,而不是重新创建一个对象

其中的javadoc详细的说明了缓存支持-128到127之间的自动装箱过程。最大值127可以通过-XX:AutoBoxCacheMax=size修改。
在Java 5中引入的时候,范围是固定的-128 至 +127。后来在Java 6中,可以通过java.lang.Integer.IntegerCache.high设置最大值

在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的值是否相等

自动拆装箱带来的问题:

  • 包装对象的数值比较,不能简单的使用==,虽然-128到127之间的数字可以,但是这个范围之外还是需要使用equals比较
  • 有些场景会进行自动拆装箱,由于自动拆箱,如果包装类对象为null,那么自动拆箱时就有可能抛出NPE
  • 如果一个for循环中有大量拆装箱操作,会浪费很多资源

一文读懂什么是Java中的自动拆装箱

你真的了解Java中的三目运算符吗?:https://mp.weixin.qq.com/s?__biz=MzI3NzE0NjcwMg==&mid=2650121204&idx=1&sn=3083f0bff6fac7c208037a90dfcce07d&chksm=f36bbed5c41c37c37284b45f7d1f52440a29377b84cbbde9796d648220665458ed638ff6d996&scene=21#wechat_redirect

猜你喜欢

转载自blog.csdn.net/fggsgnhz/article/details/103735107