【java笔记】泛型、包装类、自动装箱拆箱与缓存机制
一、泛型:类型安全的基石
1. 泛型的本质与原理
Java 泛型(Generics)是 JDK 5 引入的特性,通过类型参数化实现代码的通用性。泛型类、接口和方法允许在定义时声明类型参数(如 T、E、K、V),这些参数在使用时被具体类型替换。其核心原理是类型擦除(Type Erasure):编译时泛型类型信息被擦除,替换为原始类型(如 Object),并插入必要的类型转换。运行时 JVM unaware 泛型,仅保留原始类型。
1.1 泛型的核心价值
- 类型安全:编译期检查类型匹配,避免运行时
ClassCastException。 - 代码复用:通过参数化类型,一个类/接口/方法可处理多种数据类型。
- 消除强制转换:泛型自动推导类型,简化代码(如集合操作)。
// 非泛型时代的集合操作
List list = new ArrayList();
list.add("Hello");
String str = (String) list.get(0); // 强制转换// 泛型时代
List<String> list = new ArrayList<>();
list.add("Hello");
String str = list.get(0); // 无需转换
// 泛型类示例
class Box<T> {private T value;public void set(T value) { this.value = value; }public T get() { return value; }
}// 使用泛型
Box<String> stringBox = new Box<>();
stringBox.set("Java");
String str = stringBox.get(); // 无需强制类型转换
1.2 泛型擦除:运行时的类型透明
- 原理:编译后泛型类型被擦除,替换为原始类型(如
T→Object,有界类型→上限类型)。 - 影响:
- 运行时无法获取泛型具体类型(
List<String>与List<Integer>运行时类型相同)。 - 不支持基本类型(因擦除后无法区分
int和Integer),必须使用包装类。
- 运行时无法获取泛型具体类型(
1.3 泛型的规则与限制
- 类型参数仅支持引用类型:
List<int>非法,需使用List<Integer>。 - 通配符(Wildcards):
<?>:未知类型(等效Object)。<? extends T>:上界通配符(允许T及其子类)。<? super T>:下界通配符(允许T及其父类)。
- 多边界限制:
<T extends ClassA & InterfaceB>(类在前,接口在后)。 - 泛型方法:独立于类的类型参数,静态方法必须声明自己的泛型。
// 泛型方法示例
public static <T> void print(T element) {System.out.println(element.getClass());
}// 通配符应用:遍历任意类型的集合
public static void printCollection(Collection<?> coll) {for (Object element : coll) {System.out.println(element);}
}
1.4 高级应用:泛型与设计模式
- 工厂模式:泛型工厂方法创建类型安全的实例。
- 策略模式:泛型接口定义通用算法(如
Transformer<T, R>)。 - 容器类设计:实现通用数据结构(如
Stack<T>、Queue<T>)。
// 泛型接口与实现
interface Transformer<T, R> {R transform(T input);
}class UpperCaseTransformer implements Transformer<String, String> {public String transform(String input) {return input.toUpperCase();}
}
2. 泛型为何不支持基本数据类型?
- 擦除机制限制:基本类型(如
int)非对象,无法替换为Object,而包装类(如Integer)是引用类型,符合泛型要求。 - JVM 兼容性:JVM 字节码基于对象引用,基本类型需特殊指令处理,泛型支持基本类型会破坏兼容性。
- 结论:泛型中必须使用包装类,依赖自动装箱拆箱实现透明转换。
3. 泛型的优势
- 类型安全:编译期检查类型错误,避免运行时
ClassCastException。 - 代码复用:一套逻辑处理多种类型,减少冗余。
- 可读性提升:明确类型信息,无需强制转换。
二、包装类与自动装箱拆箱
1. 基本类型与包装类的映射
| 基本类型 | 包装类 | 缓存范围 |
|---|---|---|
byte | Byte | -128 ~ 127 |
short | Short | -128 ~ 127 |
int | Integer | -128 ~ 127(可配置) |
long | Long | -128 ~ 127 |
char | Character | 0 ~ 127 |
boolean | Boolean | TRUE/FALSE |
float | Float | 无缓存 |
double | Double | 无缓存 |
2. 自动装箱(Autoboxing)与拆箱(Unboxing)
- 装箱:基本类型 → 包装类(编译器自动调用
valueOf())。 - 拆箱:包装类 → 基本类型(编译器自动调用
xxxValue())。
// 自动装箱
Integer numBoxed = 100; // 等价于 Integer.valueOf(100)// 自动拆箱
int numUnboxed = numBoxed; // 等价于 numBoxed.intValue()// 集合中的自动装箱
List<Integer> list = new ArrayList<>();
list.add(200); // 自动装箱为 Integer
int val = list.get(0); // 自动拆箱为 int
3. 装箱拆箱的性能考量
- 频繁操作(如循环中)可能引发性能损耗(对象创建/销毁)。
- 建议:对性能敏感场景(如大数据计算)优先使用基本类型。
三、Integer 缓存机制
1. 缓存原理
Integer 缓存 -128 到 127 范围内的对象(通过静态内部类 IntegerCache 实现)。调用 valueOf() 或自动装箱时:
- 范围内:返回缓存对象(共享引用)。
- 范围外:新建
Integer对象(堆内存)。
Integer a = 100; // 缓存范围内,共享引用
Integer b = 100;
System.out.println(a == b); // true(引用相同)Integer c = 200; // 范围外,新建对象
Integer d = 200;
System.out.println(c == d); // false(引用不同)
2. 缓存范围的扩展
通过 JVM 参数 -Djava.lang.Integer.IntegerCache.high=255 可扩大缓存上限(如 255),适用于高频使用较大整数的场景。
3. 比较陷阱:== vs equals()
==比较引用,缓存范围内为true,否则为false。equals()比较值,始终正确。
Integer x = 127;
Integer y = 127;
System.out.println(x == y); // true(缓存内)Integer m = 128;
Integer n = 128;
System.out.println(m == n); // false(缓存外)
System.out.println(m.equals(n)); // true(值相等)
四、字符串转数字:parseInt vs valueOf
| 方法 | 返回类型 | 缓存影响 | 异常处理 |
|---|---|---|---|
Integer.parseInt(s) | int(基本类型) | 无(直接返回值) | 抛出 NumberFormatException |
Integer.valueOf(s) | Integer(对象) | 缓存范围内返回缓存对象 | 同上 |
// parseInt:直接获取基本类型
int num1 = Integer.parseInt("123"); // 123// valueOf:返回 Integer 对象(自动拆箱)
int num2 = Integer.valueOf("456"); // 自动拆箱为 int
Integer obj = Integer.valueOf("789"); // 保持包装类// 基数转换(16 进制)
int hex = Integer.valueOf("FF", 16); // 255
最佳实践:
- 需基本类型时优先
parseInt(避免对象开销)。 - 需对象或利用缓存时用
valueOf(如频繁小整数场景)。
五、扩展知识:深入理解底层机制
1. 基本类型与包装类的存储位置
- 基本类型:局部变量存栈,成员变量存堆(对象属性)。
- 包装类:对象存堆,引用存栈。
2. 字符串常量池与对象创建
String s1 = "abc"; // 常量池存储
String s2 = new String("abc"); // 堆内存存储,常量池引用
System.out.println(s1 == s2); // false(引用不同)
3. 深浅拷贝与泛型限制
- 浅拷贝:复制引用,共享对象(如默认
clone())。 - 深拷贝:递归复制对象,完全独立(需手动实现或序列化)。
- 泛型嵌套限制:受类型擦除影响,Java 不支持复杂嵌套泛型传递,设计 API 时需规避。
六、总结:最佳实践与编码规范
-
泛型使用:
- 优先使用泛型类/方法,避免原始类型。
- 类型参数命名遵循
T、E等约定,提高可读性。
-
包装类与缓存:
- 小整数(-128~127)直接赋值(利用缓存)。
- 比较包装类时始终用
equals(),避免==陷阱。
-
自动装箱拆箱:
- 避免在循环中高频使用,优先基本类型。
- 集合操作透明处理,无需显式转换。
-
字符串转换:
- 明确需求:基本类型用
parseInt,对象用valueOf。 - 处理异常:包裹
try-catch处理非数字字符串。
- 明确需求:基本类型用
七、代码示例汇总
// 泛型类
class GenericBox<T> {private T value;public void set(T value) { this.value = value; }public T get() { return value; }
}// 自动装箱拆箱与缓存
Integer a = 100; // 缓存内
Integer b = Integer.valueOf(100); // 同上
int c = a + b; // 自动拆箱为 int,计算后装箱// 字符串转换
try {int num = Integer.parseInt("123");Integer obj = Integer.valueOf("456");
} catch (NumberFormatException e) {System.err.println("Invalid number format: " + e.getMessage());
}// 缓存扩展(JVM 参数:-Djava.lang.Integer.IntegerCache.high=255)
Integer x = 200;
Integer y = 200;
System.out.println(x == y); // true(若缓存上限调整为 255)
八、常见面试题解答
-
为什么泛型不支持基本类型?
答:泛型擦除后类型替换为Object,基本类型非对象,无法赋值。需通过包装类实现。 -
Integer 缓存范围为何是 -128~127?
答:JDK 设计团队认为该范围是日常高频使用的整数,缓存优化性能,超出范围创建新对象以节省内存。 -
自动装箱拆箱的性能影响?
答:单次操作影响可忽略,频繁操作(如循环)建议使用基本类型避免对象开销。 -
如何比较两个 Integer 对象的值?
答:使用equals()方法,避免==比较引用(缓存范围内除外)。
参考资料:
- Java 泛型官方文档
- Integer 源码解析
- JVM 类型擦除机制详解
- Java 基本数据类型 vs 包装类
相关文章:
【java笔记】泛型、包装类、自动装箱拆箱与缓存机制
一、泛型:类型安全的基石 1. 泛型的本质与原理 Java 泛型(Generics)是 JDK 5 引入的特性,通过类型参数化实现代码的通用性。泛型类、接口和方法允许在定义时声明类型参数(如 T、E、K、V),这些…...
数仓开发那些事(11)
某神州优秀员工:一闪,领导说要给我涨米。 一闪:。。。。(着急的团团转) 老运维:Oi,两个吊毛,看看你们的hadoop集群,健康度30分,怎么还在抽思谋克?…...
自动化测试【Python3.7+Selenium3】
1、自动化测试环境搭建之selenium3安装 方法1:cmd环境下,用pip install selenium (速度很慢,不推荐) 方法2:下载selenium安装包手动安装 下载地址:https://pypi.org/project/selenium/ 在解压…...
从零开始完成冒泡排序(0基础)——C语言版
文章目录 前言一、冒泡排序的基本思想二、冒泡排序的执行过程(一)第一轮排序(二)第二轮排序(三)第三轮排序(四)第四轮排序 三、冒泡排序的代码实现(C语言)&am…...
工业级POE交换机:助力智能化与自动化发展
随着工业互联网、物联网(IoT)和自动化技术的快速发展,网络设备在工业领域的应用日益广泛。然而,在严苛环境下,传统网络设备往往难以应对复杂的温湿度变化、电磁干扰和供电不稳定等挑战。为同时满足数据传输与供电一体化…...
使用ZYNQ芯片和LVGL框架实现用户高刷新UI设计系列教程(第五讲)
在上一讲我们讲解了按键回调函数的自定义函数的用法,这一讲继续讲解回调函数的另一种用法。 首先我们将上一讲做好的按键名称以及自定义回调事件中的按键名称修改,改为默认模式为“open”当点击按键时进入回调函数将按键名称改为“close”,具…...
Burp Suite Professional 2024版本安装激活指南
文章目录 burpsuite简介Burp Suite的主要组件:Burp Suite的版本使用场景 下载地址使用教程 burpsuite简介 Burp Suite 是一个广泛使用的网络安全测试工具,特别是在Web应用程序安全领域。它主要用于发现和修复Web应用中的安全漏洞,特别适用于渗…...
【c++深入系列】:类与对象详解(上)
🔥 本文专栏:c 🌸作者主页:努力努力再努力wz 💪 今日博客励志语录: 你仰望的星辰并非遥不可及,而是跋涉者脚印的倒影;你向往的远方未必需要翅膀,只要脚下始终有路&#x…...
6、进程理论和简单进程创建
一、了解进程推荐看这个视频,很详细 1、概念 进程(Process)程序的运行过程,是系统进行资源分配和调度的独立单元程序的运行过程:多个不同程序 并发,同一个程序同时执行多个任务。 就需要很多资源来实现这个过程。 每个进程都有一…...
java八股文之JVM
1.什么是程序计数器 程序计数器是 JVM 管理线程执行的“定位器”,记录每个线程当前执行的指令位置,确保程序流程的连续性和线程切换的准确性。线程私有的,每个线程一份,内部保存的字节码的行号。用于记录正在执行的字节码指令的地…...
Todesk介绍
文章目录 ToDesk 软件介绍1. 软件概述2. ToDesk 的功能特点2.1 简单易用2.2 高质量的图像与流畅的操作2.3 跨平台支持2.4 多屏显示与协作2.5 文件传输功能2.6 实时聊天与语音通话2.7 远程唤醒与自动启动2.8 多种权限设置与安全性2.9 无需公网 IP 3. ToDesk 的应用场景3.1 个人使…...
每日总结3.27
蓝桥刷题 1. 团建 (树dfs) #include <bits/stdc.h> using namespace std; const int N200005; int a[N],b[N]; int ans; map<int,vector<int>>m1,m2; void dfs(int x,int y,int count) { if(a[x]!b[y]) {return;} ansmax(ans,c…...
Linux 进程3-fork创建子进程继承父进程缓冲区验证
目录 1. fork创建子进程继承父进程缓冲区验证 1.1 write向标准输出(终端屏幕)写入数据验证 1.1.1 write函数写入数据不带行缓冲刷新 \n 1.1.2 write函数写入数据带行缓冲刷新 \n 1.2 fork创建前执行printf函数 1.2.1 fork创建前执行printf函数带\n…...
应用服务接口第二次请求一直pending问题
目录 一、问题背景二、问题排查过程三、解决方案四、总结 一、问题背景 升级内容发布到灰度环境,验证相关服务,查看接口调用日志,发现第一次请求正常,第二次相同接口请求就一直pending,其他服务也是如此 二、问题排查…...
基于FPGA的ESP8266无线数据传输(温湿度DTH11、光照强度BH1750、WIFI模块)连接中国移动onenet云平台,仿真+上板
文章目录 一、创建云平台产品设备二、FPGA仿真WIFI模块通信过程仿真分析2.上板 总结 一、创建云平台产品设备 使用串口助手测试传输过程 相关信息记录 二、FPGA仿真WIFI模块通信过程 仿真分析 //各个状态tx_dataalways (posedge clk or negedge rst_n) beginif(!rst_n) beg…...
5款视觉OCR开源模型
一、号称「世界上最好的 OCR 模型」Mistral OCR Mistral OCR 擅长理解复杂的文档元素,包括交错图像、数学表达式、表格和高级布局(如 LaTeX 格式)。该模型可以更深入地理解丰富的文档,尤其是包含图表、图形、公式和数字的科学论文…...
计算机二级(C语言)考试高频考点总汇(三)—— 结构体和共用体、结构体对齐规则、联合体大小计算
目录 九、结构体和共用体 十、结构体对齐规则 十一、联合体大小计算 九、结构体和共用体 141. 结构体是(不同类型成员的集合),是⼀种用户自定义的数据类型,可以将不同类型的成员组合在⼀起,用于表示(复…...
transformers中学习率warmup策略具体如何设置
在使用 get_linear_schedule_with_warmup(如 Hugging Face Transformers 库中的学习率调度器)时,参数的合理设置需要结合 数据量(dataset size)、批次大小(batch size) 和 训练轮数(…...
“线程通信“一个案例
今天在处理项目需求的时候, 遇到这样一个问题: 项目中需要将用户界面上传的某种模型文件转换成另一种格式的文件, 以便进行预览操作. 而这个转换的过程需要调用到专业软件的脚本程序, 简单来说就是一个比较耗时的步骤. 并且这个转换还要分为两步进行, 需要调用两个脚本程序. 简…...
Charles抓HTTPS包
一、电脑端 1、证书下载与安装 安装完之后,重新点开看一看,确认下证书状态,安装的没问题 2、charles设置 抓电脑端要把这个点开 3、抓包 正经人看浏览器的包一般是F12,不过这里就用浏览器代替电脑软件了 如果配制好charles之后…...
JavaScript模板字符串:
1.示例代码(包含注释): <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>JS-数…...
【系统架构设计师】数据库系统 ③ ( 数据库设计过程 | 概念结构设计 | ER 图 简介 | 概念设计阶段 工作拆分 )
文章目录 一、数据库设计过程 概述二、ER 图 简介1、ER 图 概念2、ER 图 示例3、ER 图 关系类型① 一对一 ( 1:1 ) 关系② 一对多 ( 1:n ) 关系③ 多对多 ( n:n ) 关系 三、概念设计阶段 工作拆分 一、数据库设计过程 概述 数据库设计过程 : 需求分析阶段 : 明确 用户需求 ; …...
Servlet开发与生命周期详解-2
一、在集成开发环境当中开发Servlet程序 1.集成开发工具很多,其中目前使用比较多的是: IntelliJ IDEA(这个居多,IDEA在提示功能方面要强于Eclipse,也就是说IDEA使用起来比Eclipse更加智能,更好用。JetBrai…...
将网络安全和第三方风险管理与业务目标相结合
在网络安全风险领域,我们经常遇到与企业语言不通的问题。这可能导致网络安全风险管理计划得不到支持。当发现网络安全风险时,困难在于以符合组织语言和目标的方式来表达它。 第三方风险属于另一个灰色地带。在组织内部,许多利益相关者&#…...
NO.58十六届蓝桥杯备战|基础算法-枚举|普通枚举|二进制枚举|铺地毯|回文日期|扫雷|子集|费解的开关|Even Parity(C++)
枚举 顾名思义,就是把所有情况全都罗列出来,然后找出符合题⽬要求的那⼀个。因此,枚举是⼀种纯暴⼒的算法。 ⼀般情况下,枚举策略都是会超时的。此时要先根据题⽬的数据范围来判断暴⼒枚举是否可以通过。 使⽤枚举策略时…...
postman测试调用WebService时不会自动添加命名空间
这两天在学习调用webservice,发现Postman直接调用时,返回 no namesapce on "myservice" element. you must send a soap message 找了很久,才明白,Postman 不会自动为请求添加命名空间,得手动在请求的 XML 数…...
【已解决】Git:为什么 .gitignore 不生效?如何停止跟踪已提交文件并阻止推送?
你可能遇到的问题 你已经提交了某个文件夹(如 dataset)到 Git 仓库,之后修改了它,但发现修改内容被 Git 持续跟踪,无法通过 .gitignore 忽略。尝试在 .gitignore 中添加规则后,修改的文件仍然显示为"…...
WebRTC协议全面教程:原理、应用与优化指南
一、WebRTC协议概述 **WebRTC(Web Real-Time Communication)**是一种开源的实时通信协议,支持浏览器和移动应用直接进行音频、视频及数据传输,无需插件或第三方软件。其核心特性包括: P2P传输:点对点直连…...
深度学习框架对比评测:TensorFlow、PyTorch、PaddlePaddle与MXNet的技术演进与应用实践
本文针对当前主流的四大深度学习框架(TensorFlow 2.15、PyTorch 2.2、PaddlePaddle 2.5、MXNet 1.9),从架构设计、开发效率、训练性能、部署能力及生态系统等维度展开系统性评测。通过图像分类、自然语言处理、强化学习三类典型任务的基准测试…...
Ethernet(以太网)详解
一、Ethernet的定义与核心特性 以太网(Ethernet)是一种 基于IEEE 802.3标准的局域网(LAN)技术,用于设备间通过有线或光纤介质进行数据通信。其核心特性包括: 标准化:遵循IEEE 802.3系列协议&am…...
