数据结构系列二:包装类+泛型

news/2025/2/22 6:13:01

包装类+泛型

  • 一、包装类
    • (1)基本数据类型和对应的包装类
    • (2)装箱和拆箱
  • 二、泛型
    • (1)什么是泛型
    • (2)引出泛型
    • (3)语法
    • (4)泛型类的使用
      • 1.语法
      • 2.示例
      • 3.类型推导
    • (5)裸类型
    • (6)泛型是如何编译的
      • 1.擦除机制
      • 2.为什么不能实例化泛型类数组?
    • (7)泛型的上界
      • 1.语法
      • 2.示例
      • 3.复杂示例
    • (8)泛型方法
      • 1.语法
      • 2.示例


一、包装类

在Java中,由于基本类型不是继承自Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了一个包装类型

(1)基本数据类型和对应的包装类

基本数据类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean
  • 除了Integercharacter,其余基本类型的包装类都是首字母大写

(2)装箱和拆箱

  • 装箱:把一个基本数据类型转化为包装类型的过程
  • 拆箱:将一个包装类中的值取出,放到一个基本类型中

自动装箱与自动拆箱

java">public class demo {
    public static void main(String[] args) {
        //自动装箱
        int i = 10;
        Integer a = i;
        
        //自动拆箱
        int b = a;
    }
}

显示装箱和显示拆箱

java">        //显示装箱
        int c = 20;
        Integer d = Integer.valueOf(c);
        
        //显示拆箱:拆箱为自己指定的元素
        int e = d.intValue();
        double e1 = d.doubleValue();

二、泛型

(1)什么是泛型

一般的类和方法,只能使用具体的类型,要么是基本类型,要么是自定义的类,如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。—— 《Java编程思想》
泛型是在jdk1.5引入的新语法,通俗讲,泛型:就是适用于许多许多类型,从代码上将,就是对类型实现了参数化

(2)引出泛型

实现一个类,类中包含一个数组成员,使得数组中可以存放任何类型的数据,也可以根据成员方法返回数组中某个下标的值?
思路:

  1. 我们以前学过的数组,只能存放指定类型的数据,例如String[]int[]
  2. 所有类的父类,默认为Object类,数组是否可以创建为Object
java">class MyArray{
    Object[] array = new Object[10];

    public void set(int pos,Object val){
        this.array[pos] = val;
    }
    public Object get(int pos){
        return this.array[pos];
    }
}
public class demo1 {
    public static void main(String[] args) {
        MyArray myArray = new MyArray();
        myArray.set(0,10000);
        myArray.set(1,"hello");
        String ret = myArray.get(1);//编译报错
        String ret = (String) myArray.get(1);
    }
}

以上代码实现后发现

  1. 任何类型数据都可以存放
  2. 1号下标本身就是字符串,但是却编译报错,必须进行强制类型转换才可以

虽然在这种情况下,当前数组任何数据都可以存放,但是,更多情况下,我们还是希望他只能够持有一种数据类型,而不是同时持有这么多类型,所以,泛型的主要目的:指定当前的容器,要持有什么类型的对象,让编译器去做检查,此时,就需要把类型作为参数传递,需要什么类型,就传入什么类型

(3)语法

java">class 泛型类名称<类型形参列表>{
    //这里可以使用类型参数
}
class ClassName<T1,T2……Tn>{
    
}
java">class 泛型类名称<类型形参列表> extends 继承类{
    //这里可以使用类型参数
}
class ClassName<T1,T2……Tn> extends ParentClass<T1>{
   //可以只使用部分类型参数
}

上述代码进行改写如下

java">class Myarray<T>{
    Object[] array = new Object[10];

    public void set(int pos,T val){
        this.array[pos] = val;
    }
    public T get(int pos) {
        return (T)this.array[pos];
    }
}
public class demo2 {
    public static void main(String[] args) {
        Myarray<String> myArray = new Myarray<>();
        myArray.set(0,"sadfklsajdfs");
        myArray.set(1,"qwe");
        String ret = myArray.get(0);
        String ret1 = myArray.get(1);
        System.out.println(ret);
        System.out.println(ret1);

        Myarray<Integer> myarray = new Myarray<>();
        myarray.set(0,3);
        Integer ret2 = myarray.get(0);
        System.out.println(ret2);
    }
}
//输出结果
sadfklsajdfs
qwe
3

Process finished with exit code 0

代码解释:

  1. 类名后的代表占位符,表示当前类是一个泛型类

类型形参一般使用一个大写字母来表示,常用的名称有

  • E表示Element
  • K表示Key
  • V表示Value
  • N表示Number
  • T表示Type
  1. 不能new泛型类型的数组
java">T[] ts = new T[5];//是不对的
  1. 类型后需要加入包装类指定当前类型
java">Myarray<String> myArray = new Myarray<>();
  1. 存放元素的时候会帮我们检查,如果和包装类不同,会报错提示

(4)泛型类的使用

1.语法

java">泛型类<类型实参> 变量名 = new 泛型类<类型实参>(构造方法实参);

2.示例

尖括号中只能是引用类型,不能是基本类型

java">Myarray<String> myArray = new Myarray<String>();

注意:泛型只能接受类,所有的基本数据类型必须使用包装类

3.类型推导

当编译器可以根据上下文推导出类型实参时,可以省略类型实参的填写,但是尖括号不能省略

java">Myarray<String> myArray = new Myarray<>();

(5)裸类型

裸类型是一个泛型类但是没有带着类型实参

java">Myarray list = new Myarray();

注意:我们不要自己去使用裸类型,裸类型是为了兼容老版本的API保留的机制

(6)泛型是如何编译的

1.擦除机制

那么,泛型到底是怎么编译的呢?这个问题也是曾经的一个面试问题,泛型本质是一个非常难的语法,要理解好它还是需要一定的时间打磨
我们先察看字节码文件,发现所有的T都是Object

  • 在编译的过程中,将所有的T替换为Object这种机制,我们称为擦除机制
  • 运行的时候没有泛型这样的概念,泛型的擦除机制,只存在于编译期间

2.为什么不能实例化泛型类数组?

原因:替换后的方法为:将Object[]分给Integer[]引用,程序报错
不能证明所有类型都是Integer类型
通俗讲就是:返回的Object数组里面,可能存放的是任何的数据类型,可能是String,可能是Integer,运行的时候,直接传给Integer类型的数组,编译器认为是不安全的

(7)泛型的上界

在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束

1.语法

java">class 泛型类名称<类型实参 extends 类型边界>{

}

2.示例

java">class test<E extends Number>{
    
}

extends表示拓展,只接受Number的子类型作为E的类型实参

java">class test<E extends Number>{

}
public class demo4 {
    public static void main(String[] args) {
        test<Integer> a1;//正常,因为Integer是Number的子类型
        test<String> a2;//编译错误,因为String不是Number的子类型
    }
}

没有指定类型边界E,可以视为E extends Object

3.复杂示例

写一个泛型类,实现一个方法,这个方法是就指定类型泛型的最大值

java">class Alg<T extends Comparable<T>>{
    public T findMax(T[] array){
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if(array[i].compareTo(max) > 0){
                max = array[i];
            }
        }
        return max;
    }
}
public class demo5 {
    public static void main(String[] args) {
        Alg<Integer> alg1 = new Alg<>();
        Integer[] array = {1,2,3,4,5};
        Integer ret = alg1.findMax(array);
        System.out.println(ret);

    }
}
//输出结果
5

Process finished with exit code 0

(8)泛型方法

1.语法

java">方法限定符<类型形参列表> 返回值类型 方法名称(形参列表){
    
}

2.示例

java">class Util{
    public <T extends Comparable<T>> T findMax(T[] array){
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if(array[i].compareTo(max) > 0){
                max = array[i];
            }
        }
        return max;
    }
}
public class demo6 {
    public static void main(String[] args) {
        Util util = new Util();
        Integer[] array = {1,2,3,4,5};
        Integer ret = util.findMax(array);
        System.out.println(ret);
    }
}
//输出结果
5

Process finished with exit code 0

静态方法可以直接通过类名调用

java">class Util{
    public static <T extends Comparable<T>> T findMax(T[] array){
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if(array[i].compareTo(max) > 0){
                max = array[i];
            }
        }
        return max;
    }
}
public class demo6 {
    public static void main(String[] args) {
        Integer[] array = {1,2,3,4,5};
        Integer ret = Util.findMax(array);
        System.out.println(ret);
    }
}
//输出结果
5

Process finished with exit code 0


http://www.niftyadmin.cn/n/5861754.html

相关文章

SpringCloud-OpenFeign初步使用

在使用RestTemplate时,会发现拼接URL时非常不美观,并且在参数多了以后拼接起来也容易出错,而Feign就是为了解决这个问题,让远程调用像调用本地方法一样优雅且方便. 功能: 更优雅的进行远程调用 使用方法: 引入依赖,由于OpenFeign是基于负载均衡的所以要引入三个依赖,openFei…

【信息系统项目管理师-案例真题】2022下半年案例分析答案和详解

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 试题一(24分)【问题1】(6分)【问题2】(10分)【问题3】(8分)试题二(26分)【问题1】(8分)【问题2】(8分)【问题3】(4分)【问题4】(6分)试题三(25分)【问题1】(12分)【问题2】(7分)【问题…

高级SQL技术在Python项目中的应用:更进一步的数据分析与集成

引言 在第一篇中,我们深入探讨了ORM框架SQLAlchemy的高级用法以及性能优化策略。然而,要充分释放数据库的潜力,我们还需要掌握更多高级SQL特性,并将其与强大的数据分析工具生态系统有效集成。本篇将聚焦于窗口函数、CTE递归查询、JSON操作、全文搜索以及与Pandas的无缝集成…

【探商宝】2025年2月科技与商业热点头条:AI竞赛、量子计算与芯片市场新格局

一、 AI大模型竞争白热化&#xff1a;开源与闭源的博弈 OpenAI推进"星际之门"项目 为巩固美国在AI领域的领先地位&#xff0c;OpenAI正在全美评估数据中心选址&#xff0c;得州阿比林数据中心已开建。该项目被视为算力基建的关键布局&#xff0c;尽管面临DeepSeek低成…

Linux-Ansible命令

文章目录 常用命令基础命令 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Linux专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2025年02月21日18点49分 常用命令 ansible #主命令&#xff0c;管理员临时命令的执行工具 ansible-doc #…

【后端基础】布隆过滤器原理

文章目录 一、Bloom Filter&#xff08;布隆过滤器&#xff09;概述1. Bloom Filter 的特点2. Bloom Filter 的工作原理 二、示例1. 添加与查询2. 假阳性 三、Bloom Filter 的操作1、假阳性概率2、空间效率3、哈希函数的选择 四、应用 Bloom Filter 是一种非常高效的概率型数据…

不同安装路径重复R包清理

df <- as.data.frame(installed.packages()) table(duplicated(df$Package)) ids <- df$Package[duplicated(df$Package)] df2 <- subset(df, df$Package %in% ids)

nginx ngx_stream_module(3) 指令详解

nginx ngx_stream_module(3) 指令详解 相关链接 nginx 嵌入式变量解析目录nginx 嵌入式变量全目录nginx 指令模块目录nginx 指令全目录 一、目录 1.1 模块简介 ngx_stream_upstream_module&#xff1a;上游服务器模块&#xff0c;允许定义一组后端服务器&#xff0c;并控制如…