Java八种包装类、常量池

news2025/2/23 16:23:16

文章目录

  • 一、八种基本数据类型及其包装类
    • 1.1 类型详解
    • 1.2 基本类型与包装类型相互转换——自动装箱拆箱
  • 二、Java常量池
  • 三、包装类的常量池技术(缓存池)
    • 3.1 各包装类缓冲池范围
    • 3.2 Integer i1=new Integer(5) 与 Integer i2=5 的区别
    • 3.3 Integer.valueOf(int i)方法
    • 3.4 包装类与对应基本数据类型进行运算
  • 四、总结

还记得 Java八种基本数据类型及对应包装类、四种引用类型吗?如果忘记可以到这里重温复习Java数据类型(八种基本数据类型 + 四种引用类型)、数据类型转换

一、八种基本数据类型及其包装类

1.1 类型详解

基本类型存储大小初始化默认值取值范围包装类型
byte1字节(8位)0-128~127Byte
short2字节(16位)0-32768~32767Short
int4字节(32位)0-2^31 ~ 2^31 - 1Integer
long8字节(64位)0L。"L"理论上不分大小写,但若写成"l"容易与数字"1"混淆,不容易分辨,所以最好大写。-2^63 ~ 2^63 - 1Long
float4字节(32位)0.0f符合IEEE754标准的浮点数,1.4E-45 ~ 3.4028235E38Float
double8字节(64位)0.0d符合IEEE754标准的浮点数,4.9E-324 ~ 1.7976931348623157E308Double
char2字节(16位)‘\u0000’\u0000 ~ \uffff(十进制等效值为 0~65535,本质也是数值)Character
boolean1字节(8位)/4字节(32位)falsetrue/falseBoolean

1.2 基本类型与包装类型相互转换——自动装箱拆箱

基本类型与包装类型之间的转换可以通过 自动装箱和拆箱 完成。

  • Integer aInteger = 100; 自动装箱,基本类型–>包装类型【编译器会将int类型的100装箱,变成Interger类型,底层调用 Interger.valueOf(100) 方法
  • int a = aInteger; 自动拆箱,包装类型–>基本类型【编译器会将包装类型aInteger中的数据取出来,赋值给a,底层调用 aInteger.intValue() 方法
Integer a = 18;  //自动装箱。底层执行 Integer a = Integer.valueOf(18);
a = a + 6;//自动拆箱、自动装箱,底层 a=Integer.valueOf(a.intValue()+5);

二、Java常量池

Java常量池是Java内存管理中的一个重要概念,主要用于存储字符串常量、基本类型包装类常量、类和方法的全限定名等。在Java中,当创建一个字符串、基本类型包装类或类引用时,JVM会首先检查常量池中是否已存在该对象。如果存在,则直接返回对该对象的引用;如果不存在,则在常量池中创建一个新的对象并返回引用。

Java常量池主要有以下几个优势、特点

  • 节省内存空间:通过共享常量池中的对象,可以避免重复创建相同的对象,从而节省内存空间。
  • 提高性能:由于可以直接从常量池中获取对象引用,而无需每次都创建新对象,因此可以提高程序的运行效率。
  • 保证唯一性:对于字符串和基本类型包装类常量,常量池保证了它们的唯一性。这意味着在Java程序中,两个相等的字符串常量或基本类型包装类常量实际上是指向常量池中同一个对象的引用。

Java常量池主要包括以下几个部分

  • 字符串常量池:用于存储字符串字面量。当使用字面量方式创建字符串时,JVM会首先检查字符串常量池中是否已存在该字符串。如果存在,则返回该字符串的引用;否则,在字符串常量池中创建一个新的字符串并返回引用。
  • 基本类型包装类常量池:对于基本类型的包装类(如Integer、Boolean等),当使用自动装箱创建包装类对象时,如果值的范围在缓存范围内(如Integer的缓存范围是-128到127),则直接从缓存中获取对象;否则会创建一个新的对象。
  • 类元数据常量池:用于存储类的元数据信息,如类名、方法名、字段名等。这些信息在类加载过程中被解析并存储在常量池中,供后续使用。
  • 运行时常量池:是方法区的一部分。JVM为每个已加载的类型(类或接口)维护一个运行时常量池。运行时常量池包含多种常量,包括编译期产生的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。此外,Java语言并不要求常量一定只有编译期才能产生,也就是说,运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。

需要注意的是,Java 7及以后的版本对String常量池和Integer常量池做了一些优化。例如,对于String常量池,可以通过String类的intern()方法将一个字符串加入到常量池中;对于Integer常量池,缓存范围从Java 5的-128到127扩展到了Java 8及以后版本的-128到Integer.MAX_VALUE。这些优化提高了Java程序的性能和内存使用效率。

java内存模型(JDK7之前、JDK7之后)

JDK7之前

在这里插入图片描述

JDK7之后

在这里插入图片描述

String常量池已在String、StringBuilder、StringBuffer区别;String底层详解,实例化、拼接、比较;String为什么不可变介绍过,本文重点讨论基本类型包装类常量池。

三、包装类的常量池技术(缓存池)

Java中八种基本数据类型的包装类大部分都实现了缓存技术,目的是为了避免重复创建对象,提供性能、节省内存。基本数据类型直接存放在栈中,包装类型作为一种引用数据类型 在堆上分配内存(具体内容存放在堆中,栈中存放的是其具体内容所在内存的地址);Java在jdk1.5后包装类常量池使用缓存实现,缓冲池也叫常量池.

  • Byte、Short、Integer、Long、Character、Boolean,均已实现常量池技术(严格来说应该叫对象池,在堆中)。它们维护的常量仅仅是各自缓冲池范围内的常量(如Integer -128~127、Boolean true和false),在这个范围内只要不new对象,都是在常量池中创建;如果常量值超过这个范围,就会从堆中创建对象,而不再从缓存池中取
  • Float和Double没有实现常量池技术,原因在于这两种数据类型的数值是很随意的,就算有常量池命中率也不会高,还浪费额外的堆内存。

3.1 各包装类缓冲池范围

包装类缓冲池范围
Byte-128~127(包含边界)
Short-128~127
Integer-128~127
Long-128~127(为了节省内存、提高性能,该范围是基于经验选择的,因为该范围内的数值是最常用的)
Character\u0000 ~ \u007F(十进制等效值为 0~127)
Booleantrue和false

(1)内存中有一个java基本类型封装类的常量池。这些类包括Byte, Short, Integer, Long, Character,Boolean,Boolean只有true和false。Float和Double这两个类并没有对应的常量池。

public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}

(2)上面6种整型的包装类的对象是存在范围限定的;在限定范围内存在在常量池, 范围以外则在堆区进行分配。

(3)当使用new关键字创建包装类对象时,都会在堆中创建新的对象;只有使用字面量赋值、且在缓冲池范围内 才可使用对象池,否则还是会在堆中创建对象

(4)包装类的两个变量之间的比较 推荐使用equals进行(比较的是值而非地址)。

对于Integer var = ?,在-128~127范围内的赋值,Integer对象是在IntegerCache.cache产生,会复用已有对象,这个区间内的Integer值可以直接使用==进行判断;但这个区间外的所有数据 都会在堆上产生,并不会复用已有对象,此时==比较的是对象地址值、即对象是否相同,这是一个大坑,推荐使用equals方法进行判断

Integer的equals方法被重写过,比较的是内部value的值,源码如下

public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

public int intValue() {
    return value;
}

3.2 Integer i1=new Integer(5) 与 Integer i2=5 的区别

  • Integer i2=5,自动装箱【编译器在自动装箱过程中会调用valueOf()方法,等价于Integer i2=Integer.valueOf(5) 】。先从常量池中查找是否已经存在该值的Integer对象,如果存在则直接返回常量池中的对象;不存在 就创建一个新的Integer对象并存储在常量池中(当范围在-128~127之间时,多次调用会取得同一个对象的引用)
  • Integer i1=new Integer(5) ,每次都会新建一个对象;

对于valueOf方法,只有当数值在 -128~127 之间时,才会被缓存。若超出该范围 仍会创建新的对象

对于超出[-128, 127]范围的Integer对象,无论是通过valueOf()方法还是new关键字创建,都会在堆中创建新的对象

Integer aInteger = 5;    //在缓存,即常量池。底层调用Integer aInteger = Integer.valueOf(5),里面用到IntegerCache对象池
Integer bInteger = 5;    //在缓存
Integer cInteger = new Integer(5);  //在堆内存中
Integer dInteger = new Integer(5);  //在堆内存中
Integer eInteger = 500;  //不在缓存,在堆中
Integer fInteger = 500;  //不在缓存,在堆中
System.out.println((aInteger == bInteger) + ", " + (cInteger==dInteger));  //true, false
System.out.println(aInteger == cInteger);  //false
System.out.println(eInteger==fInteger);    //false

3.3 Integer.valueOf(int i)方法

通过字面量的方式赋值,实际调用Integer.valueOf方法。Integer i2=5,等价于Integer i2=Integer.valueOf(5),底层用到了IntegerCache,即Integer的缓存池。先判断值是否在缓存池中,如果在 就直接返回缓冲池中的内容,如果不在就new一个对象返回,源码如下

public final class Integer extends Number implements Comparable<Integer> {
    //...
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    //...
}

java8中,Integer缓冲池的最小值为-128,最大值默认为127,可以通过jvm参数调整上限。在IntegerCache的静态代码段中,为-128~127的所有整数生成一个Integer对象,然后添加到cache数据中,当调用Integer.valueof()时会判断数值是否在这个区间内,如果在就直接返回已经缓存好的对象,如果不再就直接新创建一个Integer对象

编译器在自动装箱过程中会调用valueOf()方法,因此如果多个Integer实例使用自动装箱来创建、在-128~127范围内、并且值相同,就会引用相同的对象。

而对于通过new关键字创建的对象,自然不会使用缓存池中的对象

IntegerCache源码如下:

private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }

    private IntegerCache() {}
}
Integer a = 127;
Integer b = 127;
System.out.println(a == b);  //true,常量池中同一个对象

Integer c = 128;  
Integer d = 128;
//由于Integer只缓存-128~127之间的值,因此128对应的数据没有被缓存
System.out.println(c == d);  //false,在堆中 不同对象

Integer e = new Integer(127);
Integer f = new Integer(127);
System.out.println(e == f);  //false,通过new关键字 都会创建一个新对象、在堆中

Byte,Short,Integer,Long,Character,Boolean这 6 种包装类都实现了缓存池技术,原理及使用与上面介绍的Integer类似;两种浮点数类型的包装类 Float、Double没有实现缓存池技术

3.4 包装类与对应基本数据类型进行运算

当包装类和对应基本数据类型进行 ==、运算符 等计算时,包装类会出现自动拆装箱,此时即便是堆中数和常量池数运算也是true

int aint = 127;
Integer i1 = 127;   //在缓存。底层调用Integer cInteger = Integer.valueOf(127),里面用到IntegerCache对象池
Integer i2 = 127;   //在缓存
Integer i3 = 0;     //在缓存
Integer i4 = new Integer(127);   //不在缓存
Integer i5 = new Integer(127);   //不在缓存
Integer i6 = new Integer(0);     //不在缓存
//Integer是int的封装类,当Integer与int进行==比较时(无论Integer是直接赋值、还是new),Integer会拆箱成一个int类型,所以还是相当于两个int类型进行比较
System.out.println((aint == i1) + ", " + (aint==i4));   //true, true。  
//2个不同的Integer对象,“==”会校验Integer地址是否相同
System.out.println((i1 == i2) + ", " + (i1==i4) + ", " + (i4==i5));  //true, false, false      
System.out.println(i1 == i2 + i3);   //true
//堆创建,但Integer对象无法直接计算 故拆箱,比较数值
System.out.println(i4 == i5 + i6);   //true   
System.out.println(127 == i5 + i6);  //true   同上


//Boolean类也实现了对象池技术
Boolean bool1 = true;
Boolean bool2 = true;
System.out.println(bool1 == bool2);  //true

//浮点类型的包装类Float、Double没有实现对象池技术
Double d1 = 2.0;
Double d2 = 2.0;
System.out.println(d1 == d2);  //false

i1、i2、i3都是常量池中的对象,i4、i5、i6是堆中的对象。

i4 == i5 + i6 返回true是因为,涉及运算 i5、i6会进行自动拆箱操作、数值相加,即i4 == 127。Integer对象无法直接与数值进行比较,故i4同样自动拆箱为int值127,最终转为 127==127 进行数值比较。

四、总结

  • 基本类型与包装类型可通过 自动装箱拆箱 相互转换;

  • 为提高性能、节省内存,Java中Byte、Short、Integer、Long、Character、Boolean包装类实现了缓存技术,Float和Double没有实现常量池技术;

  • 当使用new关键字创建包装类对象时,都会在堆中创建新的对象;只有使用字面量赋值、且在缓冲池范围内 才可使用对象池,否则还是会在堆中创建对象

  • 包装类的两个变量之间的比较 推荐使用equals进行(比较的是值而非地址);

  • Integer i2=5,自动装箱,底层调用Integer i2=Integer.valueOf(5) 方法。先从常量池中查找是否已经存在该值的Integer对象,如果存在则直接返回常量池中的对象;不存在 就创建一个新的Integer对象并存储在常量池中(当范围在-128~127之间时,多次调用会取得同一个对象的引用);

  • Integer i1=new Integer(5) ,每次都会新建一个对象;

  • 对于Integer.valueOf方法,只有当数值在 -128~127 之间时,才会被缓存。若超出该范围 仍会创建新的对象。编译器在自动装箱过程中会调用valueOf()方法,因此如果多个Integer实例使用自动装箱来创建、在-128~127范围内、并且值相同,就会引用相同的对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1627808.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ADOP带您科普什么是单纤双向BiDi光模块?一根光纤,双向通信:单纤双向模块的革命性技术。

单纤双向光模块&#xff08;也称为BiDi光模块&#xff09;是一种使用WDM&#xff08;波分复用&#xff09;双向传输技术的光模块&#xff0c;它在一根光纤上实现了同时进行光通道内的双向传输。相比常规光模块&#xff08;有两个光纤插孔&#xff09;&#xff0c;BiDi光模块只有…

playwright 使用

pip install playwright 是一个命令&#xff0c;用于通过 Python 的包管理工具 pip 安装 Playwright 库。Playwright 是一个用于端到端网页测试的库&#xff0c;支持多种浏览器&#xff0c;包括 Chromium、Firefox 和 WebKit。 执行 pip install playwright 命令后&#xff0c…

SCSS全局配置 vue项目(二)

目录 1、先要查看node版本 2、安装对应的node-sass、sass-loader版本 2.1根据项目使用的node版本安装对应的node-sass版本 2.2根据node-sass版本选择兼容的sass-loader版本&#xff0c;不然项目无法正常运行 3、在 vue.config.js 中配置&#xff1a; 4、在组件中…

全志ARM-官方库SDK安装和验证

进入界面&#xff0c;输入以下指令 git clone https://github.com/orangepi-xunlong/wiringOP //下载源码 cd wiringOP //进入文件夹 sudo ./build clean //清除编译信息 sudo ./build …

Kubernetes - CentOS7搭建k8s_v1.18集群高可用(kubeadm/二进制包部署方式)实测配置验证手册

Kubernetes - CentOS7搭建k8s集群高可用&#xff08;kubeadm/二进制包部署方式&#xff09;实测配置验证手册 前言概述&#xff1a; 一、Kubernetes—k8s是什么 Kubernetes 这个名字源于希腊语&#xff0c;意为“舵手“或”飞行员"。 Kubernetes&#xff0c;简称K8s&#…

​解析什么是物联网接入网关?-天拓四方

随着物联网技术的飞速发展&#xff0c;越来越多的设备、传感器和系统被连接到互联网&#xff0c;形成了一个庞大的、相互连接的智能网络。在这个网络中&#xff0c;物联网接入网关扮演着至关重要的角色&#xff0c;它不仅是连接物联网设备和云平台的桥梁&#xff0c;还是实现设…

iOS——NSCache

什么是NSCache NSCache是Foundation框架中的一个类&#xff0c;用于在iOS和macOS应用程序中进行临时性的内存缓存。它提供了一种轻量级的缓存机制&#xff0c;可以用于存储临时性的数据&#xff0c;例如图片、对象等。NSCache的主要特点和用法包括&#xff1a; 临时性缓存&…

【MyBatisPlus】一、公共字段填充配置

目录 一、实体类配置 二、配置MyBatis Plus元对象处理器 三、接口字段自动填充 在使用mybatisplus项目中设置公共字段填充&#xff0c;可以按如下进行配置 一、实体类配置 TableField(value "create_time",fill FieldFill.INSERT)private LocalDateTime createTime…

《苍穹外卖》Day10部分知识点记录

一、Spring Task 介绍 Spring Task是Spring框架提供的任务调度工具&#xff0c;可以按照约定的时间自动执行某个代码逻辑。 定位&#xff1a;定时任务框架 作用&#xff1a;定时自动执行某段Java代码 应用场景&#xff1a;只要是需要定时处理的场景都可以使用Spring Task …

贪吃蛇大作战【纯c语言】

如果有看到不懂的地方或者对c语言某些知识忘了的话&#xff0c;可以找我之前的文章哦&#xff01;&#xff01;&#xff01; 个人主页&#xff1a;小八哥向前冲~-CSDN博客 所属专栏&#xff1a;c语言_小八哥向前冲~的博客-CSDN博客 贪吃蛇游戏演示&#xff1a; 贪吃蛇游戏动画演…

国产3D自研技术如何突围?眸瑞科技给3D建设、管理带来全新模式

眸瑞科技是全球领先的数字孪生引擎技术及服务提供商&#xff0c;它专注于让一切3D模型在全网多端轻量化处理与展示&#xff0c;为行业数字化转型升级与数字孪生应用提供成套的国产自研3D可视化技术、产品与服务。 引言 眸瑞科技是全球领先的数字孪生引擎技术及服务提供商&…

Zynq 7000 系列中的Interconnect(互联)简介

PS&#xff08;处理器子系统&#xff09;内部的互联结构包含了多个交换机&#xff0c;用于通过AXI点对点通道连接系统资源。这些通道负责在主机和从机客户端之间进行地址、数据和响应事务通信。该互联结构负责管理多个待处理的事务&#xff0c;并且为Arm CPU设计了低延迟路径&a…

Spark 基础

/* Why Spark一、MapReduce编程模型的局限性1、繁杂&#xff1a;只有Map和Reduce两个操作&#xff0c;复杂的逻辑需要大量的样板代码2、处理效率低&#xff1a;2.1、Map中间结果写磁盘&#xff0c;Reduce写HDFS&#xff0c;多个Map通过HDFS交换数据2.2、任务调度与启动开销大3、…

Unity对应的c#版本

本文主要是记录一下unity已经开始兼容c#的版本和.net版本&#xff0c;以便更好的利用c#的特性。 c#和.net对应情况 微软已经将.net开发到.net 9了&#xff0c;但是unity的迭代速度远没有c#迭代速度快&#xff0c;已知unity最新的LTS版本unity2023已经兼容了c#9 可以在unity手册…

汽车底盘域的学习笔记

前言&#xff1a;底盘域分为传统车型底盘域和新能源车型底盘域&#xff08;新能源系统又可以分为纯电和混动车型&#xff0c;有时间可以再研究一下&#xff09; 1&#xff1a;传统车型底盘域 细分的话可以分为四个子系统 传动系统 行驶系统 转向系统 制动系统 1.1传动系…

中电金信:向“新”而行——探索融合架构的项目管理在保险行业的应用

近年来&#xff0c;险企在政策推动、市场牵引、自身发展、新技术应用日趋成熟等内外部因素的驱动下&#xff0c;积极投身到数字化转型的浪潮中。在拜访各类保险客户和合作项目的过程中&#xff0c;我们发现不少险企在数字化转型中或多或少都面临着战略如何落地、技术如何承接和…

Java 基础常见面试题整理

目录 1、java的基本数据类型有哪些&#xff1f;2、java为什么要有包装类型&#xff1f;3、String a "123" 和 String a new String("123") 区别&#xff1f;4、String、StringBuilder和StringBuffer的区别&#xff1f;5、如何理解面向对象和面向过程&…

第72天:漏洞发现-Web框架中间件联动GobyAfrogXrayAwvsVulmap

案例一&#xff1a;某 APP-Web 扫描-常规&联动-Burp&Awvs&Xray Acunetix 一款商业的 Web 漏洞扫描程序&#xff0c;它可以检查 Web 应用程序中的漏洞&#xff0c;如 SQL 注入、跨站脚本攻击、身份验证页上的弱口令长度等。它拥有一个操作方便的图形用户界 面&#…

C++系列-输入输出

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” C输入和输出 我们都知道C语言的输出是用printf函数来实现的&#xff0c;那么C呢&#xff0c;它的实现逻辑是什么呢&#xff0c;让我们一起来看一下&#xff0c; #include<i…

miniTry:Python实现web搜索(全自动+程序操控)

声明&#xff1a;本问给出了全部代码--可以复现--亲测有效 :) [ 代码为图片--> 强制自己去敲一次 又不多] 1.打开网站&#xff1a; 2.利用id去定位到我们要进行输入的内容&#xff08;bing可以直接进行搜索&#xff0c;而csdn需要登录&#xff0c;所以我们用csdn做演示&…