2026/3/24 21:57:04
网站建设
项目流程
门户网站首页模板,有那些做任务的网站,wordpress查询数据库插件,邯郸网络企业版本系列可作为JAVA学习系列的笔记#xff0c;文中提到的一些练习的代码#xff0c;小编会将代码复制下来#xff0c;大家复制下来就可以练习了#xff0c;方便大家学习。 点赞关注不迷路#xff01;您的点赞、关注和收藏是对小编最大的支持和鼓励#xff01; 系列文章目录…本系列可作为JAVA学习系列的笔记文中提到的一些练习的代码小编会将代码复制下来大家复制下来就可以练习了方便大家学习。点赞关注不迷路您的点赞、关注和收藏是对小编最大的支持和鼓励系列文章目录JAVA初阶---------已更完JAVA数据结构 DAY1-集合和时空复杂度JAVA数据结构 DAY2-包装类和泛型目录目录系列文章目录目录前言一、包装类基本类型的 “对象化” 解决方案1.1 为什么需要包装类1.2 基本类型与包装类对应关系1.3 装箱与拆箱基本类型与包装类的转换手动装箱与拆箱自动装箱与拆箱JDK 1.51.4 面试高频自动装箱的缓存机制问题引入答案解析二、泛型让代码 “通用化” 的利器2.1 什么是泛型2.2 为什么需要泛型—— 解决 “万能类型” 的痛点问题代码示例痛点分析泛型的解决方案2.3 泛型类定义与使用泛型类语法泛型类改写示例泛型类使用要点2.4 泛型的底层实现擦除机制核心原理验证查看字节码两个关键问题问题 1为什么不能直接 new T []问题 2类型擦除一定替换为 Object 吗2.5 泛型的上界限制类型实参范围为什么需要上界语法格式示例 1单边界继承类示例 2多边界接口 类2.6 泛型方法独立于泛型类的通用方法什么是泛型方法语法格式关键要点示例泛型交换方法三、核心总结与实战建议1. 包装类核心要点2. 泛型核心要点3. 实战建议总结前言小编作为新晋码农一枚会定期整理一些写的比较好的代码作为自己的学习笔记会试着做一下批注和补充如转载或者参考他人文献会标明出处非商用如有侵权会删改欢迎大家斧正和讨论在 Java 编程中包装类和泛型是两个贯穿始终的核心知识点尤其是在集合框架中应用广泛。掌握这两个概念不仅能写出更安全、通用的代码更是读懂 Java 集合源码的基础。本文将从实际应用场景出发结合案例和底层原理详细拆解包装类和泛型的核心知识点。一、包装类基本类型的 “对象化” 解决方案1.1 为什么需要包装类Java 中基本类型如 int、char、boolean并非继承自 Object这就导致它们无法直接用于需要 Object 类型的场景例如泛型、集合框架。为了解决这个矛盾Java 为每个基本类型都提供了对应的包装类将基本类型 “包装” 成对象从而具备了 Object 类的所有特性。1.2 基本类型与包装类对应关系基本数据类型包装类特殊说明byteByte首字母大写shortShort首字母大写intInteger特殊命名非 IntlongLong首字母大写floatFloat首字母大写doubleDouble首字母大写charCharacter特殊命名非 CharbooleanBoolean特殊命名非 Bool记忆技巧除了 int→Integer、char→Character其余基本类型的包装类都是首字母直接大写。1.3 装箱与拆箱基本类型与包装类的转换手动装箱与拆箱装箱将基本类型转换为对应的包装类对象拆箱将包装类对象转换为对应的基本类型代码示例// 手动装箱基本类型 → 包装类 int i 10; Integer ii Integer.valueOf(i); // 推荐方式缓存优化 Integer ij new Integer(i); // 不推荐直接创建对象无缓存 // 手动拆箱包装类 → 基本类型 int j ii.intValue();自动装箱与拆箱JDK 1.5为了简化开发Java 提供了自动装箱Auto-Boxing和自动拆箱Auto-Unboxing机制编译器会自动完成基本类型与包装类的转换无需手动调用方法。代码示例int i 10; Integer ii i; // 自动装箱等价于 Integer.valueOf(i) Integer ij (Integer)i; // 自动装箱显式强制转换效果同上 int j ii; // 自动拆箱等价于 ii.intValue() int k (int)ii; // 自动拆箱显式强制转换效果同上1.4 面试高频自动装箱的缓存机制问题引入以下代码的输出结果是什么为什么public static void main(String[] args) { Integer a 127; Integer b 127; Integer c 128; Integer d 128; System.out.println(a b); // true System.out.println(c d); // false }答案解析自动装箱时Java 对Integer类型的缓存范围为-128 ~ 127默认值可通过 JVM 参数调整上限。当装箱值在缓存范围内时直接复用缓存中的对象因此a和b指向同一个对象比较结果为true。当装箱值超出缓存范围时会创建新的Integer对象因此c和d是两个不同的对象比较结果为false。注意比较的是对象地址若要比较包装类的值应使用equals()方法。二、泛型让代码 “通用化” 的利器2.1 什么是泛型泛型Generic是 JDK 1.5 引入的新语法核心思想是将数据类型参数化。通俗地说就是让类、方法能够 “适配” 多种数据类型而无需为每种类型单独编写代码。《Java 编程思想》对泛型的描述“一般的类和方法只能使用具体的类型要么是基本类型要么是自定义的类。如果要编写可以应用于多种类型的代码这种刻板的限制对代码的束缚就会很大。”2.2 为什么需要泛型—— 解决 “万能类型” 的痛点在泛型出现之前开发者通常使用Object数组实现 “万能存储”但存在两个严重问题问题代码示例class MyArray { public Object[] array new Object[10]; // 存入数据 public void setVal(int pos, Object val) { this.array[pos] val; } // 获取数据 public Object getPos(int pos) { return this.array[pos]; } } public class TestDemo { public static void main(String[] args) { MyArray myArray new MyArray(); myArray.setVal(0, 10); // 存入 int自动装箱为 Integer myArray.setVal(1, hello); // 存入 String // 编译报错需要强制类型转换 String ret myArray.getPos(1); // 正确写法String ret (String)myArray.getPos(1); } }痛点分析类型不安全可以存入任意类型数据例如数组中同时存入Integer和String容易引发类型转换异常。代码冗余获取数据时必须手动强制类型转换繁琐且易出错。泛型的解决方案通过泛型指定容器的 “数据类型参数”让编译器在编译时进行类型检查既保证了类型安全又避免了手动类型转换。2.3 泛型类定义与使用泛型类语法class 泛型类名称类型形参列表 { // 可以使用类型参数定义成员变量、方法返回值、参数类型 } // 示例单个类型形参 class ClassNameT { } // 示例多个类型形参 class ClassNameT, K, V { } // 示例继承时使用泛型 class ClassNameT extends ParentClassT { }类型形参命名规范约定俗成增强可读性TType类型EElement元素常用于集合KKey键常用于 MapVValue值常用于 MapNNumber数字S、U、V后续类型参数泛型类改写示例class MyArrayT { // 注意不能直接 new T[]需通过 Object 数组转型后续详解 public T[] array (T[]) new Object[10]; // 存入数据参数类型为 T编译时检查类型 public void setVal(int pos, T val) { this.array[pos] val; } // 获取数据返回值类型为 T无需强制转换 public T getPos(int pos) { return this.array[pos]; } } public class TestDemo { public static void main(String[] args) { // 指定类型实参为 Integer容器只能存入 Integer 类型 MyArrayInteger myArray new MyArray(); myArray.setVal(0, 10); myArray.setVal(1, 20); // 无需强制转换编译时直接确定类型 int ret myArray.getPos(1); System.out.println(ret); // 输出 20 // 编译报错类型不匹配String 无法存入 Integer 类型容器 myArray.setVal(2, bit); } }泛型类使用要点指定类型实参创建泛型类对象时需指定具体的类型实参如Integer、String且只能是类类型基本类型需使用包装类。类型推导JDK 1.7 支持 “菱形语法”可省略右侧的类型实参编译器自动推导例如MyArrayInteger myArray new MyArray();。裸类型Raw Type不指定类型实参的泛型类如MyArray myArray new MyArray();是为了兼容老版本 API 保留的机制不推荐使用失去泛型的类型安全保障。2.4 泛型的底层实现擦除机制核心原理Java 的泛型是编译时泛型底层通过 “类型擦除” 实现编译器在编译过程中会将所有泛型类型参数如 T、E替换为其上界类型默认是 Object生成的字节码中不包含泛型类型信息。验证查看字节码通过javap -c 类名命令查看泛型类的字节码以MyArrayT为例// 编译后的字节码关键部分 class MyArrayT { public java.lang.Object[] array; // T[] 被擦除为 Object[] public java.lang.Object getPos(int); // 返回值 T 被擦除为 Object public void setVal(int, java.lang.Object); // 参数 T 被擦除为 Object }两个关键问题问题 1为什么不能直接 new T []// 错误写法编译报错 T[] ts new T[5];原因类型擦除后T[]会被替换为Object[]但编译器无法确定T的具体类型直接创建Object[]并转型为T[]存在类型安全风险例如将Object[]转型为Integer[]后若存入非 Integer 类型数据会引发运行时异常。正确写法通过反射创建指定类型的数组推荐import java.lang.reflect.Array; class MyArrayT { public T[] array; // 构造方法通过 Class 对象指定类型 public MyArray(ClassT clazz, int capacity) { // 反射创建 T 类型数组 array (T[]) Array.newInstance(clazz, capacity); } // 省略 getPos、setVal 方法 } // 使用示例 MyArrayInteger myArray new MyArray(Integer.class, 10); Integer[] integers myArray.array; // 无类型转换异常问题 2类型擦除一定替换为 Object 吗不一定。若泛型指定了上界则擦除后会替换为上界类型后续详解。2.5 泛型的上界限制类型实参范围为什么需要上界有时需要约束泛型的类型实参例如只允许传入数字类型、实现了某个接口的类型此时需要通过 “上界” 来限制。语法格式class 泛型类名称类型形参 extends 类型边界 { }示例 1单边界继承类// 限制 E 必须是 Number 的子类型如 Integer、Double class MyArrayE extends Number { } // 正确Integer 是 Number 的子类型 MyArrayInteger array1 new MyArray(); // 错误String 不是 Number 的子类型编译报错 MyArrayString array2 new MyArray();示例 2多边界接口 类// 限制 E 必须是 Number 的子类型且实现了 Comparable 接口 class MyArrayE extends Number ComparableE { } // 正确Integer 是 Number 子类且实现了 ComparableInteger MyArrayInteger array3 new MyArray(); // 错误Long 是 Number 子类但未显式实现 ComparableLong实际已实现仅作示例 MyArrayLong array4 new MyArray();注意若上界包含类和接口类必须放在最前面如E extends Number ComparableE不能写成E extends ComparableE Number。2.6 泛型方法独立于泛型类的通用方法什么是泛型方法泛型方法是指方法本身带有类型参数可以独立于泛型类存在普通类中也可以定义泛型方法。语法格式方法限定符 类型形参列表 返回值类型 方法名称(形参列表) { ... }关键要点泛型方法的类型参数需在方法限定符如 static之后、返回值类型之前声明例如E。静态方法不能使用泛型类的类型参数因为静态方法属于类泛型类的类型参数在实例化时才确定因此静态泛型方法必须自己声明类型参数。示例泛型交换方法public class Util { // 静态泛型方法交换数组中两个位置的元素 public static E void swap(E[] array, int i, int j) { if (i 0 || i array.length || j 0 || j array.length) { return; } E temp array[i]; array[i] array[j]; array[j] temp; } } // 使用示例 public class Test { public static void main(String[] args) { Integer[] intArr {1, 2, 3, 4, 5}; Util.swap(intArr, 0, 4); // 类型推导E 为 Integer System.out.println(Arrays.toString(intArr)); // 输出 [5, 2, 3, 4, 1] String[] strArr {a, b, c}; Util.Stringswap(strArr, 1, 2); // 显式指定类型实参 System.out.println(Arrays.toString(strArr)); // 输出 [a, c, b] } }三、核心总结与实战建议1. 包装类核心要点基本类型与包装类的对应关系重点记忆 Integer、Character。自动装箱 / 拆箱的底层是valueOf()和xxxValue()方法且Integer有缓存机制-128~127。比较包装类的值用equals()避免用比较地址。2. 泛型核心要点泛型的本质是 “类型参数化”核心作用是编译时类型安全检查和避免手动类型转换。泛型类、泛型方法的定义语法注意静态泛型方法的类型参数声明。底层实现是 “类型擦除”默认替换为 Object指定上界则替换为上界类型。不能直接 new T []需通过反射创建泛型数组的正确写法。泛型上界可约束类型实参支持类和接口多边界类在前接口在后。3. 实战建议集合框架中大量使用泛型如ListInteger、MapString, Object定义集合时务必指定类型实参避免裸类型。自定义通用工具类如数组工具、集合工具时优先使用泛型方法灵活性更高。包装类与泛型结合使用基本类型需通过包装类适配泛型。通过本文的学习相信你已经掌握了包装类和泛型的核心知识点。这两个概念是 Java 集合源码的基础后续学习ArrayList、HashMap等集合时就能轻松理解其泛型设计思路啦如果有疑问欢迎在评论区交流总结以上就是今天要讲的内容本文简单记录了java数据结构仅作为一份简单的笔记使用大家根据注释理解您的点赞关注收藏就是对小编最大的鼓励