前情提要

上一篇中,通过一道常见的面试题(即: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为例,了解一下什么是装箱和拆箱,还有所谓的包装类的缓存机制到底是什么?首先,关于装箱和拆箱的概念如下:

装箱——将基本类型用各自对应的包装(引用)类型包装起来:即基本类型->包装类型;

拆箱——将包装类型转换为基本数据类型:即包装类型->基本类型;

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

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

*/

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

该返回的是一个基于int(这个是入参,就是代码中定义的100)值的Integer的实例对象;

首先会判断入参的范围在不在[-128,127]之间。

如果不在这之间,则会调用构造方法返回一个新的Integer实例;

如果在这个范围内,则会从缓存中取一个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)并没有实现缓存技术外,其他的包装类都实现了。

Byte,Short,Integer,Long这四个包装类都提供了数值[-128,127]之间的相应类型的缓存;

Character提供了数值在[0,127]之间的缓存;

Boolean提供了取值在{True,False}之间的缓存;

为什么浮点型不提供?因为浮点型的取值范围太广,不可能实现缓存。

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

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

答:因为基本数据类型不支持泛型。

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

存在,所有的基本类型都有位数的限制,比如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,就需要去算一下这个二进制串的补码了。

扩展区域

扩展区域主体

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

java包装类和基本类型谁先谁后_你知道Java中基本类型和包装类的区别吗相关推荐

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

    前情提要 上一篇中,通过一道常见的面试题(即:String.StringBuilder.StringBuffer的区别),引申到Java中基本类型和包装类的相关内容.在这一篇中,我们将解决上一篇中引申 ...

  2. java 基本类型 包装类型_Java中基本类型和包装类

    基本类型运算 boolean类型数据可以进行逻辑运算(&&,||,!),其他的基本类型都可以进行数值计算(+,-,*,/).逻辑运算比较简单易懂,完全与逻辑数学的规则一致,而数值运算涉 ...

  3. java 基本类型 不赋值_探究Java中基本类型和部分包装类在声明变量时不赋值的情况下java给他们的默认赋值...

    探究Java中基本类型和部分包装类在声明变量时不赋值的情况下java给他们的默认赋值 当基本数据类型作为普通变量(八大基本类型: byte,char,boolean,short,int,long,fl ...

  4. java5引入包装类型的意义_Java中的基本类型和包装类

    Java中基本数据类型与包装类型有 基本类型 包装器类型 boolean Boolean char Character int Integer byte Byte short Short long L ...

  5. java包装器类_Java中的基本类型和包装类

    Java中基本数据类型与包装类型有 基本类型 包装器类型 boolean Boolean char Character int Integer byte Byte short Short long L ...

  6. java 定义变量时 赋值与不赋值_探究Java中基本类型和部分包装类在声明变量时不赋值的情况下java给他们的默认赋值...

    探究Java中基本类型和部分包装类在声明变量时不赋值的情况下java给他们的默认赋值 当基本数据类型作为普通变量(八大基本类型: byte,char,boolean,short,int,long,fl ...

  7. Java基础——基本类型和包装类、基本类型和字符串之间的转换

    基本类型和包装类之间的转换 基本类型和包装类之间经常需要互相转换,以 Integer 为例(其他几个包装类的操作雷同哦): 在 JDK1.5 引入自动装箱和拆箱的机制后,包装类和基本类型之间的转换就更 ...

  8. 对象在内存中的存储基本类型和包装类java类型转换

    对象在内存中的存储 对象头.实例数据和填充数据(为了对齐) 实例变量:存放类的属性数据信息,包括父类的属性信息,如果是数组的实例部分还包括数组的长度,这部分内存按4字节对齐. 填充数据:由于虚拟机要求 ...

  9. 【系列】重新认识Java——基本类型和包装类

    Java一种静态编程语言,所有变量和表达式是在编译时就确定的.同时,Java又是一种强类型语言,所有的变量和表达式都有具体的类型,并且每种类型是严格定义的.类型限制了变量可以hold什么样的值,表达式 ...

最新文章

  1. CnPack 使用的组件命名约定
  2. 排序算法——基数排序
  3. 数据结构---KMP模式匹配病毒感染人的DNA检测
  4. 【高校宿舍管理系统】第九章 寝室编号设置和宿舍初始化以及预选设置
  5. 2015-12-02 计划任务维护数据库
  6. 2 snippets vue 修改配置_教你发布vue+.netCore项目到服务器
  7. python 文件的打开与读取
  8. vm驱动程序版本不正确_微软 Win10 版本 2004 获得新版 Intel/Nvidia 显卡驱动程序
  9. 华为NP课程笔记28-IEEP课件摘录
  10. 好用、好玩的小程序第二弹,统统学会,新技能get
  11. VTN系列多通道振弦模拟信号采集仪常规操作
  12. 我们来统计一个各大学中国学生会CSSA的list吧
  13. 6到飞起, 360儿童手表三款新品即将发布
  14. 思路:controller层:后台如何取值 前端如何给name赋值 例如是id赋值还是自己随意定义...
  15. 2020 web前端面试题及答案大全
  16. NdisFilter驱动数据全部转发到应用层的性能之优化(使用共享环形队列方式)
  17. python的cv2.warpAffine()和cv2.warpPerspective()解析对比
  18. c语言CFile的使用方法,C/C++文件操作之CFile
  19. 开源爬虫神器,Playwright vs Puppeteer 对比,你应该选择哪个?
  20. ffmpeg转码php配置,PHP+ffmpeg+nginx的配置实现视频转码(转)

热门文章

  1. vc++ 6.0下Glut的配置 及 Glut 框架介绍
  2. Annoy搜索算法(Approximate Nearest Neighbors Oh Yeah)
  3. 利用微软Atlas消费外部Web服务
  4. Akka 系列(五):Java 和 Scala 中的 Future
  5. 程序设计基本概念(3)(sizeof)-2.20 2.23
  6. OJ问题检测程序---python开发
  7. 初识OR Mapping
  8. python备份cisco交换机_1.自动备份思科交换机配置
  9. 深度强化学习_深度学习理论与应用第8课 | 深度强化学习
  10. c语言链表程序框图,C语言课程设计————写下流程图! 谢谢