[日报] java基本类型与包装类型之间的关系

目录

介绍

一、原生类与包装类

1.自动装箱(Autoboxing)

2.自动拆箱(Unboxing

3.空值(null)

4.泛型支持

5.方法重载

6.性能

7.存储位置

栈(Stack)

堆(Heap)

引用

重点

8.默认值

二、注意项

1. float和double

2. 1、l、|

3.boolean的存储大小


介绍

Java中,数据类型分为基本数据类型(或叫做原生类、内置类型)和引用数据类型。原生类是指基本数据类型。Java不是纯的面向对象的语言,不纯的地方就是这些基本数据类型不是对象。当然初期Java的运行速度很慢,基本数据类型能在一定程度上改善性能。如果你想编写纯的面向对象的程序,用包装器类是取代基本数据类型就可以了。

Java不被认为是纯粹的面向对象编程语言,主要是因为它支持基本类型,这些类型不是对象。在纯粹的面向对象语言中,一切都是对象,包括数字、字符和布尔值。

在Java早期版本中,性能是一个重要的考虑因素,因为Java程序运行在Java虚拟机(JVM)上,而JVM本身就带来了一定的性能开销。使用基本类型可以减少内存占用,并提高处理速度,因为它们存储在栈上,并且可以直接访问,不需要通过引用。相比之下,包装类型是存储在堆上的对象,它们需要更多的内存,并且访问时可能涉及到指针解引用。

为了提供面向对象的一致性,Java为每个基本类型提供了对应的包装类。这些类提供了一些有用的方法,比如将字符串转换为数值,或者将数值转换为不同的基本类型。此外,由于Java 5引入了自动装箱和自动拆箱,使用包装类型变得更加方便,因为编译器可以自动在基本类型和它们的包装类型之间转换,让开发者能够更专注于业务逻辑。

但是,即使有自动装箱和拆箱,使用包装类型仍然有其代价。每次装箱操作都可能涉及到创建新的对象,这可能会导致额外的垃圾回收(GC)开销。因此,对于大量数值操作的性能敏感应用,如科学计算和高频交易系统,建议直接使用基本类型。

随着Java语言的发展,性能优化一直是JVM和Java语言本身的一个重要方向。JVM的即时编译器(JIT)和垃圾收集器(GC)都经过了大量优化,以提高性能和降低延迟。这些优化使得包装类型的性能开销相对于早期的Java版本有了显著的减少。

除了性能考虑之外,包装类型在Java的集合框架中也扮演着关键角色。由于Java的集合类(如ListSetMap)不支持基本类型,所以在这些集合中存储基本类型的数据时,必须使用相应的包装类型。这就是为什么在使用泛型集合时,我们使用Integer而不是int,使用Double而不是double等等。

一、原生类与包装类

Java中的基本数据类型和对应的包装类
基本类型 包装类型 举例 基本类型的存储空间 范围 基本类型的默认值
boolean Boolean true 没有明确定义 用于表示逻辑值true(真)或false(假) false
byte Byte 100 8位 -128 ~ 127 0
char Character 'A' 16位 16位Unicode字符 '\u0000'(空字符)
short Short 5000 16位 -32768 ~ 32767 0
int Integer 100000 32位 -2^31 ~ 2^31-1 0
long Long 15000000000L 64位 -2^63 ~ 2^63-1 0L
float Float 5.99f 32位 32位单精度浮点数 0.0f
double Double 19.99 64位 64位双精度浮点数 0.0d

没有String,记住了

基本类型与包装类型之间的关系主要体现在以下几个方面:

1.自动装箱(Autoboxing)

一个基本类型自动转换成对应的包装类型时,称为自动装箱。这是Java编译器提供的一个特性,可以让我们在编写代码时不必显式地进行类型转换。

Integer integer  = 10; 

2.自动拆箱(Unboxing

当一个包装类型自动转换成对应的基本类型时,称为自动拆箱。这同样是Java编译器的特性。

int i = integer;

3.空值(null)

包装类型是对象,因此它们可以赋值为null,表示没有任何对象与该变量关联。基本类型的变量不能赋值为null,它们总是有一个默认值,例如int的默认值是0。

4.泛型支持

Java的泛型不支持基本类型,只支持对象类型。因此,在使用泛型时,必须使用包装类型。

5.方法重载

当一个方法被重载,并且参数类型分别是基本类型和对应的包装类型时,基本类型的方法会被优先调用。

class TestTest {
    @Test
    public void mainTest(){
        method(1);
    }

    public void method(int i){
        System.out.println("基元类型方法被调用");
    }

    public void method(Integer i){
        System.out.println("包装类型方法被调用");
    }
}

6.性能

基本类型通常比包装类型更高效,因为包装类型需要额外的内存来存储对象的元数据,并且可能涉及到额外的方法调用。在性能敏感的场合,推荐使用基本类型。

7.存储位置

基本类型的数据通常存储在栈上,而包装类型的对象存储在堆上。

这里对JVM的内存模型提一嘴:

在Java中,虚拟机(JVM)的内存模型定义了不同的区域来存储不同类型的数据。其中两个主要区域是栈(Stack)和堆(Heap)。理解这两个区域如何工作有助于深入了解Java程序的内存管理。

栈(Stack)

栈是一种线程私有的内存区域,每个线程创建时都会获得自己的栈,用于存储局部变量和部分控制信息。在栈中,数据的存储遵循后进先出(LIFO)的原则。栈内存主要用于执行方法调用,每当一个方法被调用时,就会创建一个新的栈帧(Stack Frame)来存储该方法的局部变量、操作数栈、动态链接信息和方法返回值。

  • 线程:每个线程在Java虚拟机中都有自己的栈,用于存储方法调用的栈帧。
  • 栈帧:栈帧是一个包含局部变量、操作数栈、动态链接和方法返回信息的数据结构。每个方法调用都会创建自己的栈帧。
  • 局部变量:包括基本数据类型和对象引用。基本数据类型的值直接存储在栈帧的局部变量表中,而对象引用则是指向堆中实际对象的指针或者句柄。

堆(Heap)

堆是Java虚拟机中一个共享的内存区域,用于存放对象实例和数组。所有线程共享访问堆内存,因此堆内存中的对象可以在不同线程之间共享。当代码中创建一个对象时(例如使用new关键字),对象的数据会被分配到堆上。

  • 对象实例:所有通过new关键字创建的对象都存储在堆内存中。
  • 数组:在Java中,数组也被视为对象,因此数组的内存也是在堆上分配的。

引用

在Java中,当你创建一个对象时,实际上你获得的是一个引用,这个引用指向堆内存中的对象。引用本身是存储在栈上的(作为局部变量的一部分),但它指向的对象数据存储在堆上。

重点

  • 基本类型的数据通常存储在栈上。
  • 包装类型和其他对象类型存储在堆上。
  • 堆是一个共享内存区域,用于存储所有对象实例和数组。
  • 引用类型的变量存储在栈上,但它们指向的对象存储在堆上

8.默认值

类的成员变量的包装类型默认值为null,而基本类型的成员变量有其预定义的默认值,例如int为0,boolean为false。

二、注意项

1. float和double

float 类型是单精度32位IEEE 754浮点数,其最大精度大约是7个十进制数位。

double 类型是双精度64位IEEE 754浮点数,其最大精度大约是15个十进制数位。

这里的“大约”是因为浮点数的表示是基于二进制的,而我们通常讨论的精度是基于十进制的。由于二进制和十进制不是完全对应的,所以精度的值是一个近似值。

精度是指在转换成二进制数时能够保留的有效数字的位数。由于浮点数的存储方式包含了指数部分和尾数部分,所以它们并不是用来精确存储数字的,而是用来表示一个范围内的近似值。

在处理需要高精度的计算时,通常不推荐使用floatdouble,因为它们可能会引入舍入误差。对于要求高精度的应用,应该使用BigDecimal类,它提供了任意精度的浮点数运算。

误差演示:

@Test
    public void mainTest(){
        double d = 0.00000000000011;
        for (int i = 0; i < 10; i++) {
            d += 0.00000000000001;
            System.out.println(d);
        }
    }

[日报] java基本类型与包装类型之间的关系_第1张图片

2. 1、l、|

这是老生常谈的问题了,在写以1结尾的数字时,不要以小写的L作为后缀

如果使用小写的l作为long类型的后缀,它很容易与数字1混淆(1、l、|)。同样,虽然不常见,但如果在数字后使用大写的D,它可能与字母O或数字0混淆(在某些字体下特别容易混淆)。

3.boolean的存储大小

Java 语言规范并没有明确指定 boolean 类型的确切大小。这是因为 JVM 的实现可以自由选择最适合其性能的方式来表示 boolean 值。

在Stack Overflow上的讨论中,有关于Java中boolean变量占用多少字节的问题。高赞回答中提供了一个测试类,通过创建大量的booleanint变量的数组,并测量它们的内存使用情况,来估算booleanint类型的平均大小。

测试结果显示,boolean数组的平均大小约为82.57544字节,而int数组的平均大小约为335.99984字节。这表明在数组中,每个boolean大约占用1个字节的空间。

关键点:

  • 在Java虚拟机中,没有专门用于boolean值的字节码指令。在编译后,boolean值使用Java虚拟机中的int数据类型来表示。
  • Java虚拟机直接支持boolean类型的数组,使用byte类型数组的指令来访问和修改boolean数组。
  • 根据常见的虚拟机规范,编译后的boolean值使用int数据类型来代替,而int是4个字节;boolean数组中的每个元素使用1个字节。

在数组中,boolean占用1个字节,在其他情况下(如单独的boolean变量),可能占用4个字节。具体还要看虚拟机的实现,因为Java规范本身并没有明确指出boolean的大小。 

你可能感兴趣的:(java,开发语言)