java基本类型与包装类型之间的关系

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

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

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

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

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

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

除了性能考虑之外,包装类型在Java的集合框架中也扮演着关键角色。由于Java的集合类(如List、Set和Map)不支持基本类型,所以在这些集合中存储基本类型的数据时,必须使用相应的包装类型。这就是为什么在使用泛型集合时,我们使用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个十进制数位。

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

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

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

误差演示:

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


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

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

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

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

测试结果显示,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的大小。
 

  • 25
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值