当前位置: 首页 > news >正文

Java核心概念深度解析:从包装类到泛型的全面指南

引言:为什么Java需要包装类?

Java作为一门"万物皆对象"的面向对象编程语言,在设计上面临着一个重要挑战:如何处理基本数据类型与对象体系的关系?这就引出了我们今天要深入探讨的包装类概念。

基本数据类型与包装类的对应关系

包装类存在的意义
  • 让基本数据类型具备对象的特性
  • 可以在集合框架中使用(集合只能存储对象)
  • 提供丰富的工具方法(如类型转换、数值处理等)
  • 实现null值的表示

深入剖析Integer的128陷阱

现象展示

Integer i = 127;
Integer j = 127;
System.out.println(i == j); // 输出:TrueInteger k = 128;
Integer l = 128;
System.out.println(k == l); // 输出:False

底层原理:Integer缓存机制

Java为了优化性能,对常用的Integer值进行了缓存:

public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);
}

缓存范围:-128 到 127

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 propertyint 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);h = Math.min(i, Integer.MAX_VALUE - (-low) -1);} catch(NumberFormatException nfe) {// 忽略配置异常}}high = h;cache = new Integer[(high - low) + 1]; // 缓存数组大小:256int j = low;for(int k = 0; k < cache.length; k++)cache[k] = new Integer(j++);}
}

缓存映射表解析

数组索引

Integer值

0

-128

1

-127

...

...

128

0

129

1

...

...

138

10

...

...

255

127

计算原理:

// 值为10的Integer在缓存数组中的位置

int index = 10 + 128; // 138

Integer cachedValue = IntegerCache.cache[138]; // 获取值为10的缓存对象

自动拆装箱机制详解

什么是自动拆装箱?

自动装箱:基本数据类型 → 包装类

自动拆箱:包装类 → 基本数据类型

// 自动装箱示例
Integer m = 10; 
// 等价于:Integer m = Integer.valueOf(10);// 自动拆箱示例  
int a = m;
// 等价于:int a = m.intValue();

输出结果不同

valueof判断值是否在范围(-128~127)里

Integer 是引用数据类型(包装类)

valueOf() 方法返回的是 Integer 类型的引用

对于引用数据类型,传递地址;基本数据类型传递值

第一个:

比较地址,地址相同,返回true

第二个:

new integer 创建新的地址

两个指向的地址不同

128不在valueof范围里->创建新对象

总结

使用Integer定义的数据会进行自动装箱,自动装箱调用valueOf()方法,该方法会判断我们的入参是否在-128~127之间,如果在这个之间则会返回一个已经存在的对象的地址(该对象在Cache数组当中,Cache数组是一个从-128~127的数组),不会重新创建对象,否则会重新创建对象。

综合测试案例

public class Demo {public static void main(String[] args) {int a = 10;int b = 10;Integer a1 = 10;          // 自动装箱,使用缓存Integer b1 = 10;          // 自动装箱,使用缓存Integer a2 = new Integer(10);  // 新建对象Integer b2 = new Integer(10);  // 新建对象System.out.println(a == b);        // true - 基本类型比较值System.out.println(a1 == b1);      // true - 同一缓存对象System.out.println(a2 == b2);      // false - 不同对象System.out.println(a1 == a);       // true - 自动拆箱System.out.println(a1.equals(a));  // true - 自动拆箱比较值System.out.println(a1 == a2);      // false - 缓存对象 vs 新对象System.out.println(a == a2);       // true - 自动拆箱}
}

== 与 equals 的深度区别

基本概念

// == 比较规则:
// - 基本数据类型:比较值是否相同
// - 引用数据类型:比较地址是否相同// equals 比较规则:
// - Object类中默认使用 == 比较地址
// - String等类重写了equals方法,比较内容
// - 包装类重写了equals方法,比较值

实际应用场景

== 的使用场景
  • 基本数据类型的值比较
  • 引用类型的同一性检查(是否同一个对象)
equals 的使用场景
  • 对象内容的比较
  • 集合操作中的元素查找和比较
  • 需要逻辑相等性判断的场景

final关键字的全面解析

final的不同用法

修饰变量
final int MAX_VALUE = 100;  // 常量,不可修改
final List<String> list = new ArrayList<>(); 
// list引用不可变,但list内容可以修改

修饰方法:

public final void cannotOverride() {// 该方法不能被子类重写
}

修饰类:

public final class String {  // 该类不能被继承private final char value[];  // 字符数组不可变
}

final的内存语义

  • final 固定的是引用地址,不是对象内容
  • = null 和 = "" 都不报错,因为都是在赋值地址
  • 只能被赋值一次,在初始化时完成

静态代码块的特殊作用

static代码块的特点

public class Example {static {// 在类加载时执行,早于main方法System.out.println("静态代码块执行");}public static void main(String[] args) {System.out.println("main方法执行");}
}
执行特性
  • 类加载时自动执行
  • 只执行一次
  • 按代码书写顺序执行
  • 常用于静态资源初始化

泛型:类型安全的保障

泛型的基本概念

泛型:泛指一切类型,提供编译期类型安全检查

泛型在栈实现中的应用

public class Stack<E> {private Object[] arr;private int top;public Stack(int size) {arr = new Object[size];top = -1;}public void push(E value) {if(top == arr.length - 1) {System.out.println("栈满");return;}arr[++top] = value;}@SuppressWarnings("unchecked")public E pop() {if(top == -1) {System.out.println("栈空");return null;}return (E) arr[top--];}
}// 使用示例
Stack<Integer> intStack = new Stack<>(10);
Stack<String> strStack = new Stack<>(10);
Stack<Double> doubleStack = new Stack<>(10);

总结与最佳实践

包装类使用要点

  1. 比较操作:始终使用equals()而不是==比较包装类
  2. 缓存意识:了解-128到127的缓存范围,避免128陷阱
  3. null安全:包装类可能为null,使用时注意空指针检查
  4. 性能考量:在性能敏感场景,考虑使用基本数据类型

http://www.dtcms.com/a/560619.html

相关文章:

  • 灵途科技亮相NEPCON ASIA 2025 以光电感知点亮具身智能未来
  • flash-attn安装过程中编译错误
  • 世界最受欢迎的免费架站平台经营网站 备案信息
  • 7.1 阴影贴图
  • Elastic AI agent builder 介绍(三)
  • React18中在有路由的情况下父组件如何给子组件传递数据?
  • 边缘计算和云计算有什么区别?
  • 做哪种网站流量大嵌入式软件开发工程师工作内容
  • 【第二十周】机器学习笔记09
  • Linux定时任务:crontab使用教程(附案例)
  • 网站建设配色方案wordpress对接静态网页
  • Instant Mail临时邮箱v7.3.0 最新解锁版
  • MPK(Mirage Persistent Kernel)源码笔记(5)--- 执行引擎
  • 微网站菜单网站开发用哪个框架
  • 【Cache缓存】基本概念
  • 数据结构:单链表(1)
  • 4.2 【2018统考真题】
  • swoole怎么做直播网站网站的安全维护
  • 学做ps的软件的网站新网站百度收录要几天
  • Redis数据结构命令
  • 【Dify】通过Docker-Compose的安装部署(Linux系统)
  • SEO优化学习指南
  • betaflight基于自制特殊固件配置文件的 F405 飞控固件升级与配置迁移指南(附 AI 沟通命令适配技巧)
  • 通过条形码获取对应商品信息python程序
  • LaTeX基础使用
  • 【Linux】基础指令(4):基础指令热键shell运行原理
  • [Dify 实战] 搭建企业内网知识库系统:Dify 自托管部署全攻略
  • LVS 负载
  • NFS服务器的搭建
  • 【高并发服务器】十三、TcpServer服务器管理模块