初学Java之范型

范型

包装类

包装类的定义

在Java中,包装类(Wrapper Classes) 是连接基本类型与对象世界的桥梁,解决了Java"一切皆对象"理念与基本类型之间的矛盾。

通俗的来说:
包装类存在的根本原因:Java的8个基本类型不是对象,但Java的API(集合、泛型、反射、锁、工具方法)只认对象。包装类就是给基本类型"穿上对象的外衣",让它们能进入对象的世界。

在Java中一切皆为对象,要使基本类型定义的变量变成对象方便以对象的身份进行操作。

相关例子请继续阅读下文。

包装类的作用

场景1:我想把数字放进列表里

👀问题 List 只能存对象不能存 int

java 复制代码
// ❌ 编译报错:List不接受基本类型
List<int> numbers = new ArrayList<>();  

// ✅ 必须用包装类Integer
List<Integer> numbers = new ArrayList<>();
numbers.add(100);  // 100自动变成Integer对象,才能存进去

没有包装类的后果:Java的集合框架对基本类型完全不可用。

场景2:我想让方法返回"没有结果"

👀问题:基本类型必须有值,不能表示"空"

java 复制代码
// 从数据库查用户年龄,用户可能没填
public int getAge(int userId) {
    // 如果没查到,返回什么?返回0?那和"0岁"混淆了!
    return 0;  // ❌ 歧义:是0岁还是没填?
}

// ✅ 用包装类返回null,明确表示"无值"
public Integer getAge(int userId) {
    if (用户不存在) return null;  // 清晰表示"没有数据"
    return 25;  // 自动装箱为Integer
}

没有包装类的后果:无法区分"值为0"和"没有值",数据库映射会出错

场景3:我想用工具类处理数字

👀问题:基本类型没有方法,不能调用功能

java 复制代码
int num = 100;

// ❌ 编译报错:int不是对象,没有方法
num.toString();  
num.compareTo(200);  

// ✅ 包装类有丰富工具方法
Integer obj = 100;
String str = obj.toString();          // "100"
int max = Integer.MAX_VALUE;          // 获取int最大值
String hex = Integer.toHexString(100); // "64"

没有包装类的后果:数字转换、进制转换、比较等操作无法简洁完成。

场景4:泛型方法要求对象类型

👀问题:泛型 不能接受 int

java 复制代码
// 泛型方法:打印任意类型
public <T> void print(T value) {
    System.out.println(value);
}

print("hello");  // ✅ String是对象
print(100);      // ✅ 自动装箱为Integer,Integer是对象

// 如果没有包装类,100无法传入,泛型对基本类型完全失效

没有包装类的后果:泛型编程、反射、集合等高级特性基本类型无法使用。

场景5:我想在同步代码块里用数字作为锁

👀问题: synchronized 必须锁对象

java 复制代码
int count = 0;

synchronized (count) {  // ❌ 编译报错:不能锁基本类型
    count++;
}

// ✅ 包装类是对象,可以锁
Integer count = 0;
synchronized (count) {  // 合法
    count++;  // 拆箱+自增+装箱
}

没有包装类的后果:基本类型无法参与任何需要对象的同步机制。

装箱与拆箱

定义

生活比喻:快递打包

java 复制代码
//伪代码:
基本类型 = 散装水果(int苹果)
包装类   = 快递盒(Integer盒子)

【装箱】:把散装水果装进快递盒
         int 100 →  new Integer(100)  [穿上对象外衣]
//可以变成快递盒,使用快递盒的性质,对应对象的方法与使用

【拆箱】:从快递盒取出水果
         Integer 100 →  int 100      [取出数值]

代码演示:

java 复制代码
//1. 手动装箱拆箱(JDK 5之前)
// 手动装箱
int num = 100;
Integer obj = new Integer(num);  // 基本类型 → 包装类

// 手动拆箱
int back = obj.intValue();        // 包装类 → 基本类型

//2. 自动装箱拆箱(现代Java)
// 自动装箱(编译器帮你装)
Integer obj = 100;  
// 实际执行:Integer.valueOf(100)

// 自动拆箱(编译器帮你拆)
int num = obj;      
// 实际执行:obj.intValue()

为什么要装箱拆箱

java 复制代码
//原因1:集合只存对象
List<int> list = new ArrayList<>();     // ❌ 报错!不能存基本类型
List<Integer> list = new ArrayList<>(); // ✅ 只能存包装类

list.add(100);        // 自动装箱:100 → Integer
int n = list.get(0);  // 自动拆箱:Integer → int

//原因2:需要 null 表示"无值"
int age;              // 默认0,无法表示"没填"
Integer age = null;   // ✅ 可以表示"未知"

//原因3:调用对象方法
int num = 100;
// num.toString();     // ❌ 基本类型没有方法

Integer obj = num;    // 装箱
obj.toString();       // ✅ "100"  包装类有方法

总结

装箱 = 给基本类型穿上对象外衣(进集合、调方法、存null),拆箱 = 从对象里扒出数值(做计算、比较大小)。小整数有缓存,大整数新建;循环别用包装类,空值要防NPE。

范型

范型的定义

一句话定义:
泛型 = 类型的"占位符",让代码可以处理"任意类型",同时保持类型安全。

为什么叫"泛型"?

"泛" = 广泛、通用

"型" = 类型

泛型 = 广泛的类型 = 可以代表"任意类型"的"通用类型参数"

结合生活比喻范型

生活比喻:快递柜
没有泛型(像旧式储物柜)

旧式储物柜:只能存"物品",取出时需要自己辨认

  • 你存了一个"手机",取出一个"物品"
  • 你以为是手机,结果拿出来是双袜子
  • 运行时才发现错误(ClassCastException)

有泛型(像智能快递柜)

智能快递柜:存的时候指定类型,取的时候自动识别

  • 你存"手机"时,柜子贴上"手机柜"标签
  • 取的时候,柜子确保拿出来的一定是手机
  • 放错类型(比如放袜子)时,存的时候就报错,不会等到取的时候

代码实例

场景:存取一个值

没有泛型(JDK 1.5之前):

java 复制代码
// 只能存Object,什么都往里塞
List list = new ArrayList();
list.add("hello");     // 存字符串
list.add(100);         // 存整数(混乱!)
list.add(new Date());  // 存日期

// 取出来全是Object,必须强转
String str = (String) list.get(0);  // ✅ 成功
String num = (String) list.get(1);  // ❌ 运行时报错!ClassCastException

有泛型(现代Java):

java 复制代码
// 指定只能存String
List<String> list = new ArrayList<>();
list.add("hello");     // ✅ 成功
list.add(100);         // ❌ 编译时报错!类型不匹配

// 取出来就是String,无需强转
String str = list.get(0);  // ✅ 安全,编译器保证类型正确

以上代码实例对应上文生活例子

没有泛型时的三大痛点

痛点1:类型不安全(运行时崩溃)

java 复制代码
// 旧式代码:List可以存任意对象
List list = new ArrayList();
list.add("hello");
list.add(100);        // 编译通过!但类型混乱
list.add(new Date());

// 取出来必须强转,极易出错
String s = (String) list.get(1);  // ❌ 运行时报错!100不是String
// 异常:ClassCastException

//后果:程序上线后突然崩溃,无法提前发现问题。

解决方案1:编译时类型检查(提前发现错误)

java 复制代码
List<String> list = new ArrayList<>();  // 指定:只能存String
list.add("hello");      // ✅ 编译通过
list.add(100);          // ❌ 编译报错!类型不匹配

// 错误在写代码时就被发现,不会带到运行时

//价值:程序更稳定,上线后不会因类型错误崩溃。

痛点2:代码重复(复制粘贴)

java 复制代码
// 需要存字符串,写一套StringList
class StringList {
    void add(String s) { ... }
    String get(int index) { ... }
}

// 需要存整数,再写一套IntegerList(代码几乎一样!)
class IntegerList {
    void add(Integer i) { ... }
    Integer get(int index) { ... }
}

// 需要存学生,再写一套StudentList...
// 无穷无尽的复制粘贴!

//后果:100种类型就要写100个类,维护噩梦。

解决方案2:一套代码通用所有类型(消灭复制粘贴)

java 复制代码
// 写一个泛型类,T可以代表任意类型
class Box<T> {
    private T content;
    void set(T value) { content = value; }
    T get() { return content; }
}

// 使用时指定具体类型
Box<String> stringBox = new Box<>();   // T变成String
Box<Integer> intBox = new Box<>();     // T变成Integer
Box<Student> studentBox = new Box<>(); // T变成Student

// 无需重复写三个类!

//价值:代码复用率100%,维护只需改一处。

痛点3:强制类型转换(代码丑陋)

java 复制代码
// 每次取出都要强转,繁琐且危险
List list = new ArrayList();
list.add("hello");

String s = (String) list.get(0);  // 必须写(String),不能省略
// 如果忘了强转?编译报错或运行异常

//后果:代码冗长,可读性差,出错率高。

解决方案3:自动类型推断(无需强转)

java 复制代码
List<String> list = new ArrayList<>();
list.add("hello");

String s = list.get(0);  // ✅ 直接是String,无需强转!
// 编译器自动知道里面是String

//价值:代码简洁,可读性强,出错率为0。

擦除机制

什么是擦除

定义:Java泛型在编译后会被"擦除"成原始类型,运行时完全不知道泛型的存在。

java 复制代码
源代码:List<String>  →  编译后:List(变成原始类型)
源代码:T extends Number  →  编译后:Number(变成边界类型)
源代码:<T>  →  编译后:Object(无边界时变成Object)

为什么需要擦除机制

  1. 历史兼容性(核心原因)
java 复制代码
java
// JDK 1.5之前(2004年)没有泛型,大家这样写:
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);  // 手动强转

// JDK 1.5引入泛型后,必须兼容旧代码:
List<String> newList = oldList;  // 必须能赋值,否则所有旧代码报废

解决方案:擦除成相同类型,保证新旧代码二进制兼容。

擦除的具体规则

在上述场景4中范型只能接受包装类integer,是因为Java中存在一种擦除机制

相关推荐
heartbeat..2 小时前
java中常用的几种加密方式
java·开发语言
QQ22792391022 小时前
Java springboot基于微信小程序的智慧旅游导游系统景点门票酒店预订(源码+文档+运行视频+讲解视频)
java·spring boot·微信小程序·maven·vuejs
小碗羊肉2 小时前
【从零开始学Java | 第三十九篇】 打印流
java·开发语言
晔子yy2 小时前
[JAVA探索之路]带你手写多线程实现生产者-消费者模型
java·开发语言
你不是我我2 小时前
【Java 开发日记】我们来讲一讲 MVCC 的实现原理
java·开发语言
ftpeak2 小时前
网络爬虫Playwright Python 教程:从入门到实战
开发语言·爬虫·python·playwright
啥咕啦呛2 小时前
跟着AI学Java第2天:Java基础语法巩固
java·python·算法
青云交2 小时前
Java 大视界 -- Java 大数据在智能医疗临床路径优化与医疗资源合理利用中的应用(424)
java·drools·spark streaming·智能医疗·apache camel·医疗资源调度·临床路径优化
摸鱼界在逃劳模2 小时前
Java的JDK下载与安装
java·开发语言