精华内容
下载资源
问答
  • 面试官:兄弟,说说基本类型和包装类型的区别吧

    万次阅读 多人点赞 2019-09-29 08:36:07
    Java 的每个基本类型都对应了一个包装类型,比如说 int 的包装类型为 Integer,double 的包装类型为 Double。基本类型和包装类型的区别主要有以下 4 点。

    六年前,我从苏州回到洛阳,抱着一幅“海归”的心态,投了不少简历,也“约谈”了不少面试官,但仅有两三个令我感到满意。其中有一位叫老马,至今还活在我的手机通讯录里。他当时扔了一个面试题把我砸懵了:说说基本类型和包装类型的区别吧。

    我当时二十三岁,正值青春年华,从事 Java 编程已有 N 年经验(N < 4),自认为所有的面试题都能对答如流,结果没想到啊,被“刁难”了——原来洛阳这块互联网的荒漠也有技术专家啊。现在回想起来,脸上不自觉地泛起了羞愧的红晕:主要是自己当时太菜了。不管怎么说,是时候写篇文章剖析一下基本类型和包装类型的区别了。

    Java 的每个基本类型都对应了一个包装类型,比如说 int 的包装类型为 Integer,double 的包装类型为 Double。基本类型和包装类型的区别主要有以下 4 点。

    01、包装类型可以为 null,而基本类型不可以

    别小看这一点区别,它使得包装类型可以应用于 POJO 中,而基本类型则不行。

    POJO 是什么呢?这里稍微说明一下。

    POJO 的英文全称是 Plain Ordinary Java Object,翻译一下就是,简单无规则的 Java 对象,只有属性字段以及 setter 和 getter 方法,示例如下。

    class Writer {
    	private Integer age;
    	private String name;
    
    	public Integer getAge() {
    		return age;
    	}
    
    	public void setAge(Integer age) {
    		this.age = age;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    }
    

    和 POJO 类似的,还有数据传输对象 DTO(Data Transfer Object,泛指用于展示层与服务层之间的数据传输对象)、视图对象 VO(View Object,把某个页面的数据封装起来)、持久化对象 PO(Persistant Object,可以看成是与数据库中的表映射的 Java 对象)。

    那为什么 POJO 的属性必须要用包装类型呢?

    《阿里巴巴 Java 开发手册》上有详细的说明,我们来大声朗读一下(预备,起)。

    数据库的查询结果可能是 null,如果使用基本类型的话,因为要自动拆箱(将包装类型转为基本类型,比如说把 Integer 对象转换成 int 值),就会抛出 NullPointerException 的异常。

    02、包装类型可用于泛型,而基本类型不可以

    泛型不能使用基本类型,因为使用基本类型时会编译出错。

    List<int> list = new ArrayList<>(); // 提示 Syntax error, insert "Dimensions" to complete ReferenceType
    List<Integer> list = new ArrayList<>();
    

    为什么呢?因为泛型在编译时会进行类型擦除,最后只保留原始类型,而原始类型只能是 Object 类及其子类——基本类型是个特例。

    03、基本类型比包装类型更高效

    基本类型在栈中直接存储的具体数值,而包装类型则存储的是堆中的引用。

    很显然,相比较于基本类型而言,包装类型需要占用更多的内存空间。假如没有基本类型的话,对于数值这类经常使用到的数据来说,每次都要通过 new 一个包装类型就显得非常笨重。

    03、两个包装类型的值可以相同,但却不相等

    两个包装类型的值可以相同,但却不相等——这句话怎么理解呢?来看一段代码就明明白白了。

    Integer chenmo = new Integer(10);
    Integer wanger = new Integer(10);
    
    System.out.println(chenmo == wanger); // false
    System.out.println(chenmo.equals(wanger )); // true
    

    两个包装类型在使用“”进行判断的时候,判断的是其指向的地址是否相等。chenmo 和 wanger 两个变量使用了 new 关键字,导致它们在“”的时候输出了 false。

    chenmo.equals(wanger) 的输出结果为 true,是因为 equals 方法内部比较的是两个 int 值是否相等。源码如下。

    private final int value;
    
    public int intValue() {
        return value;
    }
    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }
    

    瞧,虽然 chenmo 和 wanger 的值都是 10,但他们并不相等。换句话说就是:将“==”操作符应用于包装类型比较的时候,其结果很可能会和预期的不符

    04、自动装箱和自动拆箱

    既然有了基本类型和包装类型,肯定有些时候要在它们之间进行转换。把基本类型转换成包装类型的过程叫做装箱(boxing)。反之,把包装类型转换成基本类型的过程叫做拆箱(unboxing)。

    在 Java SE5 之前,开发人员要手动进行装拆箱,比如说:

    Integer chenmo = new Integer(10);  // 手动装箱
    int wanger = chenmo.intValue();  // 手动拆箱
    

    Java SE5 为了减少开发人员的工作,提供了自动装箱与自动拆箱的功能。

    Integer chenmo  = 10;  // 自动装箱
    int wanger = chenmo;     // 自动拆箱
    

    上面这段代码使用 JAD 反编译后的结果如下所示:

    Integer chenmo = Integer.valueOf(10);
    int wanger = chenmo.intValue();
    

    也就是说,自动装箱是通过 Integer.valueOf() 完成的;自动拆箱是通过 Integer.intValue() 完成的。理解了原理之后,我们再来看一道老马当年给我出的面试题。

    // 1)基本类型和包装类型
    int a = 100;
    Integer b = 100;
    System.out.println(a == b);
    
    // 2)两个包装类型
    Integer c = 100;
    Integer d = 100;
    System.out.println(c == d);
    
    // 3)
    c = 200;
    d = 200;
    System.out.println(c == d);
    

    答案是什么呢?有举手要回答的吗?答对的奖励一朵小红花哦。

    第一段代码,基本类型和包装类型进行 == 比较,这时候 b 会自动拆箱,直接和 a 比较值,所以结果为 true。

    第二段代码,两个包装类型都被赋值为了 100,这时候会进行自动装箱,那 == 的结果会是什么呢?

    我们之前的结论是:将“==”操作符应用于包装类型比较的时候,其结果很可能会和预期的不符。那结果是 false?但这次的结果却是 true,是不是感觉很意外?

    第三段代码,两个包装类型重新被赋值为了 200,这时候仍然会进行自动装箱,那 == 的结果会是什么呢?

    吃了第二段代码的亏后,是不是有点怀疑人生了,这次结果是 true 还是 false 呢?扔个硬币吧,哈哈。我先告诉你结果吧,false。

    为什么?为什么?为什么呢?

    事情到了这一步,必须使出杀手锏了——分析源码吧。

    之前我们已经知道了,自动装箱是通过 Integer.valueOf() 完成的,那我们就来看看这个方法的源码吧。

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

    难不成是 IntegerCache 在作怪?你猜对了!

    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];
    
        static {
            // high value may be configured by property
            int h = 127;
            int i = parseInt(integerCacheHighPropValue);
            i = Math.max(i, 127);
            h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            high = h;
    
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
    
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }
    }
    

    大致瞟一下这段代码你就全明白了。-128 到 127 之间的数会从 IntegerCache 中取,然后比较,所以第二段代码(100 在这个范围之内)的结果是 true,而第三段代码(200 不在这个范围之内,所以 new 出来了两个 Integer 对象)的结果是 false。

    看完上面的分析之后,我希望大家记住一点:当需要进行自动装箱时,如果数字在 -128 至 127 之间时,会直接使用缓存中的对象,而不是重新创建一个对象

    自动装拆箱是一个很好的功能,大大节省了我们开发人员的精力,但也会引发一些麻烦,比如下面这段代码,性能就很差。

    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);
    

    sum 由于被声明成了包装类型 Long 而不是基本类型 long,所以 sum += i 进行了大量的拆装箱操作(sum 先拆箱和 i 相加,然后再装箱赋值给 sum),导致这段代码运行完花费的时间足足有 2986 毫秒;如果把 sum 换成基本类型 long,时间就仅有 554 毫秒,完全不一个等量级啊。

    05、最后

    谢谢大家的阅读,原创不易,喜欢就点个赞,这将是我最强的写作动力。如果你觉得文章对你有所帮助,也蛮有趣的,就关注一下我的公众号,谢谢。

    展开全文
  • 基本类型和包装类型的区别详解

    千次阅读 多人点赞 2019-11-30 22:20:01
    他当时扔了一个面试题把我砸懵了:说说基本类型和包装类型的区别吧。 我当时二十三岁,正值青春年华,从事 Java 编程已有 N 年经验(N < 4),自认为所有的面试题都能对答如流,结果没想到啊,被“刁难”了——...

    六年前,我从苏州回到洛阳,抱着一幅“海归”的心态,投了不少简历,也“约谈”了不少面试官,但仅有两三个令我感到满意。其中有一位叫老马,至今还活在我的手机通讯录里。他当时扔了一个面试题把我砸懵了:说说基本类型和包装类型的区别吧。

    我当时二十三岁,正值青春年华,从事 Java 编程已有 N 年经验(N < 4),自认为所有的面试题都能对答如流,结果没想到啊,被“刁难”了——原来洛阳这块互联网的荒漠也有技术专家啊。现在回想起来,脸上不自觉地泛起了羞愧的红晕:主要是自己当时太菜了。不管怎么说,是时候写篇文章剖析一下基本类型和包装类型的区别了。

    Java 的每个基本类型都对应了一个包装类型,比如说 int 的包装类型为 Integer,double 的包装类型为 Double。基本类型和包装类型的区别主要有以下 4 点。

    01、包装类型可以为 null,而基本类型不可以

    别小看这一点区别,它使得包装类型可以应用于 POJO 中,而基本类型则不行。

    POJO 是什么呢?这里稍微说明一下。

    POJO 的英文全称是 Plain Ordinary Java Object,翻译一下就是,简单无规则的 Java 对象,只有属性字段以及 setter 和 getter 方法,示例如下。

    class Writer {
    	private Integer age;
    	private String name;
    
    	public Integer getAge() {
    		return age;
    	}
    
    	public void setAge(Integer age) {
    		this.age = age;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    }
    

    和 POJO 类似的,还有数据传输对象 DTO(Data Transfer Object,泛指用于展示层与服务层之间的数据传输对象)、视图对象 VO(View Object,把某个页面的数据封装起来)、持久化对象 PO(Persistant Object,可以看成是与数据库中的表映射的 Java 对象)。

    那为什么 POJO 的属性必须要用包装类型呢?

    《阿里巴巴 Java 开发手册》上有详细的说明,我们来大声朗读一下(预备,起)。

    数据库的查询结果可能是 null,如果使用基本类型的话,因为要自动拆箱(将包装类型转为基本类型,比如说把 Integer 对象转换成 int 值),就会抛出 NullPointerException 的异常。

    02、包装类型可用于泛型,而基本类型不可以

    泛型不能使用基本类型,因为使用基本类型时会编译出错。

    List<int> list = new ArrayList<>(); // 提示 Syntax error, insert "Dimensions" to complete ReferenceType
    List<Integer> list = new ArrayList<>();
    

    为什么呢?因为泛型在编译时会进行类型擦除,最后只保留原始类型,而原始类型只能是 Object 类及其子类——基本类型是个特例。

    03、基本类型比包装类型更高效

    基本类型在栈中直接存储的具体数值,而包装类型则存储的是堆中的引用。

    很显然,相比较于基本类型而言,包装类型需要占用更多的内存空间。假如没有基本类型的话,对于数值这类经常使用到的数据来说,每次都要通过 new 一个包装类型就显得非常笨重。

    03、两个包装类型的值可以相同,但却不相等

    两个包装类型的值可以相同,但却不相等——这句话怎么理解呢?来看一段代码就明明白白了。

    Integer chenmo = new Integer(10);
    Integer wanger = new Integer(10);
    
    System.out.println(chenmo == wanger); // false
    System.out.println(chenmo.equals(wanger )); // true
    
    

    两个包装类型在使用“”进行判断的时候,判断的是其指向的地址是否相等。chenmo 和 wanger 两个变量使用了 new 关键字,导致它们在“”的时候输出了 false。

    chenmo.equals(wanger) 的输出结果为 true,是因为 equals 方法内部比较的是两个 int 值是否相等。源码如下。

    private final int value;
    
    public int intValue() {
        return value;
    }
    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }
    

    瞧,虽然 chenmo 和 wanger 的值都是 10,但他们并不相等。换句话说就是:将“==”操作符应用于包装类型比较的时候,其结果很可能会和预期的不符

    04、自动装箱和自动拆箱

    既然有了基本类型和包装类型,肯定有些时候要在它们之间进行转换。把基本类型转换成包装类型的过程叫做装箱(boxing)。反之,把包装类型转换成基本类型的过程叫做拆箱(unboxing)。

    在 Java SE5 之前,开发人员要手动进行装拆箱,比如说:

    Integer chenmo = new Integer(10);  // 手动装箱
    int wanger = chenmo.intValue();  // 手动拆箱
    
     
    • 1
    • 2

    Java SE5 为了减少开发人员的工作,提供了自动装箱与自动拆箱的功能。

    Integer chenmo  = 10;  // 自动装箱
    int wanger = chenmo;     // 自动拆箱
    
     
    • 1
    • 2

    上面这段代码使用 JAD 反编译后的结果如下所示:

    Integer chenmo = Integer.valueOf(10);
    int wanger = chenmo.intValue();
    
     
    • 1
    • 2

    也就是说,自动装箱是通过 Integer.valueOf() 完成的;自动拆箱是通过 Integer.intValue() 完成的。理解了原理之后,我们再来看一道老马当年给我出的面试题。

    // 1)基本类型和包装类型
    int a = 100;
    Integer b = 100;
    System.out.println(a == b);
    
    // 2)两个包装类型
    Integer c = 100;
    Integer d = 100;
    System.out.println(c == d);
    
    // 3)
    c = 200;
    d = 200;
    System.out.println(c == d);
    

    答案是什么呢?有举手要回答的吗?答对的奖励一朵小红花哦。

    第一段代码,基本类型和包装类型进行 == 比较,这时候 b 会自动拆箱,直接和 a 比较值,所以结果为 true。

    第二段代码,两个包装类型都被赋值为了 100,这时候会进行自动装箱,那 == 的结果会是什么呢?

    我们之前的结论是:将“==”操作符应用于包装类型比较的时候,其结果很可能会和预期的不符。那结果是 false?但这次的结果却是 true,是不是感觉很意外?

    第三段代码,两个包装类型重新被赋值为了 200,这时候仍然会进行自动装箱,那 == 的结果会是什么呢?

    吃了第二段代码的亏后,是不是有点怀疑人生了,这次结果是 true 还是 false 呢?扔个硬币吧,哈哈。我先告诉你结果吧,false。

    为什么?为什么?为什么呢?

    事情到了这一步,必须使出杀手锏了——分析源码吧。

    之前我们已经知道了,自动装箱是通过 Integer.valueOf() 完成的,那我们就来看看这个方法的源码吧。

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

    难不成是 IntegerCache 在作怪?你猜对了!

    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];
    
        static {
            // high value may be configured by property
            int h = 127;
            int i = parseInt(integerCacheHighPropValue);
            i = Math.max(i, 127);
            h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            high = h;
    
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
    
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }
    }
    

    大致瞟一下这段代码你就全明白了。-128 到 127 之间的数会从 IntegerCache 中取,然后比较,所以第二段代码(100 在这个范围之内)的结果是 true,而第三段代码(200 不在这个范围之内,所以 new 出来了两个 Integer 对象)的结果是 false。

    看完上面的分析之后,我希望大家记住一点:当需要进行自动装箱时,如果数字在 -128 至 127 之间时,会直接使用缓存中的对象,而不是重新创建一个对象

    自动装拆箱是一个很好的功能,大大节省了我们开发人员的精力,但也会引发一些麻烦,比如下面这段代码,性能就很差。

    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);
    

    sum 由于被声明成了包装类型 Long 而不是基本类型 long,所以 sum += i 进行了大量的拆装箱操作(sum 先拆箱和 i 相加,然后再装箱赋值给 sum),导致这段代码运行完花费的时间足足有 2986 毫秒;如果把 sum 换成基本类型 long,时间就仅有 554 毫秒,完全不一个等量级啊。

    05、最后

    谢谢大家的阅读,原创不易,喜欢就点个赞,这将是我最强的写作动力。如果你觉得文章对你有所帮助,也蛮有趣的,就关注一下我的公众号,谢谢。

    展开全文
  • 一、基本类型 Java一种静态编程语言,所有变量表达式是在编译时就确定的。同时,Java又是一种强类型语言,所有的变量表达式都有具体...最简单的理解,基本类型有默认值,而包装类型初始为null。然后再根据这两...

    一、基本类型

    Java一种静态编程语言,所有变量和表达式是在编译时就确定的。同时,Java又是一种强类型语言,所有的变量和表达式都有具体的类型,并且每种类型是严格定义的。类型限制了变量可以hold什么样的值,表达式最终会产生什么样的值,可以进行哪些操作。在Java中共有8中基本类型数据,同时每种基本类型又有对应的包装类。

    最简单的理解,基本类型有默认值,而包装类型初始为null。然后再根据这两个特性进行分业务使用,在阿里巴巴的规范里所有的POJO类必须使用包装类型,而在本地变量推荐使用基本类型。 

    基本类型介绍

    Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。 

    1、整数:包括int、short、byte、long初始值为0。

    2、浮点型:float、double初始值为0.0

    3、字符:char初始值为空格,即'' ",如果输出,在Console上是看不到效果的。

    4、布尔:boolean初始值为false 

    基本型别大小最小值最大值包装类
    boolean----------------Boolean
    char16-bitUnicode 0Unicode 2^16-1Character
    byte8-bit-128+127Byte
    short16-bit-2^15+2^15-1Short
    int32-bit-2^31+2^31-1Integer
    long64-bit-2^63+2^63-1Long
    float32-bitIEEE754IEEE754Float
    double64-bitIEEE754IEEE754Double

     基本类型的运算和相互转换

    基本类型运算

    boolean类型数据可以进行逻辑运算(&&、||、!),其他的基本类型都可以进行数值计算(+、-、*、/、%等)。逻辑运算比较简单易懂,完全与逻辑数学的规则一致。而数值运算涉及到运算后的结果的类型问题,稍微比较复杂一点。一般来说,运算最终结果的类型与表达式中的最大(占用空间最大)的类型。

    long l = 1 + 2L;      // 与1L的类型一致
    int i = 1 + 2L;       //编译不通过
    float f = 1 + 2 + 1.2f;      //与1.2f的类型一致
    double d = 1 + 2 + 1.2;        //与1.2的类型一致

    如果两种相同的类型的数据进行运算,按理来说,运算结果应该还是那个类型。但事实上,bytecharshort等类型是满足这个结论的。

    // 编译不通过,编辑器报:Type mismatch: cannot convert from int to byte 。
    byte s1 = 1;
    byte s2 = 1;
    byte s = s1 + s2;
    
    // 编译不通过,编辑器报:Type mismatch: cannot convert from int to char 。
    char s1 = 1;
    char s2 = 1;
    char s = s1 + s2;
    
    // 编译不通过,编辑器报:Type mismatch: cannot convert from int to short 。
    short s1 = 1;
    short s2 = 1;
    short s = s1 + s2;

    从字面上来看,1+1=2绝对没有超过这个类型的范围。下面的例子都可以编译通过

    byte s1 = 1 + 1;
    char s2 = 1 + 1;
    short s3 = 1 + 1;

    这是因为Java中的数值运算最低要求是int类型,如果参与运算的变量类型都没有超过int类型,则它们都会被自动升级为int类型再进行运算,所以它们运算后的结果类型也是int类型。这种方式所得到结果是否超过了对应类型所表示的范围只能在运行时才能确定,在编译时是无法知晓的。而编译器会直接将byte s1 = 1 + 1编译成byte s1 = 2,这个表达式在编译器就可以确定是合法表达式,故可以通过编译。可以通过字节码来进行佐证。

    short s = 1 + 1;

    上面伪代码所对应的字节码如下,iconst_2表示直接生成常量,然后赋值给s变量。

    Code:
          stack=1, locals=2, args_size=1
             0: iconst_2
             1: istore_1
             2: return

    类型转换

    Java中除了boolean类型之外,其他7中类型相互之间可以进行转换。转换分为自动转换和强制转换。对于自动转换(隐式),无需任何操作,而强制类型转换需要显式转换,即使用转换操作符(type)。7种类型按照其占用空间大小进行排序:

    byte <(short=char)< int < long < float < double

    类型转换的总则是:小可直接转大、大转小会失去精度。这句话的意思是较小的类型直接转换成较大的类型,没有任何印象;而较大的类型也可以转换为较小的类型,但是会失去精度。他们之间的转换都不会抛出任何运行时异常。小转大是Java帮我们自动进行转换的,与正常的赋值操作完全一样;大转小需要进行强制转换操作,其语法是target-type var =(target-type) value。

    // 自动转换
    long l = 10;
    double d = 10;
    float = 10;
    
    // 强制转换
    int a = (int) 1.0;
    char c = (char) a;

    值得注意是,大转小是一个很不安全的动作,可能导致莫名其妙的错误。譬如在下面的代码中,1111111111111L强转成int类型后,其值(-1285418553)与转换前的值相差巨大。这是由于在进行强制转换时,在二进制层面上直接截断,导致结果“面目全非”。

    二、包装类型 

    Java中每一种基本类型都会对应一个唯一的包装类,基本类型与其包装类都可以通过包装类中的静态或者成员方法进行转换。每种基本类型及其包装类的对应关系如下,值得注意的是,所有的包装类都是final修饰的,也就是它们都是无法被继承和重写的。

    包装类与基本类型的转换

    基本类型------>包装器类
    Integer obj=new Integer(145);
    
    包装器类------>基本类型
    int num=obj.intValue();
    
    字符串------>包装器类
    Integer obj=new Integer("-45.36");
    
    包装器类------>字符串包装器类
    String str=obj.toString();
    
    字符串------>基本类型
    int num=Integer.parseInt("-45.36");
    
    基本类型------>字符串包装器类
    String str=String.valueOf(5);

     

     “莫名其妙”的NullPointException在笔者开发经历中,碰到过不少因为请求参数或者接口定义字段设置为int(或者其他基本类型)而导致NullPointException。代码大致地运行步骤如下所示,当然不会跟这个完全一样。

    Integer a =null;...intb = a;// 抛出NullPointException上面的代码可以编译通过,但是会抛出空指针异常(NullPointException)。前面已经说过了,int b = a实际上是int b = a.intValue(),由于a的引用值为null,在空对象上调用方法就会抛出NullPointException。

    三、拆箱与装箱 

    JDK1.5的新特性:自动装包/拆包(Autoboxing/unboxing)

      自动装包/拆包大大方便了基本类型数据和它们包装类地使用。

      自动装包:基本类型自动转为包装类.(int >> Integer)

      自动拆包:包装类自动转为基本类型.(Integer >> int)

      在JDK1.5之前,我们总是对集合不能存放基本类型而耿耿于怀,现在自动转换机制解决了我们的问题。

    Java作为一种强类型的语言,对象直接赋值给引用类型变量,而基础数据只能赋值给基本类型变量,这个是毫无异议的。那么基本类型和包装类型为什么可以直接相互赋值呢?这其实是Java中的一种“语法糖”。“语法糖”是指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会(来自百度百科)。换句话说,这其实是一种障眼法,那么实际上是怎么样的呢?下面是Integer a = 1;语句编译的字节码。

    0: iconst_1
    1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    4: astore_1

    首先,生成一个常量1,然后调用Integer.valueOf(int)方法返回Integer对象,最后将对象的地址(引用)赋值给变量a。Integer a = 1;其实相当于Integer a = Integer.valueOf(1);。其他的也类似,比如Double、Character,不相信的朋友可以自己手动尝试一下。

    Integer

    在Java中,“==”符号判断的内存地址所对应的值得相等性,具体来说,基本类型判断值是否相等,引用类型判断其指向的地址是否相等。看看下面的代码,两种类似的代码逻辑,但是得到截然不用的结果。

    Integer a1 = 1;
    Integer a2 = 1;
    System.out.println(a1 == a2); // true
    
    Integer b1 = 222;
    Integer b2 = 222;
    System.out.println(b1 == b2); // false

     这个必须从源代码中才能找到答案。Integer类中的valueOf()方法的源代码如下:

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high) // 判断实参是否在可缓存范围内,默认为[-128, 127]
            return IntegerCache.cache[i + (-IntegerCache.low)]; // 如果在,则取出初始化的Integer对象
        return new Integer(i); // 如果不在,则创建一个新的Integer对象
    }

    由于1属于[-128, 127]集合范围内,所以valueOf()每次都会取出同一个Integer对象,故第一个“==”判断结果为true;而222不属于[-128, 127]集合范围内,所以valueOf()每次都会创建一个新的Integer对象,由于两个新创建的对象的地址不一样,故第一个“==”判断结果为false。 

    Double

    下面这段代码的输出结果又为什么不一样呢? 

            Double i1 = 100.0;
            Double i2 = 100.0;
            System.out.println(i1==i2);   //false
    
            Double i3 = 200.0;
            Double i4 = 200.0;       
            System.out.println(i3==i4);   //false

    至于具体为什么,读者可以去查看Double类的valueOf的实现。

      在这里只解释一下为什么Double类的valueOf方法会采用与Integer类的valueOf方法不同的实现。很简单:在某个范围内的整型数值的个数是有限的,而浮点数却不是。

    Boolean

    下面这段代码输出结果又是什么?

            Boolean i1 = false;
            Boolean i2 = false;
            System.out.println(i1==i2);     //true
    
            Boolean i3 = true;
            Boolean i4 = true;         
            System.out.println(i3==i4);     //true

    至于为什么是这个结果,同样地,看了Boolean类的源码也会一目了然。下面是Boolean的valueOf方法的具体实现:

    public static Boolean valueOf(boolean b) {
            return (b ? TRUE : FALSE);
        }

    而其中的 TRUE 和FALSE又是什么呢?在Boolean中定义了2个静态成员属性:

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

    至此,大家应该明白了为何上面输出的结果都是true了

      注意,Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。

         Double、Float的valueOf方法的实现是类似的。 

    谈谈Integer i = new Integer(xxx)和Integer i =xxx;这两种方式的区别

    • 1)第一种方式不会触发自动装箱的过程;而第二种方式会触发;
    • 2)在执行效率和资源占用上的区别。第二种方式的执行效率和资源占用在一般性情况下要优于第一种情况(注意这并不是绝对的)。

    再来看看这个题,加深对装箱拆箱操作的理解

            Integer a = 1;
            Integer b = 2;
            Integer c = 3;
            Integer d = 3;
            Integer e = 321;
            Integer f = 321;
            Long g = 3L;
            Long h = 2L;
             
            System.out.println(c==d);            //true
            System.out.println(e==f);            //false
            System.out.println(c==(a+b));        //true
            System.out.println(c.equals(a+b));   //true
            System.out.println(g==(a+b));        //true
            System.out.println(g.equals(a+b));   //false
            System.out.println(g.equals(a+h));   //true

    注意:

    • 当 "=="运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象
    • 如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)
    • 对于包装器类型,equals方法并不会进行类型转换

    第一个和第二个输出结果没有什么疑问。第三句由于  a+b包含了算术运算,因此会触发自动拆箱过程(会调用intValue方法),因此它们比较的是数值是否相等。而对于c.equals(a+b)会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。同理对于后面的也是这样,不过要注意倒数第二个和最后一个输出的结果(如果数值是int类型的,装箱过程调用的是Integer.valueOf;如果是long类型的,装箱调用的Long.valueOf方法)。

    三、基本类型与包装类型的异同 

    1、在Java中,一切皆对象,但八大基本类型却不是对象。

    2、声明方式的不同,基本类型无需通过new关键字来创建,而封装类型需new关键字。

    3、存储方式及位置的不同,基本类型是直接存储变量的值保存在堆栈中能高效的存取,封装类型需要通过引用指向实例,具体的实例保存在堆中。

    4、初始值的不同,封装类型的初始值为null,基本类型的的初始值视具体的类型而定,比如int类型的初始值为0,boolean类型为false;

    5、使用方式的不同,比如与集合类合作使用时只能使用包装类型。

    6、什么时候该用包装类,什么时候用基本类型,看基本的业务来定:这个字段允不允许null值,如果允许null值,则必然要用封装类,否则值类型就可以了,用到比如泛型和反射调用函数.,就需要用包装类! 

    展开全文
  • 浅析八种基本类型和包装类型

    Java八种基本类型

    《Java八种基本类型分析》

    Java基本类型对应的包装类

    Everything is object

    • Java编程语言不把基本数据类型看作对象。Java 编程语言提供 包装类来将基本数据类型看作对象。
    • 在功能上包装类能够完成数据类型之间(除boolean)的相互转 换,尤其是基本数据类型和String类型的转换。
      包装类中包含了对应基本数据类型的值,封装了String和基本数 据类型之间相互转换的方法,还有一些处理这些基本数据类型时 非常有用的属性和方法。

    包装类简介

    基本数据类型不是对象层次结构的组成部分。有时需要像处理对象一 样处理这些基本数据类型,可通过相应的“包装类”来将其“包装” 后使用。

    基本数据类型和包装类的对应

    数据类型包装类
    booleanBoolean
    byteByte
    charCharacter
    doubleDouble
    floatFloat
    intInteger
    longLong
    shortShort

    字符串与基本数据类型、包装类型转换图

    在这里插入图片描述

    自动装箱和自动拆箱

    JDK自从5.0版本后引入自动装箱和自动拆箱机制,可方便程序的编写 。

    这两个机制,在进行基本数据类型和对应的包装类转换时,系统将自动进行。

    所谓自动装箱,就是直接把一个基本类型的变量赋值给其对应的包装类型。
    所谓自动拆箱,就是直接把一个包装类型的变量赋值给其对应的基本类型。

    举例

    预定义:

    int pInt = 500;
    

    自动装箱:

    Integer  wInt = pInt;
    

    自动拆箱:

    int n = wInt; 
    
    展开全文
  • JAVA中的基本类型与包装类型

    千次阅读 2021-03-14 19:54:46
    JAVA中的基本类型与包装类型 一:基本类型与包装类型简介 1.由于基本类型没有面向对象的特征,为了他们参加到面向对象的开发中, java为八个基本类型提供了对应的包装类。 2.六个包装类继承java.lang.Number,...
  • 而每一个基本类型又各自对应了一个引用类型,称为包装类型(或装箱类型,boxed primitive)。 基本类型与包装类型的主要区别在于以下三个方面: 1、基本类型只有值,而包装类型则具有与它们的
  • 为了让基本类型也具有对象的特征,就出现了包装类型(如我们在使用集合类型Collection时就一定要使用包装类型而非基本类型),它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性方法,...
  • Java数据类型包装

    万次阅读 多人点赞 2020-12-16 16:16:27
    1. 为什么需要包装类:JAVA是面向对象的语言,很多类方法中的参数都需使用对象(例如集合),但基本数据类型却不是面向对象的,这就造成了很多不便 2. 拆装箱的概念:将基本数据类型转为包装类的过程叫装箱,将包装...
  • java自动包装功能

    千次阅读 2011-10-10 13:50:35
    java基本类型具有包装器类,使得可以在堆...Java的自动包装功能就是自动的将基本类型转换为包装类型,并可以反向转换。 练习:编写程序,证明自动包装功能对所有的基本类型包装器类型都起作用。 Exercise 9: (2
  • java中基本类型包装

    万次阅读 多人点赞 2020-12-14 08:38:00
    1. 基本类型包装类概述为了方便操作基本数据类型值,将其封装成了对象,在对象中定义了属性行为丰富了该数据的操作。用于描述该对象的类就称为基本数据类型对象包装类。将基本数据类型封装成对象...
  • J2SE入门(二) JAVA基本类型数据类型包装类及自动拆装箱 我们知道基本数据类型包括byte, short, int, long, float, double, char, boolean,对应的包装类分别是Byte, Short, Integer, Long, Float, Double, ...
  • 基本数据类型和包装类、String类的转换&toString方法&instanceof运算符1.包装
  • 包装类是对象,拥有方法字段.对象的调用都是通过引用对象的地址,产生对象时直接用引用指向这个对象(引用变量是在存在栈内存中,对象是在堆内存中的,堆中分配内存); 基本类型不是,直接存储数值.另外一个区别...
  • java基本数据类型对象包装

    千次阅读 2017-10-30 22:37:35
    为了方便操作基本数据类型值,将其封装成了对象,在对象中定义了属性行为丰富了该数据的操作。用于描述该对象的类就称为基本数据类型对象包装类。 基本数据类型与相应的包装类 byte Byte short Short int ...
  • JAVA 包装类型 及 易错陷阱

    千次阅读 2016-09-04 22:00:52
    包装类型是对基本数据类型的加强,自动装箱技术简化了其转换过程,但是整型池NULL是特别需要注意的地方
  • SAP外向交货中的包装功能

    千次阅读 2019-01-28 15:10:03
    SAP的包装管理功能(handling unit management)集成了wm, pp, sd ,mm等多个模块;在这些模块中都使用得到。本篇博客主要介绍一下包装管理在sap外向交货中的应用。其中包括销售包装、交货包装以及可退回包装的管理。...
  • Java中的——基本类型包装

    万次阅读 2018-07-13 18:12:00
    今天我们来一起聊聊基本类型包装类我们在学习 Java 的时候有了解Java有四类八种基本数据类型这些基本数据类型都有对应的包装类基本类型包装类基本类型包装类的概述将基...
  • 包装类型是引用类型,所以使用==是不能比较包装类型对象包含的值是否相等,==只是比较两个引用变量是否指向相同的对象。 由于在jdk1.5之后,支持自动装箱功能,对于Integer会出现下面这种情况: Integer a = 1; ...
  • Java SE-基本数据类型对应包装

    千次阅读 2016-06-30 21:45:09
    其次,包装类作为类是有属性有方法的,功能比基本数据类型要强大。 基本数据类型 对应的包装类 byte Byte short Short int Integer long Long char Character float Float double Double
  • 8个基本数据类型包装

    千次阅读 2017-02-20 15:03:55
    这8种基本数据类型带来了一定的方便性,例如简单的数据运算常规数据的处理。但是在某些时候,基本数据类型也会有一些制约,例如所有的引用类型的变量都继承了Object类,都可以当成Object类型变量使用。但基
  • 基本数据类型包装

    万次阅读 2012-09-13 15:55:45
    因为在对象描述中就可以定义更多的属性行为对该基本数据类型进行操作。 【八种基本数据类型包装类】 byte ——Byte short ——Short int ——Integer long-----Long float-----Float double----Dou
  • java:基本类型转换为包装

    千次阅读 2018-01-08 05:29:55
    java 中的包装类 相信各位小伙伴们对基本数据类型都非常熟悉,例如 int、float、double、boolean、char 等。基本数据类型是不具备对象的特性的,比如基本类型不能调用方法、功能...基本类型和包装类之间的对应关系:
  • 61. 基本数据类型优于包装类 ...正如条目 6 中提到的,自动装箱自动拆箱模糊了基本类型和包装类型之间的区别,但不会消除它们。这两者之间有真正的区别,重要的是你要始终意识到正在使用的是哪一种,并在它们之
  • 什么是包装类? java中的基本数据类型如int,double等不是对象,无法通过向上转型获取到Object提供的方法. 而String却可以,因为String是一个对象而不是一个...包装类顾名思义就是将基本的数据类型以及一些辅助方法...
  • 字符串与其他数据类型之间的转换
  • 在Java1.5支持了自动装箱拆箱功能。什么是装箱拆箱呢?把基本数据类型转换成包装类型就是装箱,把包装类型装换成基本数据类型就是拆箱。 1.代码举例自动拆箱和包装过程 package eclipse; public class...
  • 基本类型包装类的区别

    千次阅读 2015-05-31 10:51:26
    说道数据类型这个问题是一个基本的不能再基本的问题,我们当初编程入门第一课一般就是讲数据类型,而今天我想记录的是一个在Java中容易忽略的问题,即基本类型和包装类型。   基本类型出现的原因 我们都知道在...
  • Java中包装类型的大小比较

    千次阅读 2019-03-29 14:19:00
    通过它,我们可以对照源代码字节码,从而了解很多编译器内部的工作。 于是,我们通过javap命令反编译IntegerTest.class字节码文件,得到结果如下: $ javap -c IntegerTest ▒▒▒▒: ▒▒▒▒▒▒▒ļ▒...
  • SAP 外向交货的包装功能实现

    千次阅读 2012-12-13 20:51:21
    SAP 外向交货的包装功能实现 在执行VL01N创建出埠交付通知单是,各位肯定注意到了有个图标Packing,可各位知道Packing(包装)的作业机制吗?SAP的包装作业,体现为handling unit(HU)的形式,Handling unit是一...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 226,017
精华内容 90,406
关键字:

包装的不同类型和功能