Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >深入理解Java包装类与泛型的应用

深入理解Java包装类与泛型的应用

作者头像
学无止尽5
发布于 2024-11-29 04:04:36
发布于 2024-11-29 04:04:36
17800
代码可运行
举报
运行总次数:0
代码可运行

今天我将带领大家进入Java包装类和泛型应用的学习。

我的Java-数据结构专栏Java-数据结构,希望能帮助到大家。

一、Java包装类基础

在Java中,装箱(boxing)是指将基本数据类型(如int, char, double等)转换为对应的包装类对象(如Integer, Character, Double等)的过程。相反,拆箱(unboxing)是指将包装类对象转换回基本数据类型的过程。

从Java 5(JDK 1.5)开始,Java引入了自动装箱和拆箱机制,以简化基本数据类型和包装类之间的转换。这意味着在需要的时候,Java编译器会自动进行装箱和拆箱操作,而不需要程序员显式地调用转换方法。

  1. 包装类的定义与作用:包装类(Wrapper Classes)在Java编程语言中,指的是将基本数据类型(也称为原始数据类型,如int、char、double等)封装为对象的类。这些类使得基本数据类型可以作为对象进行处理,从而提供了更多的灵活性和功能。
  2. Java中的包装类:在Java中,由于基本类型不是继承自Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了 一个包装类型。

注意:​​​​​​​除了 Integer 和 Character, 其余基本类型的包装类都是首字母大写。

自动装箱和拆箱示例代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class BoxingUnboxingExample {
    public static void main(String[] args) {
        // 自动装箱
        int primitiveInt = 10;
        Integer boxedInt = primitiveInt; // 编译器会自动调用 Integer.valueOf(int)

        // 自动拆箱
        int unboxedInt = boxedInt; // 编译器会自动调用 boxedInt.intValue()

        // 在算术运算中自动拆箱
        Integer anotherBoxedInt = 20;
        int result = boxedInt + anotherBoxedInt; // 实际上会拆箱为 int,然后执行加法运算,再装箱为 Integer(如果赋值给 Integer 变量)
        // 但由于 result 是 int 类型,所以这里只涉及拆箱,不涉及装箱

        // 显式装箱和拆箱(通常不需要这样做,因为自动装箱和拆箱已经足够)
        Integer explicitlyBoxedInt = Integer.valueOf(primitiveInt); // 显式装箱
        int explicitlyUnboxedInt = explicitlyBoxedInt.intValue(); // 显式拆箱

        // 打印结果以验证
        System.out.println("Primitive int: " + primitiveInt);
        System.out.println("Boxed int: " + boxedInt);
        System.out.println("Unboxed int: " + unboxedInt);
        System.out.println("Result of boxed int addition: " + result);
        System.out.println("Explicitly boxed int: " + explicitlyBoxedInt);
        System.out.println("Explicitly unboxed int: " + explicitlyUnboxedInt);
    }
}

​​​​​​​

  1. 性能考虑:虽然自动装箱和拆箱为程序员提供了便利,但在性能敏感的应用中,过多的装箱和拆箱操作可能会导致性能下降。因为装箱操作需要创建新的对象,而拆箱操作则涉及方法调用。
  2. 空指针异常:由于包装类对象是引用类型,因此它们可以是null。在进行拆箱操作时,如果包装类对象为null,则会抛出NullPointerException。例如,Integer nullInt = null; int value = nullInt; 这段代码会抛出空指针异常。
  3. 缓存机制:Java对某些包装类(如IntegerBooleanByteShortCharacterLong,但不适用于FloatDouble)的值进行了缓存。对于Integer来说,缓存范围是-128到127。在这个范围内的值被装箱时,会返回缓存中的对象,而不是创建新的对象。这有助于提高性能并减少内存使用。
  4. 比较操作:当比较两个包装类对象时,应该使用equals()方法而不是==运算符,因为==比较的是对象的引用而不是值。但是,对于缓存范围内的Integer值,使用==可能会得到正确的结果(因为它们是同一个对象的引用),但这是一种不可靠的做法,因为它依赖于Java的内部实现。
  5. 包装类的常用方法
    • 转换方法(如valueOf, intValue等)
    • 比较方法(如compareTo, equals等)
    • 静态方法(如parseInt, parseDouble等)

二、Java泛型基础:

  1. 泛型的引入与意义​​​​​​​​​​​​​​
    • 在Java 5之前,集合类(如ArrayList、HashMap等)只能存储Object类型的对象。这意 味着在存储和取出元素时,需要进行强制类型转换,这不仅繁琐,而且容易出现类型错误,如ClassCastException。为了解决这个问题,Java引入了泛型机制,允许在定义类、接口和方法时,使用类型参数来指代具体的类型,从而实现代码的通用性和类型安全性。
    • 类型安全
      • 泛型可以在编译时期检查数据类型的合法性,避免出现类型不匹配导致的运行时错误。
      • 编译器可以在编译期验证数据结构中的类型使用是否正确,降低运行时错误的概率。
    • 代码复用
      • 泛型使得代码能够操作多种数据类型,而无需为每种类型单独实现一个版本。
      • 一个泛型数据结构可适配多种类型,提高了代码的复用性。
    • 代码清晰与简洁
      • 使用泛型可以使代码更加清晰、易懂,降低了代码阅读的难度。
      • 避免了不必要的类型转换,减少了代码的冗余,提高了代码的可读性。
    • 性能提升
      • 由于泛型避免了不必要的类型转换,所以在一定程度上可以提高程序的性能。
    • 增强程序的健壮性
      • 通过在编译时期进行类型检查,泛型可以帮助开发者更早地发现并修复类型相关的错误,从而增强程序的健壮性。
    • 泛型类
      • 在定义类时使用泛型参数,可以将具体的数据类型作为参数传递给类,并在类内部使用这些数据类型。
    • 泛型方法
      • 在方法的返回值前使用泛型参数,可以将具体的数据类型作为参数传递给方法,并在方法内部使用这些数据类型。
    • 泛型接口
      • 在定义接口时使用泛型参数,可以将具体的数据类型作为参数传递给接口,并在实现接口的类中使用这些数据类型。
    • Java泛型的引入极大地增强了代码的类型安全性和可读性,提高了代码的复用性和维护性。无论是标准库中的集合类,还是自定义的数据结构,都可以通过泛型实现更灵活、更高效的代码设计。在数据结构中,泛型为开发者提供了统一性和扩展性,同时也为程序的安全性和健壮性保驾护航。

泛型类是指在定义类时使用类型参数(也称为类型占位符)的类。类型参数在类名后面的尖括号<>中指定。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 定义一个泛型类 Box
public class Box<T> {
    // T 是类型参数,表示任意类型
    private T content;

    public void setContent(T content) {
        this.content = content;
    }

    public T getContent() {
        return content;
    }
}

使用泛型类时,可以指定具体的类型参数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Box<Integer> integerBox = new Box<>();
integerBox.setContent(100);
Integer content = integerBox.getContent();

泛型接口与泛型类的定义类似,只是在接口名后使用尖括号指定类型参数。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 定义一个泛型接口 Pair
public interface Pair<K, V> {
    K getKey();
    V getValue();
}

实现泛型接口时,需要指定具体的类型参数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class OrderedPair<K, V> implements Pair<K, V> {
    private K key;
    private V value;

    public OrderedPair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    @Override
    public K getKey() {
        return key;
    }

    @Override
    public V getValue() {
        return value;
    }
}

泛型方法是指在方法定义时使用类型参数的方法。类型参数在方法返回类型前和方法名后的尖括号中指定。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 定义一个泛型方法 printArray
public static <T> void printArray(T[] array) {
    for (T element : array) {
        System.out.print(element + " ");
    }
    System.out.println();
}

使用泛型方法时,无需指定类型参数,因为编译器会根据方法调用时的实际参数类型进行推断:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Integer[] intArray = {1, 2, 3, 4, 5};
printArray(intArray); // 输出: 1 2 3 4 5

Java集合框架中的许多类都是泛型的,如ArrayListHashMap等。使用泛型集合可以避免类型转换和潜在的ClassCastException

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ArrayList<String> stringList = new ArrayList<>();
stringList.add("Hello");
stringList.add("World");

for (String s : stringList) {
    System.out.println(s);
}

泛型通配符?用于表示未知的类型。它主要有两种形式:无界通配符(?)、上界通配符(? extends SomeType)和下界通配符(? super SomeType)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 使用无界通配符
List<?> unknownList = new ArrayList<String>();

// 使用上界通配符
List<? extends Number> numberList = new ArrayList<Integer>();
// numberList 可以是 Integer、Double、Float 等 Number 子类的列表,但不能添加除 null 以外的元素

// 使用下界通配符
List<? super Integer> integerSuperList = new ArrayList<Number>();
// integerSuperList 可以是 Number 或 Number 的父类(如 Object)的列表,可以添加 Integer 或 Integer 的子类对象
三、Java包装类与泛型的结合

包装类与泛型的结合:

当包装类与泛型结合使用时,可以创建更加灵活和类型安全的集合和数据结构。例如,ArrayList<Integer>是一个使用Integer包装类的泛型集合,它可以存储整数值,并且提供了类型安全的保证。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.util.ArrayList;
import java.util.List;

public class WrapperGenericsExample {
    public static void main(String[] args) {
        // 创建一个存储Integer对象的ArrayList
        List<Integer> integerList = new ArrayList<>();

        // 向列表中添加整数(自动装箱)
        integerList.add(10);
        integerList.add(20);
        integerList.add(30);

        // 遍历列表并打印每个元素(自动拆箱)
        for (Integer integer : integerList) {
            System.out.println(integer);
        }

        // 使用包装类的方法
        int sum = 0;
        for (Integer integer : integerList) {
            sum += integer.intValue(); // 使用intValue()方法将Integer转换为int
        }
        System.out.println("Sum: " + sum);
    }
}

ArrayList<Integer>是一个泛型集合,它使用Integer包装类来存储整数。由于使用了泛型,我们得到了类型安全的保证:我们不能向integerList中添加除Integer对象以外的任何对象(除了null,但在Java 5及更高版本中,泛型集合通常不允许存储null以避免潜在的NullPointerException)。

四、Java泛型进阶:

泛型擦除示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

// 定义一个简单的泛型类
public class Box<T> {
    private T content;

    public void setContent(T content) {
        this.content = content;
    }

    public T getContent() {
        return content;
    }

    // 一个用于反射获取泛型类型信息的方法(注意:这个方法在泛型擦除后无法直接获取到T的具体类型)
    public Type getGenericType() {
        return ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public static void main(String[] args) {
        Box<String> stringBox = new Box<>();
        stringBox.setContent("Hello, World!");
        System.out.println(stringBox.getContent());

        // 通过反射尝试获取泛型类型信息(实际上在运行时已经擦除)
        Type genericType = stringBox.getGenericType();
        System.out.println("Generic type (at runtime, may be erased): " + genericType);

        // 但我们可以通过子类化并立即获取泛型信息来“绕过”擦除(这种方法不适用于所有情况)
        class StringBox extends Box<String> {}
        Type stringBoxType = StringBox.class.getGenericSuperclass().getActualTypeArguments()[0];
        System.out.println("Generic type of StringBox (inferred at compile time): " + stringBoxType);

        // 注意到,直接使用Box<String>的实例在运行时无法获取到T的具体类型,因为泛型信息已被擦除
    }
}

Java的类型推断机制允许编译器根据上下文自动推断出变量的类型,从而简化了代码的编写。以下是一个使用类型推断的示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.util.ArrayList;
import java.util.List;

public class TypeInferenceExample {
    public static void main(String[] args) {
        // 在Java 7及更高版本中,可以在右侧的构造函数调用中省略泛型类型参数
        List<String> list1 = new ArrayList<>(); // 类型推断为List<String>

        // 在Java 10及更高版本中,可以使用var关键字进一步简化变量声明
        var list2 = new ArrayList<String>(); // 类型推断为ArrayList<String>(但var的使用应谨慎,以避免降低代码可读性)

        // 方法调用中的类型推断
        printList(list1); // 编译器可以推断出list1的类型为List<String>

        // 泛型方法调用中的类型推断
        List<Integer> numbers = createListWithNumbers(1, 2, 3, 4, 5); // 编译器可以推断出返回类型为List<Integer>
    }

    // 一个简单的泛型方法,用于创建并返回一个包含指定元素的列表
    public static <T> List<T> createListWithElements(T... elements) {
        List<T> list = new ArrayList<>();
        for (T element : elements) {
            list.add(element);
        }
        return list;
    }

    // 一个用于打印列表内容的方法(注意:这里不是泛型方法,只是接受泛型类型的参数)
    public static void printList(List<?> list) {
        for (Object element : list) {
            System.out.println(element);
        }
    }

    // 一个具体的泛型方法调用示例,用于创建并返回一个包含数字的列表
    public static List<Integer> createListWithNumbers(Integer... numbers) {
        return createListWithElements(numbers); // 这里再次调用了泛型方法createListWithElements,并进行了类型推断
    }
}
五、Java包装类与泛型实战

在实际开发中,包装类与泛型经常一起使用。例如,当需要将基本数据类型存储在集合中时,由于集合的泛型参数必须是对象类型,因此需要使用包装类。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.util.ArrayList;
import java.util.List;

public class WrapperAndGenericsExample {
    public static void main(String[] args) {
        // 创建一个存储Integer对象的列表
        List<Integer> integerList = new ArrayList<>();
        integerList.add(10);
        integerList.add(20);
        integerList.add(30);

        // 遍历列表并打印元素
        for (Integer integer : integerList) {
            System.out.println(integer);
        }

        // 创建一个存储Double对象的列表
        List<Double> doubleList = new ArrayList<>();
        doubleList.add(1.1);
        doubleList.add(2.2);
        doubleList.add(3.3);

        // 使用泛型方法来打印Double列表
        printList(doubleList);
    }

    // 泛型方法,用于打印列表中的元素
    public static <T> void printList(List<T> list) {
        for (T element : list) {
            System.out.println(element);
        }
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-11-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java基本数据类型、包装类及拆装箱详解
Java的基本数据类型和对应的包装类是Java语言中处理数据的两个关键概念。基本数据类型提供了简单而高效的方式来存储数据,而包装类使得基本数据类型具有对象的特性。本文将深入探讨基本数据类型与包装类的应用场景及详细描述,并对自动拆箱和装箱的源码实现进行分析。
修己xj
2023/12/05
6500
Java基本数据类型、包装类及拆装箱详解
Java学习【深入探索包装类和泛型】
在Java的学习中,包装类和泛型是两个重要的概念,它们不仅丰富了Java的数据类型,还提高了代码的可读性和安全性。下面,我们将深入探讨这两个主题。
2的n次方
2024/10/15
870
Java学习【深入探索包装类和泛型】
【Java】什么是泛型?什么是包装类
Java是一个面向对象的编程语言,但其基本数据类型(如int、char、boolean等)并不直接支持面向对象的特性。为了弥补这一不足,Java为每种基本数据类型设计了一个对应的类,这些类统称为包装类(Wrapper Class)。包装类均位于java.lang包中。
椰椰椰耶
2024/10/15
1380
【Java】什么是泛型?什么是包装类
包装类和泛型
在Java中,由于基本;类型不是继承自Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了一个包装类型。
用户11070251
2024/04/11
1320
【数据结构】包装类和泛型
除了int的包装类是Integer,char的包装类是Character,其他的基本类型对应的包装类都是将首字母大写。
用户11162265
2024/08/05
980
【数据结构】包装类和泛型
《JavaSE》---21.<简单认识Java的集合框架&包装类&泛型>
其主要表现为将多个元素 element 置于一个单元中,用于对这些元素进行快速、便捷的存储 store 、检索 retrieve 、 管理 manipulate ,即平时我们俗称的增删查改 CRUD 。
用户11288958
2024/09/24
1750
《JavaSE》---21.<简单认识Java的集合框架&包装类&泛型>
数据结构-1.初始包装类与泛型
在 Java 中,由于基本类型不是继承自 Object ,为了在泛型代码中可以支持基本类型, Java 给每个基本类型都对应了一个包装类型.
用户11369350
2024/11/19
600
知识改变命运——【数据结构】包装类&简单认识泛型
在Java中,由于基本类型不是继承自Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了 一个包装类型。
用户11319080
2024/10/17
720
知识改变命运——【数据结构】包装类&简单认识泛型
Java 泛型深入解析:类型安全与灵活性的平衡
Java 泛型(Generics)是一个强大的语言特性,它允许在类、接口和方法中使用参数化类型,从而实现代码的重用、增强类型安全性,并提升代码的可读性。泛型的引入解决了 Java 编程中常见的类型转换问题,使得我们能够编写更加灵活且健壮的代码。然而,泛型背后的类型擦除(Type Erasure)机制和一些高级特性也给我们带来了一定的挑战。
科技新语
2024/10/11
1790
Java 泛型深入解析:类型安全与灵活性的平衡
【数据结构】包装类&泛型
小编在这里设置了一个上界comparable,可以进行comparaTo方法的调用,当然改为整型也是可以的。 
用户11288949
2024/09/24
970
【数据结构】包装类&泛型
Java中的泛型(很细)
非常好,让我们深入探讨Java中的泛型这个重要主题。我将按照之前提供的框架,为您创作一篇全面而专业的技术博客文章。
程序员朱永胜
2024/07/18
2250
Java中的泛型(很细)
Java 中文官方教程 2022 版(六)
大多数情况下,如果您使用单个字符值,您将使用基本的char类型。然而,有时您需要将 char 用作对象—例如,作为期望对象的方法参数。Java 编程语言为此提供了一个包装类,将char包装在Character对象中。Character类型的对象包含一个类型为char的单个字段。这个Character类还提供了许多有用的类(即静态)方法来操作字符。
ApacheCN_飞龙
2024/05/24
2580
Java 中文官方教程 2022 版(六)
包装类与泛型,到底区别在哪?
在 Java 中,基本数据类型是不具有对象特性的,不支持面向对象的操作。但是,在某些情况下,我们需要将基本数据类型作为对象来操作,这时就需要使用包装类。
网络技术联盟站
2023/06/04
1910
Java 泛型:理解和应用
这就是泛型的概念,是 Java 后期的重大变化之一。泛型实现了参数化类型,可以适用于多种类型。泛型为 Java 的动态类型机制提供很好的补充,但是 Java 的泛型本质上是一种高级语法糖,也存在类型擦除导致的信息丢失等多种缺点,我们可以在本篇文章中深度探讨和分析。
phoenix.xiao
2023/08/28
2800
Java 泛型:理解和应用
Java泛型深入理解「建议收藏」
在面向对象编程语言中,多态算是一种泛化机制。例如,你可以将方法的参数类型设置为基类,那么该方法就可以接受从这个基类中导出的任何类作为参数,这样的方法将会更具有通用性。此外,如果将方法参数声明为接口,将会更加灵活。
全栈程序员站长
2022/09/10
8800
Java基础(十四):包装类
冬天vs不冷
2025/01/21
950
Java基础(十四):包装类
java基础之泛型
泛型 术语 "?"通配符 通配符的扩展 自定义泛型方法 "擦除"实例 类型参数的类型推断 自定义泛型类 泛型方法和泛型类的比较 泛型和反射 通过反射获得泛型的实际类型参数 本文对泛型的基本
xiangzhihong
2018/02/01
1.1K0
java基础之泛型
如何使用 Java 泛型来避免 ClassCastException
泛型是相关语言特性的集合,它允许类或方法对各种类型的对象进行操作,同时提供编译时类型安全性检查
Java宝典
2021/01/28
2.2K0
Java 比较器 和 包装类
Java Comparable接口强行对实现它的每个类的对象进行整体排序 这种排序被称为:自然排序
Java_慈祥
2024/08/06
1710
Java 比较器 和 包装类
Java包装类,基本的装箱与拆箱
将原始类型和包装类分开以保持简单。当需要一个适合像面向对象编程的类型时就需要包装类。当希望数据类型变得简单时就使用原始类型。
JanYork_简昀
2022/03/31
5260
Java包装类,基本的装箱与拆箱
相关推荐
Java基本数据类型、包装类及拆装箱详解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验