【JavaSE专栏6】Java 基本类型转换、包装类、自动装箱、自动拆箱

2023-08-01 14:49:54 浏览数 (1)

作者主页:Designer 小郑 作者简介:Java全栈软件工程师一枚,来自浙江宁波,负责开发管理公司OA项目,专注软件前后端开发(Vue、SpringBoot和微信小程序)、系统定制、远程技术指导。CSDN学院、蓝桥云课认证讲师,全栈领域优质创作者。热爱技术、专注业务、开放合作、乐于分享,期待你我共同成长! 主打方向:Vue、SpringBoot、微信小程序

Java 作为一个强类型的编程语言,在不同数据类型之间进行转换时,需要进行手动 / 自动转换。

byte 的数据范围是 -128 到 127,如果将数值 128 直接赋值给 byte 类型的数据,IntelliJ IDEA 会编译报错,如下图所示。

所以需要进行数据转换,请看以下代码:

代码语言:javascript复制
public class Main {
    public static void main(String[] args) {

        int a = 128;
        byte b = (byte) a;
        System.out.println("答案 = "   b);
    }
}

最终输出结果为:

代码语言:javascript复制
答案 = -128

因为 128 超过了 byte 的数据范围,则重新计数赋值,第一个值为 -128

通过以上一个简单的例子,同学们只需简单了解下为什么要进行数据类型转换即可。

只要参与运算的基本数据类型不一致时,就会发生数据类型的转换

Java 中基本数据类型的转换主要分为两类

  • 自动转换类型(隐式转换)
  • 强制转换类型(显示转换)

接下来逐个讲解。


1.2 自动类型转换(隐式转换)

自动类型转换,顾名思义就是自动进行数据类型转换。

由 Java 编译器进行自动处理,Java 代码不需要经过任何处理

小类型到大类型是自动提升的,那什么是小类型和大类型呢?如下图所示,箭头非被指结点为相对小类型,反之箭头被指结点为相对大类型,如 short 相对于 int 来说就是小类型。


1.2.1 自动类型转换1——直接赋值

小类型变量赋值给大类型时,会触发自动类型转换,比如:

代码语言:javascript复制
public class Main {
    public static void main(String[] args) {

        long a = 1;
    }
}

数值 1int 类型,而承载的变量为 long 类型,则数值 1 会被自动转换为 1L


1.2.2 自动类型转换2——运算时转换

小类型变量和大类型变量进行运算时,会将小类型提升为大类型,再进行数学运算,如下所示。

代码语言:javascript复制
public class Main {
    public static void main(String[] args) {

        int a = 1;
        long b = 5L   a;
    }
}

比如变量 a 为 int 类型,在计算 5L a 时,会首先将 a 转换为 1L,再计算 5L 1L = 6L,最终得出 6L 结果。

简单来说 long int 会自动转换为 long long 再进行计算。

同理 int double 也会自动转换为 double double 再进行计算,如下代码所示。

代码语言:javascript复制
public class Main {
    public static void main(String[] args) {

        int a = 1;
        double b = 3.14   a;
    }
}

1.3 强制类型转换(显示转换)

大类型转为小类型时,需要强制类型转换,可能会导致数据丢失。

比如 int 类型的取值范围是 -2^31到2^31-1byte 类型的取值范围是 -2^7到2^7-1

int 类型转换为 byte 类型时,会出现数据溢出的情况,如下代码所示。

代码语言:javascript复制
public class Main {
    public static void main(String[] args) {

        int a = 128;
        byte b = (byte) a;
        System.out.println("b = "   b);
    }
}

最终输出的结果为 b = -128,即发生了数据溢出情况,也可以理解为数据丢失。

同理,在 double 类型强制转换为 int 时,也会出现数据精度丢失(数据丢失),如下代码所示。

代码语言:javascript复制
public class Main {
    public static void main(String[] args) {

        double a = 3.1415926;
        int b = (int) a;
        System.out.println("b = "   b);
    }
}

输出结果为 b = 3,即发生了数据精度丢失情况,也可以理解为数据丢失。


1.3 类型转换小结

关于 Java 的数据类型转换,同学们需要理解以下三点:

  • 大转小:强制类型转换,如下:
代码语言:javascript复制
int a = 6;
byte b = (byte) a;

提示:对于 byte 和 int 类型之间的转换中需要注意,当把一个 int 数值赋值给 byte 变量时,不管是否超过范围,都需要强制转换。

  • 小转大:自动类型转换,如下:
代码语言:javascript复制
int a = 6;
long c = a;
  • 默认定义类型

整数的默认类型为 int。 整数带有后缀 L 时类型为 long。 浮点数的默认类型为 double。 浮点数带有后缀 f 时类型为 float

提示:关于 String 的类型转换,将在后续的课时中讲解,因为 String 不属于 Java 的基本数据类型。


二、自动装箱和自动拆箱

在学习自动装箱和自动拆箱之前,首先了解什么是包装类。

2.1 包装类是什么?

在 jdk1.4 中,新增了 8 个基本数据类型的对应包装类,如下表所示。

基本类型

包装类

byte

Byte

short

Short

int

Integer

long

Long

float

Float

double

Double

char

Character

boolean

Boolean

基本数据类型的对应包装类的方式基本相同,但在 JVM 上的分配有所不同,具体将在后续的课时中讲解,同学们只需知道在时间或空间上,基本数据类型优于对应包装类即可。


2.2 为什么要用包装类?

从 jdk1.5 开始,定义集合需要使用包装类。定义普通数组,使用基本类型和包装类都可,如下代码所示。

代码语言:javascript复制
public class Main {
    public static void main(String[] args) {

        /**
         * 普通数组的定义方法
         */
        int[] a = new int[10];
        /**
         * 集合的定义方法
         */
        List<Integer> arr = new ArrayList<>();
        /**
         * 对基本类型的集合定义需要转为包装类
         */
        // List<int> arr = new ArrayList<>(); // 编译错误
    }
}

关于集合的用法,会在下面的课时中讲解,同学们只需要了解包装类的使用场景即可。


2.3 自动装箱

自动装箱是什么?基本类型自动转换为包装类型就是自动装箱

请看以下代码:

代码语言:javascript复制
public class Main {
    public static void main(String[] args) {

        /**
         * 直接赋值
         */
        Integer a = 666;
        /**
         * 包装类的静态工厂方法
         */
        Integer b = Integer.valueOf(666);
        /**
         * 构造器赋值,在现有 JDK 中已被废弃
         */
        Integer c = new Integer(666);
    }
}

在对 Integer 类型的变量赋值时,666 这是一个基本数据类型,而变量 abc 为包装类。

自动装箱,就是将一个基本类型直接赋值给包装类型的过程。

如果没有自动装箱,以上代码无法通过 JVM 编译。


2.4 自动拆箱

自动拆箱是什么?包装类型自动转换为基本类型就是自动拆箱

请看以下代码:

代码语言:javascript复制
public class Main {
    public static void main(String[] args) {

        Integer a = 666;
        int b = 666;
        System.out.println("判断 1 = "   (a == b));
        System.out.println("判断 2 = "   Objects.equals(a,b));
    }
}

代码中分别定义基本类型和包装类,赋同样的值,最后判断是否相等,输出为:

代码语言:javascript复制
判断 1 = true
判断 2 = true

其中变量 a 在比较之前,自动拆箱为基本类型,然后在于 b 进行比较,最后得出 true 的结果。

如果不进行自动拆箱,两个不同类型的变量无法进行比较

就好比拿 苹果电视 比较性能,这是不可行的。

Java 只能将包装类 Integer 拆箱为 int 才能和 int 值进行比较。

这就是 自动拆箱


2.5 性能比较

同学们学习自动装箱、自动拆箱之后,还需要了解它们的性能差距。

因为,这将影响你在实战开发中,决定常用哪个,最终影响你的程序性能。


2.5.1 测试基本类型

首先对基本数据类型进行测试,代码如下所示。

代码语言:javascript复制
public class Main {

    private static final long MAX_NUMBER = 1000000000;
    public static void main(String[] args) {

        long start = System.currentTimeMillis();
        long sum = 0;
        for(long i = 0; i < MAX_NUMBER; i   ) {
            sum  = i;
        }
        long end = System.currentTimeMillis();

        System.out.printf("耗时 = "   (end - start)   ",计算结果 = "   sum);
    }
}

最后输出结果:

代码语言:javascript复制
耗时 = 304,计算结果 = 499999999500000000

2.5.2 测试包装类

接着对包装类进行测试,代码如下所示。

代码语言:javascript复制
public class Main {

    private static final Long MAX_NUMBER = 1000000000L;
    public static void main(String[] args) {

        long start = System.currentTimeMillis();
        Long sum = 0L;
        for(Long i = 0L; i < MAX_NUMBER; i   ) {
            sum  = i;
        }
        Long end = System.currentTimeMillis();

        System.out.printf("耗时 = "   (end - start)   ",计算结果 = "   sum);
    }
}

最后输出结果:

代码语言:javascript复制
耗时 = 6374,计算结果 = 499999999500000000

2.5.3 测试小结

由此可见,使用包装类虽然简便,但频繁自动装拆箱会带来性能低下的问题。

所以在实战开发中,建议使用基本数据类型

如果一定要使用包装类的场景下,再去使用包装类。

在 2.1 小节 也提到过,基本类型无论是在时间还是空间上都是优于引用类型(如包装类)的。


三、课时小结

在本节课时中,讲解了低转高、高转低的 Java 基本类型转换,接着学习了包装类的概念,再引申出自动装箱、自动拆箱的概念,最后分别进行了性能测试。在下节课时中,将学习 Java 变量、常量及其作用域的知识。

0 人点赞