Java 的八大基本类型及其包装类型(超级详细)

简介: Java 的八大基本类型及其包装类型(超级详细)

  Java 中有八种内置的基本数据类型,他们分别是 byte、short、int、long、float、double、char 和 boolean,其中,byte、short、int 和 long 都是用来表示整数,float 和 double 是用来表示浮点数的,那它们之间有什么区别和联系呢?除了这八种基本类型(primitive type)外,其余的类型都是引用类型(reference type)。但是包装类型(wrapper class) Integer 和 Long 也可以用来表示整数,那它们与 int 和 long 又有什么区别和联系呢?这八种基本类型之间的运算关系又是怎样的呢?

一、基本类型

基本类型的表示范围

八种基本类型中,char 类型是用来表示字符的,boolean 类型是用来表示布尔值的,除此之外的 6 种都是用来表示数字的。byte、short、int 和 long 是用来表示整数的,float 和 double 是用来表示浮点数的。下面是每一种类型能表示的范围:image.png定义变量时超过对应数据类型最大的表示范围的话将报错。位数越大,单个该数据类型的变量所占用的内存就越多。


每一种类型的最大最小值都保存在了其对应包装类型的属性中,如 Integer.MAX_VALUE 的值为 2147483647,关于包装类型,下面会做详细说明。

类型的默认值每一种类型都有一个默认值,除基本类型外,其他的类型的默认值都是 null,因为它们都是引用类型。整数默认为 int 类型,浮点数默认为 double 类型。

image.png当我们定义静态的基本类型变量时,它们将采用默认值:

public class Test {
    static byte b;
    static short s;
    static int i;
    static long l;
    static float f;
    static double d;
    static char c;
    static boolean bool;
    static String str;
    public static void main(String[] args) {
        System.out.println("byte " + b);  // byte 0
        System.out.println("short " + s);  // short 0
        System.out.println("long " + l);  // long 0
        System.out.println("int " + i);  // int 0
        System.out.println("float " + f);  // float 0.0
        System.out.println("double " + d);  // double 0.0
        System.out.println("char " + c);  // char '\u0000' (这个显示不出来)
        System.out.println("boolean " + bool);  // boolean false
        System.out.println("String " + str);  // String null
    }
}

类型之间的转换

类型之间的转换有自动类型转换和强制类型转换两种。自动类型转换也称隐式转换,是编译器自动进行的,强制类型转换称为显式转换,是写代码时显式地完成的。

自动类型转换(隐式转换

在八种基本数据类型中,除开 boolean 类型,其他的七种都是可以互相进行运算的(如加减法运算)。不同的类型在进行运算的时候,低位的数据类型会向高位的转化,最后的计算结果将是整个运算过程中数据类型位数最大的,这就是自动类型转换。

1. int a = 1;
2. long b = 2;
3. int c = a + b;  // Error
4. long c = a + b;  // Right

但要注意一点,比 int 类型数据小的数据类型在做运算的时候,会转换成 int 类型然后再做运算,尽管计算结果能被 byte 或 short 类型表示:

1. short c = 1, d = 2;
2. short e = c + d;  // Error
3. int e = c + d;  // Right

自动类型转换的规则可以总结为如下:


若有一个变量是 double 类型,则另一个也会被隐式转换为 double 类型;否则,若有一个变量是 float 类型,则另一个也会被隐式转换为 float 类型;否则,若有一个变量是 long 类型,则另一个也会被隐式转换为 long 类型;否则,若有一个变量是 int 类型,则另一个也会被隐式转换为 int 类型。特别的,boolean 类型无法转换(包括自动类型转换和强制类型转换)


即,自动类型转换优先级为:double > float > long > int > short = char = byte (boolean 不可转换)


注意:在 short、byte 和 char 之间不会发生自动类型转换,它们运算时都会先转换成 int 类型,结果最终也是 int 类型,所以前面说,比 int 还小的类型在运算时会被自动转换为 int 类型。

强制类型转换(显式转换)

强制类型转换就是在程序中显式地将一个变量从某一类型强制转换成另一类型。从小位类型到大位类型的转换是向上转换,一般这是没有问题的,但反过来,从大位类型转换到小位类型,即向下转换,是有可能发生数据溢出或者精度损失的。

int a = 32768;
float b = 3.1415926f;
System.out.println((long)a);  // Right
// Output: 1
System.out.println((short)a);  // overflow
// Output: -32768
System.out.println((int)b);  // loss of accuracy
// Output: 3

数据溢出和精度损失

这里关于数据溢出和精度损失做一些简单的说明。

数据溢出

数据溢出之后变量也还是有值的,程序也不会报错,那溢出后的值是如何计算出来的?这实际上和数据存储的规则有关,涉及到原码、反码和补码等知识,这里不会详细说明这个规则,我们只需要一张图便能知道溢出后的值是如何计算得到的。数据溢出解释图(以 short 类型为例)

举个例子,int 类型变量值为 131073,在强制类型转换为 short 类型后值就变为了 1。我们可以这样理解,上面的图是 short 类型变量所有的取值,从 0 开始,131073 一个一个去对应,到 32768 时发生数据溢出,变成了 -32768,继续顺时针旋转去对应每个数,最会对应两圈多两个数,然后 131073 对应到第三圈的第二个数字 1,因此,强制类型转换数据溢出后的结果为 1。


注意:131073 = 65536 * 2 + 1

1. int a = 131073;
2. System.out.println((short)a);  // Output: 1

精度损失

在数据存储长度比较长的数据转换为数据存储长度较短的类型时,就可能会发生精度损失,因为要把长数据变成短数据只能将数据后面多余的部分截断才行。因为转换方式采用的是截断多余的,因此我们可以认为当浮点型数据转换为整型数据时是做了一个向零取整的操作(直接舍去小数)。

1. float a = 3.14f, b = -3.14f;
2. System.out.println((int)a);  // Output: 3
3. System.out.println((int)b);  // Output: -3

隐含强制类型转换

除了前面提到的两种,还有一种隐含的强制类型转换,一个整数数字(int 表示范围之内的)会被隐含地转换成 int 类型,一个浮点数字会被隐含地转换成 double 类型。这也解释了为什么前面说整数默认是 int 类型,浮点数的默认类型是 double 类型了。所以,以后在用 float 类型时一定要在后面加上字母 f 或 F,long 类型后面要加上字母 l 或者 L。

二、包装类型

基本数据类型并不是对象,但为了编程的方便,Java 还是引入了它们,也就是前面提到的八个基本类型,但要将其作为对象来操作的话,还是要用这八个基本类型对应的包装类型(wrapper class)。这个包装类型就是 Java 对基本类型的封装,使其能作为对象使用,它们与其他的类型并没有什么太多的区别,都是引用类型(reference type)。

image.png在 Java5 的时候引入了自动装箱/拆箱机制,使得基本类型和对应的包装类型之间可以相互转换。

基本类型和包装类型的区别

基本类型和包装类型在使用时所表示的值是一样的,表示的范围也是对应的,当 int 类型在转换为 Integer 类型的时候,实际上是调用了 Integer 的 valueOf 方法:

1. Integer a = 1;
2. Integer a = Integer.valueOf(1);
3. // the two are equivalent

下面我们列出基本类型和包装类型的一些区别:


基本类型不是对象,而包装类型是对象(有属性和方法);

基本类型不需要实例化,而包装类型需要实例化;

基本类型是直接存储的值,而包装类型是对实例对象的引用;

基本类型有特殊的默认值(如 int 的默认值是 0),而包装类型都是 null;

基本类型所占用的内存比较少,包装类型占用内存较大;

同一基本类型相同值的不同变量的内存地址一样,但实例化得到的同一包装类型相同值的不同变量的内存地址不一样;

基本类型的运算是直接进行的,而包装类型的运算要先拆箱为基本类型才能进行运算。

对象有属性和方法,前面提到的 Integer.MAX_VALUE 就是包装类型的一个属性,上面提到的 Integer.valueOf() 就是一个方法,而基本类型 int 是没有这些的。


基本类型的值都是直接储存的,所以同一个基本类型、相同值的不同变量的地址是一样的:

1. int a = 1, b = 1;
2. System.out.println(a == b);  // Output: true

包装类型都是对象实例化的引用,一般每个对象都拥有不同的内存空间,它们的内存地址也就不一样:

1. Integer a = new Integer(100);
2. Integer b = new Integer(100);
3. System.out.println(a == b);  // Output: false

valueOf 方法的缓存机制

如果一个 Integer 实例对象是由基本类型转换而来的,值处于 -128 ~ 127 之间时,之前的代码输出结果就为 true 。是因为转换时调用的 valueOf 方法有一个缓存机制。 这个判断范围 -128 ~ 127 是默认的,可以在 JVM 提供的配置 (-XX:AutoBoxCacheMax) 进行修改。


为什么要有这个缓存机制?这是因为处于这个区间的值比较小,而且又比较常用,因此编译器就提前将其缓存了,需要时就直接从缓存里拿,既能提高速度,又能在同样值的包装类型较多的时候节省内存。所以,两个值在 -128 ~ 127 之间,且由基本类型 int 转换而来的 Integer 就是同一个对象。这对于包装类型 Byte、Short 和 Long 也是一样的。

Integer a = 100, b = 100;
System.out.println(a == b);  // Output: true
Integer c = 128, d = 128;
System.out.println(c == d);  // Output: false

与此类似的还有包装类型 Boolean,它一共就只有两种可能,true 和 false,因此也被提前缓存了。包装类型 Character 在 '\u0000' ~ '\u007F' 之间的值(0 ~ 127)也是被提前缓存了。不过包装类型 Float 和 Double 的 valueOf 方法就没有这个缓存机制了。

基本类型和包装类型的联系

为什么包装类型叫做“包装类型”呢?顾名思义,就是对基本类型的一个封装,从名字里我们就能知道,基本类型和包装类型之间的实际上有个装箱与拆箱的关系。


直接由基本类型 int 转化而来的 Integer 对象是对 int 类型的装箱操作,而由 Integer 对象转换来的 int 类型变量就是对 Integer 对象的拆箱操作。

Integer a = 1;  // 装箱
Integer a = Integer.valueOf(1);  // 等价形式
int b = a;  // 拆箱
int b = a.intValue();  // 等价形式

实例化生成的 Integer 类型变量与基本类型转化而来的 integer 类型变量尽管值相同,但内存地址并不相同(非 new 生成的 Integer 对象指向 Java 常量池中的对象),不是同一个对象,所以在比较的时候输出 false。但是当二者与基本类型相同值的变量去比较时却都输出 true

Integer a = 1;
Integer b = new Integer(1);
System.out.println(a == b);  // Output: false
int c = 1;
System.out.println(c == a);  // Output: true
System.out.println(c == b);  // Output: true

这是因为包装类型 Integer 与基本类型在比较的时候会自动拆箱为 int 类型,然后再去比较,这样所得到的结果就是一样的,因此输出 true。

目录
相关文章
|
16天前
|
存储 Java Windows
java基础(9)数据类型中的char类型以及涉及到的转义字符
Java中的char类型可以存储一个中文字符,因为它占用两个字节。转义字符允许在代码中使用特殊字符,例如`\n`表示换行,`\t`表示制表符,`\\`表示反斜杠,`\'`表示单引号,`\"`表示双引号。可以使用`\u`后跟Unicode编码来表示特定的字符。
30 2
java基础(9)数据类型中的char类型以及涉及到的转义字符
|
8天前
|
存储 安全 Java
Java 数据结构类型总结
在 Java 中,常用的数据结构包括基础数据结构(如数组和字符串)、集合框架(如 Set、List 和 Map 接口的多种实现)、特殊数据结构(如栈、队列和双端队列)、链表(单链表、双链表和循环链表)以及图和树等。这些数据结构各有特点和适用场景,选择时需考虑性能、内存和操作需求。集合框架提供了丰富的接口和类,便于处理对象集合。
|
16天前
|
Java
java基础(10)数据类型中的整数类型
Java中的整数类型包括byte、short、int和long。整数字面值默认为int类型,加L表示long类型。整数字面值可以是十进制、八进制(0开头)或十六进制(0x开头)。小容量类型(如int)可自动转换为大容量类型(如long),但大容量转小容量需强制转换,可能导致精度损失。
27 2
|
19天前
|
监控 算法 Java
深入理解Java中的垃圾回收机制在Java编程中,垃圾回收(Garbage Collection, GC)是一个核心概念,它自动管理内存,帮助开发者避免内存泄漏和溢出问题。本文将探讨Java中的垃圾回收机制,包括其基本原理、不同类型的垃圾收集器以及如何调优垃圾回收性能。通过深入浅出的方式,让读者对Java的垃圾回收有一个全面的认识。
本文详细介绍了Java中的垃圾回收机制,从基本原理到不同类型垃圾收集器的工作原理,再到实际调优策略。通过通俗易懂的语言和条理清晰的解释,帮助读者更好地理解和应用Java的垃圾回收技术,从而编写出更高效、稳定的Java应用程序。
|
1月前
|
自然语言处理 算法 Java
Java如何判断两句话的相似度类型MySQL的match
【9月更文挑战第1天】Java如何判断两句话的相似度类型MySQL的match
21 2
|
2月前
|
Java 应用服务中间件 HSF
Java应用结构规范问题之dal层中的mapper数据源类型进行组织的问题如何解决
Java应用结构规范问题之dal层中的mapper数据源类型进行组织的问题如何解决
|
2月前
|
开发工具 数据安全/隐私保护
【Azure Developer】使用MSAL4J 与 ADAL4J 的SDK时候,遇见了类型冲突问题 "java.util.Collections$SingletonList cannot be cast to java.lang.String"
【Azure Developer】使用MSAL4J 与 ADAL4J 的SDK时候,遇见了类型冲突问题 "java.util.Collections$SingletonList cannot be cast to java.lang.String"
|
12天前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
30 2
|
4天前
|
Java 数据库 UED
Java的多线程有什么用
Java的多线程技术广泛应用于提升程序性能和用户体验,具体包括:提高性能,通过并行执行充分利用多核CPU;保持响应性,使用户界面在执行耗时操作时仍流畅交互;资源共享,多个线程共享同一内存空间以协同工作;并发处理,高效管理多个客户端请求;定时任务,利用`ScheduledExecutorService`实现周期性操作;任务分解,将大任务拆分以加速计算。多线程尤其适用于高并发和并行处理场景。
|
16天前
|
存储 缓存 Java
java线程内存模型底层实现原理
java线程内存模型底层实现原理
java线程内存模型底层实现原理