你知道Java中基本类型和包装类的区别吗

前情提要

 上一篇中,通过一道常见的面试题(即:String、StringBuilder、StringBuffer的区别),引申到Java中基本类型和包装类的相关内容。在这一篇中,我们将解决上一篇中引申出来的问题——基本类型和包装类型到底有什么区别?
 首先,要弄明白这两者的区别,我们就必须要知道基本数据类型和包装类到底是啥?各自都有些什么特性?
 请注意,除非特别注明,否则本文篇中所涉及得到内容都是基于jdk1.8来说的。
 先来了解一下基本数据类型吧。

关于基本数据类型

基本数据类型到底是个啥?

 关于基本数据类型的定义,我翻了一些资料,至今没有在任何地方找到一个准确的描述。
 这里我就谈一下我个人的见解吧,如果有朋友觉得下面的内容有不妥的地方欢迎在评论区留下您的高见,大家共同进步!

在一个变量定义后,该变量指向的只能是具体的数值而非内存地址。这样的变量就属于基本数据类型。

 这一块的内容后续会再来补充,由于不是主线,暂时搁置影响也不大。

Java中基本数据类型有哪些

 在Java中,基本数据类型有8种,分别为:

布尔类型:boolean
字符类型:char
整数类型:byte、short、int、long
浮点类型:float、double

 各类型的详细信息如下表:

类型描述 名称 位数 字节数 默认值
布尔类型 boolean - - false
字符类型 char 16 2 'u0000'
整数类型 byte 8 1 0
整数类型 short 16 2 0
整数类型 int 32 4 0
整数类型 long 64 8 0L
浮点类型 float 32 4 0f
浮点类型 double 64 8 0d

对于boolean而言,依赖于jvm厂商的具体实现。逻辑上理解是占用1位,但是实际中会考虑的因素较多。在此也不展开描述,如果有人问你究竟boolean占多少内存空间,你只需要回答:理论上1位(注意不是一个字节)就可满足需求,实际还要看jvm的实现。

基本数据类型为什么不用new运算符?

 我们都知道new运算符可用于实例化一个对象,也就是给对象实例分配一块足够大的内存空间并返回指向该内存的引用。注意,这里所指的对象实际上是引用类型。引用对象开辟的内存空间一般是在堆中。
 相对于引用类型来说,值类型一般存放在栈上(作为成员变量的时候才会放在堆中)。因为虚拟机(根据虚拟机不同,boolean可能占用空间大小不同)对每一种基本类型的空间占用大小都是明确知晓的,所以不再需要new去开辟空间。
实际中,Java的数据类型分为两种:值类型和引用类型,我们习惯于把所有引用类型都统称为对象。所以,基本数据类型不在我们理解的对象的定义范围内。

关于包装类

包装类的定义

 其实包装类的意义从名字就能看出一些端倪。啥叫包装,通俗了说就是把一个物体打包然后装起来。举个例子来说,比如今天我在网上买了一颗苹果,等我收到货的时候还是只有那一颗苹果吗?并不是,我拿到的是一个贴了有运单号的箱子,里面用了塑胶袋把苹果给包起来了。同理,包装类也是一样,每一个包装类里面都包了一种基本数据类型。作用也和这个例子类似,是为了让运输更方便,让苹果更安全,让我们操作更简单。

包装类的种类

 八种基本类型都有自己对应的包装类,分别为:

布尔类型:Boolean
字符类型:Character
整数类型:Byte、Short、Integer、Long
浮点类型:Float、Double

装箱和拆箱、包装类型的缓存机制

 下面我们以Integer为例,了解一下什么是装箱和拆箱,还有所谓的包装类的缓存机制到底是什么?首先,关于装箱和拆箱的概念如下:

  1. 装箱——将基本类型用各自对应的包装(引用)类型包装起来:即基本类型->包装类型;
  2. 拆箱——将包装类型转换为基本数据类型:即包装类型->基本类型;

 例如下面的代码将会发生装箱和拆箱的过程。

    public static void main(String[] args){
        // 装箱
        Integer packageObject = 100;
        // 拆箱
        int baseObject = packageObject;
    }

 编译上面的代码(javac命令),查看对应的.class文件的内容(javap命令)。

 public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=3, args_size=1
         0: bipush        100
         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
      LineNumberTable:
        line 9: 0
        line 11: 6
        line 12: 11

 可以看到装箱的时候用的是Integer的valueOf方法;而拆箱的时候用的是intValue方法。在Integer中找到这两个方法。

    valueOf方法的API说明如下:
    /**
     * Returns an {@code Integer} instance representing the specified
     * {@code int} value.  If a new {@code Integer} instance is not
     * required, this method should generally be used in preference to
     * the constructor {@link #Integer(int)}, as this method is likely
     * to yield significantly better space and time performance by
     * caching frequently requested values.
     *
     * This method will always cache values in the range -128 to 127,
     * inclusive, and may cache other values outside of this range.
     *
     * @param  i an {@code int} value.
     * @return an {@code Integer} instance representing {@code i}.
     * @since  1.5
     */

 因为篇幅原因,就不一步一步的点进去看了,有兴趣的朋友自行去翻源码吧。上面的内容已经足够说明问题了,上面的描述大概说了这些东西:

  1. 该返回的是一个基于int(这个是入参,就是代码中定义的100)值的Integer的实例对象;
  2. 首先会判断入参的范围在不在[-128,127]之间。
  3. 如果不在这之间,则会调用构造方法返回一个新的Integer实例;
  4. 如果在这个范围内,则会从缓存中取一个Integer对象返回;

 Integer所谓的缓存其实是在Integer类的内部定义了一个IntegerCache的class,IntegerCache里面持有一个静态并且final修饰的缓存数组,在一开始这个数组里面就已经存入了[-128,127]之间的整型值,当你用自动装箱的方式初始化一个Integer对象并且你的整型值在这个范围内的话,会自动从这个数组中找到对应的Integer对象返回给你,而不是重新创建一个Integer对象。

 下面再来看一下拆箱中遇到的intValue方法。

    intValue方法的API说明如下:
    /**
    * Returns the value of this {@code Integer} as an
    * {@code int}.
    */

 intValue方法的描述很简单,直接返回一个int类型的值。这个int值其实就是在Integer内部包装的基本数据类型(int)。

 到此,(Integer类的)装箱、拆箱以及缓存机制差不多咱们就已经揭开那层面纱了。事实上,八大包装类型中除了浮点类型的包装类(Double和Float)并没有实现缓存技术外,其他的包装类都实现了。

  1. Byte,Short,Integer,Long这四个包装类都提供了数值[-128,127]之间的相应类型的缓存;
  2. Character提供了数值在[0,127]之间的缓存;
  3. Boolean提供了取值在{True,False}之间的缓存;
  4. 为什么浮点型不提供?因为浮点型的取值范围太广,不可能实现缓存。

基本数据类型和包装数据类型常见的面试题

(一)为什么List<int> = new List<int>();类似这样的代码会报错?

答:因为基本数据类型不支持泛型。
 List支持泛型,但是泛型必须是对象。也就是说List支持所有继承自Object类的类型参数,但基本数据类型并没有继承自Object,所以基本数据类型并不是对象。

(二)包装类缓存的常见题
    // 题目
    Integer i1 = 55;
    Integer i2 = 55;
    Integer i3 = new Integer(55);
    Integer i4 = new Integer(55);
    Integer i5 = new Integer(56);
    Integer i6 = 1;
    System.out.println("i1 = i2 ? " + (i1 == i2));
    System.out.println("i3 = i4 ? " + (i3 == i4));
    System.out.println("i5 = i4 + i6 ? " + (i5 == i4 + i6));
    System.out.println("56 = i4 + i6 ? " + (56 == i4 + i6));
    Double d1 = 1.0d;
    Double d2 = 1.0d;
    System.out.println("d1 = d2 ? " + (d1 == d2));
    // 答案
    i1 = i2 ? true
    i3 = i4 ? false
    i5 = i4 + i6 ? true
    56 = i4 + i6 ? true
    d1 = d2 ? false
    // 解析
    // 从缓存数组中取一个值为55的Integer实例
    Integer i1 = 55;
    // 从缓存数组中取一个值为55的Integer实例
    Integer i2 = 55;
    // 创建一个值为55的Integer实例
    Integer i3 = new Integer(55);
    // 创建一个值为55的Integer实例
    Integer i4 = new Integer(55);
    // 创建一个值为56的Integer实例
    Integer i5 = new Integer(56);
    // 从缓存数组中取一个值为1的Integer实例
    Integer i6 = 1;
    // 只要值相等,从缓存数组中取出来的一定是同一个实例
    System.out.println("i1 = i2 ? " + (i1 == i2));
    // 虽然值相同,但是属于两个不同的实例(因为遇到了两个new)
    System.out.println("i3 = i4 ? " + (i3 == i4));
    // Integer类不提供+的实现,所以i4和i6先拆箱为基本数据类型,因为i5要和基本类型比较,i5也只能拆箱
    System.out.println("i5 = i4 + i6 ? " + (i5 == i4 + i6));
    // 同上一个
    System.out.println("56 = i4 + i6 ? " + (56 == i4 + i6));
    Double d1 = 1.0d;
    Double d2 = 1.0d;
    // Double不提供缓存机制,每次都是new的新对象
    System.out.println("d1 = d2 ? " + (d1 == d2));
(三)Integer类的缓存区间为什么是[-128,127],为什么不把范围定义的再广一些?

 事实上,针对于Integer类,我们可以通过改参数的方式来设置这个区间的上下限。那是不是意味着我的区间越大越好呢?
 并不是,看源码的时候就会知道,Integer类的缓存区间的每一个整型值都会被提前创建并加载到内存中去。换句话说,这个区间越大,你的内存就占用的越多。这是一个典型的时间和空间的抉择问题。缓存的意义就是拿空间换时间,但是你拿了太多的空间,可能换来的回报远远不是那点时间能补偿得了的。

(四)在Java中存在i+1<i的情况吗?

 存在,所有的基本类型都有位数的限制,比如int是32位,那么int能表示的整型范围为[-2147483648,2147483647],即:[负2的31次幂,正2的31次幂-1]。当超过这个范围时,将发生溢出,溢出后该值将变为负数。

    int x = 2147483647;
    int y = x + 1;
    System.out.println(y);// 输出:-2147483648
    System.out.println("y < x ? " + (y < x));// 输出:y < x ? true

 关于为什么溢出后会变为负数,大家可自己演练一下。简单提一下,32位的有符号数,第一位是符号位(为0表示正数,为1表示负数)。所以32位能表示的最大的正数是[0111 1111 1111 1111 1111 1111 1111 1111],写成16进制就是0x7FFFFFFF。再加1,低位向高位进位,相加的结果变成[1000 0000 0000 0000 0000 0000 0000 0000],表示成16进制则为0x80000000。这个结果一看就是负数,因为最高位的符号位已经变成1了。至于为什么是-2147483648,就需要去算一下这个二进制串的补码了。


扩展区域

扩展区域主体

这是一个没有实现的扩展。


上一篇:String常见的面试题之String、StringBuilder、StringBuffer的区别是什么
下一篇:聊一聊虚拟机类加载机制吧

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345