DataStructure.包装类简单认识泛型

包装类&简单认识泛型

  • 【本节目标】
  • 1 包装类
    • 1.1 基本数据类型和对应的包装类
    • 1.2 装箱和拆箱
    • 1.3 自动装箱和自动拆箱
  • 2 什么是泛型
  • 3 引出泛型
    • 3.1 语法
  • 4 泛型类的使用
    • 4.1 语法
    • 4.2 示例
    • 4.3 类型推导(Type Inference)
  • 5. 裸类型(Raw Type) (了解)
    • 5.1 说明
  • 6 泛型如何编译的
    • 6.1 擦除机制
    • 6.2 为什么不能实例化泛型类型数组
  • 7 泛型的上界
    • 7.1语法
    • 7.2 示例
    • 7.3 复杂示例
      • Alg 类
      • TestGeneric 类
      • 逻辑关系和总结
  • 8 泛型方法
    • 8.1 定义语法
    • 8.2 示例
    • 8.3 使用示例
      • Alg2 类
      • TestGeneric 类
      • 逻辑关系和总结

【本节目标】

  1. 以能阅读 java 集合源码为目标学习泛型
  2. 了解包装类
  3. 了解泛型

1 包装类

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

1.1 基本数据类型和对应的包装类

基本数据类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charcharacter
booleanBoolean

除了 IntegerCharacter, 其余基本类型的包装类都是首字母大写。

1.2 装箱和拆箱

public class Test {
    public static void main(String[] args) {
        int i = 10;

        //装箱操作:新建一个Integer类型对象,将i的值放进对象的某个属性中
        Integer ii = Integer.valueOf(i);
        Integer ij = new Integer(i);

        //拆箱操作:将Integer对象中的值取出,放到一个基本数据类型中
        int j = ii.intValue();
    }
}

1.3 自动装箱和自动拆箱

可以看到在使用过程中,装箱和拆箱带来不少的代码量,所以为了减少开发者的负担,java 提供了自动机制。

public class Test {
//可以看到在使用过程中,装箱和拆箱带来不少的代码量,所以为了减少开发者的负担,java 提供了自动机制。
    public static void main2(String[] args) {
        int a = 10;

        Integer b = a; // 自动装箱
        Integer c = (Integer) a; // 自动装箱

        int j = b; // 自动拆箱
        int k = (Integer) b; // 自动拆箱 
    }
}

在这里插入图片描述
【面试题】

下列代码输出什么,为什么?

public class Test {
    //下列代码输出是什么,为什么?
    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
    }
}

2 什么是泛型

一般的类和方法,只能使用具体的类型: 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。

泛型是在JDK1.5引入的新的语法,通俗讲,泛型:就是适用于许多许多类型。 从代码上讲,就是对类型实现了参数
化。

3 引出泛型

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

思路:

  1. 以前学过的数组,只能存放指定类型的元素,例如:int[] array = new int[10]; String[] array = new String[10];
  2. 所有类的父类,默认为Object类,数组是否创建为Object

代码示例:

class MyArray {
   public Object[] objects = new Object[10];
   //输入
   public Object getObjects(int pos) {
       return objects[pos];
   }
   //输出
   public void setObjects(int pos, Object obj) {
       this.objects[pos] = obj;
   }
}

public class Test {
    public static void main(String[] args) {
        MyArray myArray = new MyArray();
        myArray.setObjects(0, 100);
        myArray.setObjects(1, "hello");//字符串也可以存放

        String ret = (String)myArray.getObjects(1);
        //String ret = myArray.getObjects(1);//编译报错
        System.out.println(ret);

    }
}

问题:以上代码实现后 发现

  1. 任何类型数据都可以存放
  2. 下标1本身是字符串,但是编译报错,必须进行强制类型转换
  • 虽然在这种情况下,当前数组任何数据都可以存放,但是,更多情况下,我们还是希望他只能够持有一种数据类型。而不是同时持有这么多类型。
  • 所以,泛型的主要目的:就是指定当前的容器,要持有什么样的类型。让编译器去做检验。 此时,就需要把类型,作为参数传递,需要什么的类型,就传入什么类型。

3.1 语法

class 泛型类名称<类型形参列表> {
    //这里可以使用类型参数
}

class ClassName<T1, T2, T3...> {
}

class 泛型类型名称<类型参数列表> extends 继承类/*这里可以使用类型参数*/{
    //这里可以使用类型参数
}

class ClassName<T1, T2, T3...>extends ParentClass<T1>{
    //可以使用部分类型参数
}

上述代码进行改写如下:

class MyArray1 <T> {
    public T[] objects = (T[])new Object[10];//1
    public T getObjects(int pos) {
        return objects[pos];
    }
    public void setObjects(int pos, T obj) {
        this.objects[pos] = obj;
    }
}

public class Test1 {
    public static void main(String[] args) {
        MyArray1<Integer> myArray11 = new MyArray1<>();//2
        myArray11.setObjects(1, 100);
        int ret1 = myArray11.getObjects(1);//3
        System.out.println(ret1);

        System.out.println(myArray11.getObjects(1));

        //myArray11.setObjects(2,"bit");//4
    }
}

代码解释:

  1. 类名后的代表占位符,表示当前类是一个泛型类
    了解: 【规范】类型形参一般使用一个大写字母表示,常用的名称有:
  • E表示Element
  • K表示Key
  • V表示Value
  • N表示Number
  • T表示Type
  • S, U, V 等等 - 第二、第三、第四个类型
  1. 注释1处,不能new泛型类型的数组
    意味着:
T[] ts = new T[];//error

课件当中的代码:T[] array = (T[])new Object[10];是否就足够好,答案是未必的。这块问题一会儿介绍。

  1. 注释2处,类型后加入<Integer>指定当前类型
  2. 注释3处,不需要进行强制类型转换
  3. 注释4处,代码编译报错,此时因为在注释2处指定类当前的类型,此时在注释4处,编译器会在存放元素的时候帮助我们进行校验检查。

4 泛型类的使用

4.1 语法

泛型类<类型实参> 变量名; //定义一个泛型类
new 泛型类<类型实参>(构造方法实参); //实例一个泛型类对象

4.2 示例

MyArray<Integer> list = new MyArray<Integer>();

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

4.3 类型推导(Type Inference)

当编译器可以根据上下文导出类型实参时,可以省略类型实参的填写

MyArray<Integer> list = new MyArray<>(); //可以推导出实例化需要的类型实参为 Integer

5. 裸类型(Raw Type) (了解)

5.1 说明

裸类型是一个泛型类但没有带着类型实参,例如 MyArrayList 就是一个裸类型

MyArray List = new MyArray();

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

下面的类型擦除部分,我们也会讲到编译器是如何使用裸类型的。

小结:

  1. 泛型是将数据类型参数化,进行传递
  2. 使用<T>表示当前类是一个泛型类。
  3. 泛型到目前为止的优点:数据类型参数化,编译时自动进行类型检查和转换

6 泛型如何编译的

6.1 擦除机制

那么,泛型到底是怎么编译的?这个问题,也是曾经的一个面试问题。泛型本质是一个非常难的语法,要理解好他还是需要一定的时间打磨。

通过命令:javap -c 查看字节码文件,所有的T都是Object

在这里插入图片描述
在编译的过程当中,将所有的T替换为Object这种机制,我们称为:擦除机制

Java的泛型机制是在编译级别实现的。编译器生成的字节码在运行期间并不包含泛型的类型信息。

提出问题:

1、那为什么,T[] ts = new T[5]; 是不对的,编译的时候,替换为Object,不是相当于:Object[] ts = new Object[5]吗?
2、类型擦除,一定是把T变成Object吗?

6.2 为什么不能实例化泛型类型数组

代码1:

class MyArray2 <T> {
    public T[] array = (T[]) new Object[10];

    public T getPos(int pos) {
        return array[pos];
    }

    public void setVal(int pos, T val) {
        this.array[pos] = val;
    }
    public T[] getArray() {
        return array;
    }
}

public class Test {
    public static void main(String[] args) {
        MyArray2<Integer> myArray2 = new MyArray2<>();
        Integer[] string = myArray2.getArray();
    }
}

/*
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
 */

原因:替换后的方法为:将Object[]分配给Integer[]引用,程序报错.

public Object[] getArray() {
	return array;
}

通俗讲就是:返回的Object数组里面,可能存放的是任何的数据类型,可能是String,可能是Person,运行的时候,直接转给Integer类型的数组,编译器认为是不安全的。

正确的方式:【了解即可】

class MyArray<T> {
    public T[] array;

    public MyArray() {
    }

    /**
     * 通过反射创建,指定类型的数组
     *
     * @param clazz
     * @param capacity
     */
    public MyArray(Class<T> clazz, int capacity) {
        array = (T[]) Array.newInstance(clazz, capacity);
    }

    public T getPos(int pos) {
        return this.array[pos];
    }

    public void setVal(int pos, T val) {
        this.array[pos] = val;
    }

    public T[] getArray() {
        return array;
    }
}

public class Test1 {
    public static void main(String[] args) {
        MyArray<Integer> myArray1 = new MyArray<>(Integer.class, 10);
        Integer[] integers = myArray1.getArray();
    }
}

7 泛型的上界

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

7.1语法

class泛型类名称<类型形参 extends 类型边界> {
    ...
}

7.2 示例

public class MyArray<E extends Number> {
	...
}

只接受Number的子类型作为E的类型实参

MyArray<Integer> l1; //正常,因为Integer是Number的子类型
MyArray<String> l2; //编译报错,因为String不是Number的子类型

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

7.3 复杂示例

写一个泛型类,当中的方法,可以求数组中的最大值

//写一个泛型类,当中的方法,可以求数组中的最大值
class Alg<E extends Comparable<E>> {
    public E findMax(E[] array) {
        E max = array[0];
        for (int i = 0; i < array.length; i++) {
            if(max.compareTo(array[i]) < 0) {
                max = array[i];
            }
        }
        return max;
    }
}

public class TestGeneric {
    public static void main(String[] args) {
        Integer[] array = {1, 2, 3, 4, 5, };
        Alg<Integer> alg = new Alg<>();
        Integer ret = alg.findMax(array);
        System.out.println(ret);
    }
}

代码包含两个类:AlgTestGeneric。其中,Alg 是一个泛型类,用于找到数组中的最大值,而 TestGeneric 是包含 main 方法的测试类,用于演示如何使用 Alg 类来找到整数数组中的最大值。

Alg 类

  1. 泛型定义Alg 类被定义为一个泛型类 Alg<E extends Comparable<E>>,这意味着它接受一个类型参数 E,这个类型必须实现 Comparable<E> 接口。这样,Alg 类就能对 E 类型的对象进行排序和比较。

  2. findMax 方法

    • 这个方法接受一个 E 类型的数组作为参数。
    • 它首先将数组的第一个元素赋值给变量 max,作为初始的最大值。
    • 然后,它遍历整个数组,使用 compareTo 方法比较当前最大值 max 和数组中的每个元素。
    • 如果当前元素大于 max(即 max.compareTo(array[i]) < 0),则更新 max 为当前元素。
    • 最后,返回找到的最大值。

TestGeneric 类

  1. main 方法
    • 首先,定义了一个 Integer 类型的数组 array,并初始化了一些值。
    • 然后,创建了一个 Alg<Integer> 的实例 alg
    • 调用 algfindMax 方法,将整数数组作为参数传入,并将返回的最大值存储在变量 ret 中。
    • 最后,打印出找到的最大值。

逻辑关系和总结

  • Alg 类利用了 Java 的泛型机制,使其能够处理实现了 Comparable 接口的任何类型的数据。这提供了很大的灵活性,因为你可以用它来找到任何可比较类型数组中的最大值。
  • TestGeneric 类中,通过创建一个 Alg<Integer> 的实例并调用其 findMax 方法,我们演示了如何使用这个泛型类来找到整数数组中的最大值。
  • compareTo 方法用于比较两个对象的大小,这是 Comparable 接口的一部分。在这个例子中,它用于确定数组中的最大值。
  • 最终,程序打印出数组 {1, 2, 3, 4, 5} 中的最大值,即 5

整个程序的逻辑关系是:定义一个能找数组中最大元素的泛型类 -> 实例化这个类并传入特定类型的数组 -> 找出并打印数组中的最大值。

8 泛型方法

8.1 定义语法

方法限定符<类型参数列表> 返回值类型 方法名称(相残列表) {
    ...
}

8.2 示例

public class Util {
    //静态的泛型方法 需要在static后用<>声明泛型类型参数
    public static <E> void swap(E[] array, int i, int j) {
        E t = array[i];
        array[i] = array[j];
        array[j] = t;
    }
}

8.3 使用示例

//写一个泛型类,当中的方法,可以求数组中的最大值
class Alg2 {
    public <E extends Comparable<E>> E findMax(E[] array) {
        E max = array[0];
        for (int i = 0; i < array.length; i++) {
            if(max.compareTo(array[i]) < 0) {
                max = array[i];
            }
        }
        return max;
    }
}

public class TestGeneric {
	public static void main2(String[] args) {
	    Integer[] array = {1, 2, 3, 4, 5, };
	    Alg2 alg2 = new Alg2();
	    Integer ret = alg2.findMax(array);
	    //Integer ret = alg2.<Integer>findMax(array);
	    System.out.println(ret);
	}
}

在这段代码中,Alg2 类不再是一个泛型类,而是其中的 findMax 方法是一个泛型方法。这意味着这个方法可以接受任何实现了 Comparable 接口的类型数组,并返回该类型数组中的最大值。

Alg2 类

  • findMax 方法被声明为泛型方法,通过 <E extends Comparable<E>> 来定义类型参数 E,这表明 E 必须实现 Comparable<E> 接口。
  • 方法的实现逻辑与之前相同:初始化 max 为数组的第一个元素,遍历数组,通过 compareTo 方法比较每个元素,并更新 max 如果找到更大的元素,最后返回 max

TestGeneric 类

  • main 方法中,创建了一个 Alg2 的实例 alg2
  • 调用 alg2findMax 方法,并传入一个 Integer 类型的数组 array。由于 Integer 实现了 Comparable<Integer>,因此可以作为 findMax 方法的参数。
  • 将返回的最大值存储在变量 ret 中,并打印出来。

逻辑关系和总结

  1. 泛型方法findMax 是一个泛型方法,它允许在方法调用时确定类型参数,而不是在类实例化时。这提供了灵活性,因为你可以对不同类型的数组调用同一个方法,只要这些类型实现了 Comparable 接口。

  2. 类型推断:在调用 findMax 方法时,Java 编译器能够根据传入的数组类型自动推断出类型参数 E。因此,在调用 alg2.findMax(array) 时,编译器知道 E 应该是 Integer 类型。这就是为什么你可以直接写 Integer ret = alg2.findMax(array); 而不是显式指定类型参数(如 alg2.<Integer>findMax(array))。

  3. 代码复用:通过使用泛型方法,Alg2 类可以很容易地用于找到任何实现了 Comparable 接口的类型数组中的最大值,从而提高了代码的复用性。

  4. 输出:程序将打印出数组 {1, 2, 3, 4, 5} 中的最大值,即 5

总的来说,这段代码展示了如何使用泛型方法来编写灵活且可重用的代码,以便处理不同类型的数组并找到其中的最大值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/744361.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++初学者指南-2.输入和输出---文件输入和输出

C初学者指南-2.输入和输出—文件输入和输出 文章目录 C初学者指南-2.输入和输出---文件输入和输出1.写文本文件2.读文本文件3.打开关闭文件4.文件打开的模式 1.写文本文件 使用&#xff1a; std::ofstream&#xff08;输出文件流&#xff09; #include <fstream> // 文…

昂科烧录器支持KIOXIA铠侠的可编程只读存储器TH58NVG4S0HTAK0

芯片烧录行业领导者-昂科技术近日发布最新的烧录软件更新及新增支持的芯片型号列表&#xff0c;其中KIOXIA铠侠的电可擦除可编程只读存储器TH58NVG4S0HTAK0已经被昂科的通用烧录平台AP8000所支持。 TH58NVG4S0HTAK0是一个单一的3.3V 16Gbit&#xff08;18253611008位&#xff…

如何在Paperspace中使用 H100 单 GPU 和多 GPU 机器

本文将介绍如何在 DigitalOcean 的 Paperspace 平台上运行 NVIDIA H100 单卡 GPU 和 8 卡 GPU 。使用低成本的 GPU 云服务构建和扩展你的人工智能模型&#xff0c;即刻简化并加速你的机器学习工作。 如果需要了解 DigitalOcean 的 H100 GPU 云主机&#xff0c;请查看我们在 Dig…

【PA交易】BackTrader: 讨论下分析器和评测指标

前言 BackTrader的分析器主要使用的是analyzers模块&#xff0c;我们可以从Analyzers - Backtrader找到一个非常简单的示例。这个示例中使用方式很简单&#xff0c;其他分析器也可以通过如此简单封装方式进行装载。如果仅是复制粘贴官方教程&#xff0c;完全是制造互联网垃圾…

【前后端实现】AHP权重计算

AHP权重计算&#xff1a; 需求&#xff1a;前端记录矩阵维度、上三角值&#xff0c;后端构建比较矩阵、计算权重值并将结果返回给前端 比较矩阵构建 如果你想要根据上三角&#xff08;不包括对角线&#xff09;的值来构建对称矩阵&#xff0c;那么你可以稍作修改上述的generate…

Kivy tutorial 004: Making the GUI do stuff, binding to events

Kivy tutorial 004: Making the GUI do stuff, binding to events – Kivy Blog Central themes: Events and Kivy properties 中心主题&#xff1a;事件和kivy属性 We left the last tutorial with a calculator app GUI with some nice automatic behaviour, but which doe…

嵌入式C语言中常见寄存器的控制方法

使用C语言对寄存器赋值时,常常需要用到C语言的位操作方法。 把寄存器某位清零 假设a代表寄存器,且其中本来已有值。如果要把其中某一位清零且其它位不变,代码如下。 //定义一个变量 a = 1001 1111 b (二进制数)unsigned char a = 0x9f;//对 bit2 清零a &= ~(1<<…

实现批量自动化电商数据采集|商品详情页面|店铺商品信息|订单详情数据

电商数据采集是指通过技术手段获取电商平台上的商品信息、店铺信息和订单信息等数据。这些数据可以用于市场分析、竞品分析、用户行为分析等。 商品详情页面是指电商平台上展示商品详细信息的页面&#xff0c;包括商品名称、价格、图片、描述、评价等信息。通过采集商品详情页…

springboot+vue+mybatis门窗管理系统+PPT+论文+讲解+售后

如今社会上各行各业&#xff0c;都在用属于自己专用的软件来进行工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。互联网的发展&#xff0c;离不开一些新的技术&#xff0c;而新技术的产生往往是为了解决现有问题而产生的。针对于仓库信息管理方…

[学习笔记] 禹神:一小时快速上手Electron笔记,附代码

课程地址 禹神&#xff1a;一小时快速上手Electron&#xff0c;前端Electron开发教程_哔哩哔哩_bilibili 笔记地址 https://github.com/sui5yue6/my-electron-app 进程通信 桌面软件 跨平台的桌面应用程序 chromium nodejs native api 流程模型 main主进程 .js文件 node…

攻克PS之路——Day1(A1-A8)

#暑假到了&#xff0c;作为可能是最后一个快乐的暑假&#xff0c;我打算学点技能来傍身&#xff0c;首先&#xff0c;开始PS之旅 这个帖子作为我跟着B站up主学习PS的记录吧&#xff0c;希望我可以坚持下去&#xff01; 学习的链接在这里&#xff1a;A02-PS软件安装&#xff0…

Qt | 子类化 QStyle(Qt自带图标大全)

01、简介 1、把绘制自定义部件外观的步骤大致分为三大板块,如下: ①、样式元素:即指定需要绘制的图形元素(比如焦点框、按钮,工具栏等)。样式元素使 用 QStyle 类中的一系列枚举(共有 11 个枚举)进行描述。 ②、样式选项:包含了需要绘制的图形元素的所有信息,比如包含…

【QCustomPlot实战系列】QCPGraph区域高亮

使用QCPDataSelection来设置选中的区域&#xff0c;并将QCPGraph的可选择区域设置成QCP::stMultipleDataRanges void AreaPieces::initCustomPlot(QCustomPlot *parentPlot) {QVector<double> x {0, 1, 2, 3, 4, 5, 6, 7, 8};QVector<double> y {200, 560, 750…

asp.net core反向代理

新建项目 新建空白的asp.net core web项目 安装Yarp.ReverseProxy包版本为2.2.0-preview.1.24266.1 编写代码 namespace YarpStu01;public class Program {public static void Main(string[] args){var builder WebApplication.CreateBuilder(args);builder.Services.AddRev…

昇思25天学习打卡营第01天|基本介绍

作为曾经的javaer&#xff0c;本着不断学习的初心&#xff0c;报名了昇思25天的课程&#xff0c;希望自己能学会点东西的目的。 昇思MindSpore介绍 昇思MindSpore是一个全场景深度学习框架&#xff0c;旨在实现易开发、高效执行、全场景统一部署三大目标。 其中&#xff0c;…

AI大模型日报#0625:OpenAI停止不支持国家API、大模型「考上」一本、苹果上新视觉模型4M-21

导读&#xff1a;AI大模型日报&#xff0c;爬虫LLM自动生成&#xff0c;一文览尽每日AI大模型要点资讯&#xff01;目前采用“文心一言”&#xff08;ERNIE-4.0-8K-latest&#xff09;生成了今日要点以及每条资讯的摘要。欢迎阅读&#xff01;《AI大模型日报》今日要点&#xf…

【LeetCode】一、数组相关(双指针算法 + 置换)

文章目录 1、算法复杂度1.1 时间复杂度1.2 空间复杂度 2、数组3、leetcode485&#xff1a;最大连续1的个数4、leetcode283&#xff1a;移动05、leetcode27&#xff1a;移除元素 1、算法复杂度 1.1 时间复杂度 算法的执行时间与输入值之间的关系&#xff08;看代码实际总行数的…

MySQL 5.7.42 主从复制环境搭建

MySQL 5.7.42 主从复制环境搭建 下载MySQL二进制包操作系统环境配置安装过程搭建从库 本次安装环境&#xff1a; OS版本&#xff1a;Red Hat Enterprise Linux Server release 6.8 (Santiago) MySQL版本&#xff1a;5.7.42 架构&#xff1a;同一台机器&#xff0c;多实例安装搭…

洁净室(区)浮游菌检测标准操作规程及GB/T 16292-2010测试方法解读

洁净室(区)空气中浮游菌的检测。洁净区浮游菌检测是一种评估和控制洁净区(如实验室、生产车间等)内空气质量的方法。这种检测的目的是通过测量空气中的微生物(即浮游菌)数量&#xff0c;来评估洁净区的清洁度或污染程度。下面中邦兴业小编带大家详细看下如何进行浮游菌采样检测…

TSLANet:时间序列模型的新构思

实时了解业内动态&#xff0c;论文是最好的桥梁&#xff0c;专栏精选论文重点解读热点论文&#xff0c;围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型重新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;…