所有分类
  • 所有分类
  • 未分类

Java-自动拆箱/装箱/实例化顺序/缓存

简介

说明

本文介绍Java的基本类型知识,包括:拆箱与装箱、实例化顺序、区域问题、缓存问题、效率问题。

 为什么基本类型需要包装类

  • 基本数据类型方便、简单、高效,但泛型不支持、集合元素不支持
  • 不符合面向对象思维
  • 包装类提供很多方法,方便使用,如 Integer 类 toHexString(int i)、parseInt(String s) 方法

Long或Integer如何比较大小?

  • 错误方法
    • 使用==。 因为Long与Ineger都是包装类型,是对象。  而不是普通类型long与int
    • 使用equals方法。因为equals方法只能比较同类型的类,例如两个都是Integer类型。
  • 正确方法
    • 先使用longValue()或intValue()方法来得到他们的基本类型的值然后使用==比较也是可以的。

拆箱与装箱原理

装箱就是将基本数据类型转化为包装类型,那么拆箱就是将包装类型转化为基本数据类型。

实例1:查看反汇编文件

package org.example.a;

public class Demo{
    public static void main(String[] args) {
        //自动装箱,底层其实执行了Integer a=Integer.valueOf(10);
        Integer a = 10;
        //自动拆箱,底层其实执行了int b=a.intValue();
        int b = a;
    }
}

生成并查看反汇编文件

依次执行如下命令

javac -d . org\example\a\Demo.java

javap -c org.example.a.Demo

输出结果如下(没有任何手动添加内容(//xxx都是自己生成的)):

E:\work\idea_proj\test_java\src>javap -c org.example.a.Demo
Compiled from “Demo.java”
public class org.example.a.Demo {
  public org.example.a.Demo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object.”<init>”:()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: bipush        10
       2: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       5: astore_1
       6: aload_1
       7: invokevirtual #3                  // Method java/lang/Integer.intValue:()I
      10: istore_2
      11: return
}

Integer a = 10; 
执行上面那句代码的时候,系统为我们执行了:Integer a = Integer.valueOf(10);

int b = a; 
执行上面那句代码的时候,系统为我们执行了:int b = a.intValue();

实例:自动拆装箱

实例1:基本类型与其包装类

package org.example.a;

public class Demo {
    public static void main(String[] args) {
        int int1 = 12;
        int int2 = 12;
        Integer integer1 = new Integer(12);
        Integer integer2 = new Integer(12);

        System.out.println("int1 == int2         : " + (int1 == int2));
        System.out.println("int1 == integer1     : " + (int1 == integer1));
        System.out.println("integer1 == integer2 : " + (integer1 == integer2));
    }
}

运行结果

int1 == int2         : true
int1 == integer1     : true
integer1 == integer2 : false

Integer是int的封装类,当Integer与int进行==比较时,Integer就会拆箱成一个int类型,所以还是相当于两个int类型进行比较,这里的Integer不管是直接赋值,还是new创建的对象,只要跟int比较就会拆箱为int类型,所以就是相等的。

实例2:自动拆箱引起的异常

首先思考,下边的程序会输出什么

package com.example.a;

public class Demo {
    public static void main(String[] args) {
        Integer a = null;
        System.out.println(2 == a);
    }
}

结果

Exception in thread "main" java.lang.NullPointerException
	at com.example.a.Demo.main(Demo.java:6)

原因:

因为是基本类型与包装类型进行比较,此时会将包装类型自动拆箱,调用a.intValue()获得其基本类型的值,但a是null,所以报空指针异常。

实例3:基本类型与Object

package org.example.a;

public class Demo {
    public static void main(String[] args) {
        Object object = true;
        System.out.println(object);
        boolean b = (boolean)object;
        System.out.println(b);
        System.out.println(b == true);
    }
}

运行结果

true
true
true

实例:实例化顺序

package org.example.a;

public class Demo {
    public static void main(String[] args) {
        int int1 = 12;
        Integer integer1 = new Integer(12);

        Integer integer2 = new Integer(34);
        int int2 = 34;

        System.out.println("int1 == integer1     : " + (int1 == integer1));
        System.out.println("int2 == integer2     : " + (int2 == integer2));
    }
}

执行结果(不影响)

int1 == integer1     : true
int2 == integer2     : true

实例:区域问题

package org.example.a;

public class Demo {
    public static void main(String[] args) {
        Integer integer1 = new Integer(12);
        Integer integer2 = 12;
        System.out.println("integer1 == integer2 : " + (integer1 == integer2));
    }
}

执行结果

integer1 == integer2 : false 

实例:缓存问题

测试代码

package org.example.a;

public class Demo{
    public static void main(String[] args) {
        Integer a = 127;
        Integer b = 127;
        Integer c = 128;
        Integer d = 128;
        System.out.println(a == b);
        System.out.println(c == d);
    }
}

执行结果

true
false

 原因

Integer有个缓存,原理如下。

 valueOf源码

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

当i的值位于[-128,127]的时候,会直接返回Integer缓存数组中相应对象的引用,如果i大于127或小于-128,会重新创建一个Integer实例,并返回。

那么第一条式子a和b的值都在缓存范围内,因此他们指向同一个对象,因此返回true。c和d的值不在范围内,都是通过new创建出来的,因此不是同一个对象,返回false。

其他包装类的缓存

Byte、Short、Integer、Long、Character的valueOf()实现机制类似。

包装类说明
Byte相同值的Byte比较永远返回true。因为byte取值范围就是[-128,127]。
Short、Integer、Long相同值在[-128,127]则返回true,不在则返回false
Character要返回true,只需保证i <= 127。因为char最小值为0,本来就大于等于-128。
Float、Double永远返回false。因为其永远返回新创建的对象,因为一个范围内的整数是有限的,但是小数却是无限的,无法保存在缓存中。
Boolean只有两个对象,要么是true的Boolean,要么是false的Boolean,只要boolean的值相同,Boolean就相等。

实例:效率问题

package org.example.a;

public class Demo {
    public static void main(String[] args) {
        long t1 = System.currentTimeMillis();
        Long sum = 0L;
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            sum += i;
        }
        long t2 = System.currentTimeMillis();
        System.out.println(t2 - t1);
    }
}

上述代码,结果为:6218

如果将sum改为long型,结果为:624

可见效率差别之大

3

评论3

请先

  1. 并且,所有类型我都做了一个实验,int最快
    543862544 2023-08-26 0
  2. 最后一句话错了,我这边亲自做了一个实验(复制的代码),long改为int,才会降低10倍
    543862544 2023-08-26 0
    • 我这边的测试结果是:Long:5888,Integer:5076,long:582,int:588,JDK版本是:jdk-8u202。按理说只要是基本类型(long、int),避免了装箱肯定会快。你是哪个版本?这四项结果分别是多少呢?
      自学精灵 2023-08-26 3
显示验证码
没有账号?注册  忘记密码?

社交账号快速登录