认识泛型和包装类

news2024/11/17 21:31:11

认识泛型和包装类

  • 包装类
    • 基本数据类型和对应的包装类
    • 装箱和拆箱
    • 自动装箱和自动拆箱
  • 什么是泛型
  • 引出泛型
    • 语法
  • 泛型类的使用
    • 语法
    • 示例
    • 类型推导
  • 裸类型(Raw Type)
    • 说明
  • 泛型如何编译的
    • 擦除机制
  • 泛型的上界
    • 语法
    • 示例
    • 复杂示例
  • 泛型方法
    • 定义方法
    • 示例
    • 使用类型推导和不用类型推导
    • 静态的泛型方法

包装类

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

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

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

除了Integer和Charater,其余基本类型的包装类都是大写字母。

装箱和拆箱

装箱:把基本数据类型变为包装类类型的过程叫做装箱
拆箱:把包装类类型变为基本数据类型的过程叫做拆箱

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

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

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

自动装箱和自动拆箱

装箱/装包:自动(隐式)装箱 显式装箱
拆箱/拆包:自动(隐式)拆箱 显式拆箱
在使用过程中,装箱和拆箱带来了不少代码量,为了减少开发者的负担,java提供了自动机制

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

        Integer integer1 = Integer.valueOf(a);  //显式装箱
        Integer integer2 = a;                   //隐式装箱,底层帮我们调用了Integer.valueOf(a)

        int i = integer1.intValue();            //显式拆箱
        int j = integer1;                       //隐式拆箱,底层帮我们调用了integer1.intValue()
        
    }
}

接下来我们再来看一段代码:

public class Test {
    //下面代码输出什么,为什么?
    public static void main(String[] args) {
       Integer a = 100;
       Integer b = 100;

       Integer c = 200;
       Integer d = 200;

        System.out.println(a == b);
        System.out.println(c == d);
    }
    //结果为:
    //true
    //false
}

为什么是这种结果呢,这跟我们装箱中的valueOf(int i)这个方法有关系,我们来看源码:

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

当 i 在 IntegerCache.low 和 IntegerCache.high之间返回数组中的值,若不是则创建新对象,所以我们再来看这个范围是什么
在这里插入图片描述
在这里插入图片描述
所以100在这个区域内,返回同一个数,所以输出true,而200不在,所以创建了两个新对象,所以输出false

什么是泛型

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

引出泛型

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

  1. 我们以前学过的数组,只能存放指定类型的元素,例如:int[] array = new int[10];Strings[] strs = new String[10];
  2. 所有类的父类默认为Object类。数组是否可以创建为Object?
public class MyArray {
    public Object[] myArray = new Object[10];

    public Object getMyArray(int pos) {
        return myArray[pos];
    }

    public void setMyArray(int pos,Object value) {
        this.myArray[pos] = value;
    }
}
public class Test {

    public static void main(String[] args) {
        MyArray myArray = new MyArray();
        myArray.setMyArray(0,123);
        myArray.setMyArray(1,"onward");

        int num = (int)myArray.getMyArray(0);
        String str = (String)myArray.getMyArray(1);
        System.out.println(num);
        System.out.println(str);
    }
    //结果为:
    //123
    //onward
}

问题:

  • 任何数据都可以存放,存放太乱,什么类型都能存放
  • 数组1号下标本身就是字符串,但是编译报错,必须进行强转,什么类型都需要强转。

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

语法

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

class ClassName<T1,T2,...,Tn>{

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

我们可以对上面的代码进行改写

public class MyArray2 <T>{
    public Object[] myArray = new Object[10];

    public T getMyArray(int pos) {
        T value = (T)myArray[pos];
        return value;
    }

    public void setMyArray(int pos, T value) {
        this.myArray[pos] = value;
    }
}

public class Test {

    public static void main(String[] args) {
        MyArray2<Integer> myArray = new MyArray2<Integer>();    //指定当前类型为Integer
        myArray.setMyArray(0,123);  //自动类型强转
        //myArray.setMyArray(1,"onward");       //自动类型检查,编译报错
        myArray.setMyArray(1,12);
        int num = myArray.getMyArray(0);    //不用进行强制类型强转
        System.out.println(num);

        MyArray2<String> myArray2 = new MyArray2<String>();
        myArray2.setMyArray(0,"onward");
        myArray2.setMyArray(1,"forward");
        String str = myArray2.getMyArray(0);
        System.out.println(str);
    }
    //结果为:
    //123
    //onward
}

注意:

  • 类名后的 < T > 代表占位符,表示当前类是一个泛型类
    了解:【规范】类型形参一般使用一个大写字母表示,常用的名称有:
  • E 表示 Element
  • K 表示 Key
  • V 表示 Value
  • N 表示 Number
  • T 表示 Type
  • S、U、V等等表示第二、第三、第四个类型
  • 类型后加入< Integer >指定当前类型,编译器会在我们存放元素的时候进行类型检查。

泛型类的使用

语法

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

示例

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

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

类型推导

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

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

裸类型(Raw Type)

说明

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

public class MyArray2 <T>{
    public Object[] myArray = new Object[10];

    public T getMyArray(int pos) {
        T value = (T)myArray[pos];
        return value;
    }

    public void setMyArray(int pos, T value) {
        this.myArray[pos] = value;
    }
}
public class Test {

    public static void main(String[] args) {
        MyArray2 myArray = new MyArray2();    //裸类型
        myArray.setMyArray(0,12);
        myArray.setMyArray(1,"onward");
        int num = (int)myArray.getMyArray(0);
        String str = (String) myArray.getMyArray(1);
        System.out.println(num);
        System.out.println(str);
    }
    //结果为:
    //12
    //onward
}

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

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

泛型如何编译的

擦除机制

在编译的过程当中,将所有的T替换为Object这种机制,我们称为:擦除机制。
java的泛型机制是在编译级别实现的,编译器生成的字节码在运行期间并不包含泛型的类型信息。
有关擦除机制的文章介绍我们可以参考:链接: Java泛型擦除机制之答疑解惑

泛型的上界

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

语法

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

示例

public class MyArray2 <E extends Number>{
    public Object[] myArray = new Object[10];

    public E getMyArray(int pos) {
        E value = (E)myArray[pos];
        return value;
    }

    public void setMyArray(int pos, E value) {
        this.myArray[pos] = value;
    }
}

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

public class Test {

    public static void main(String[] args) {
        MyArray2<Integer> myArray = new MyArray2<Integer>();
        //正常,因为Integer是Number的子类型   
        myArray.setMyArray(0,123);  
        myArray.setMyArray(1,12);
        int num = myArray.getMyArray(0);   
        System.out.println(num);

        MyArray2<String> myArray2 = new MyArray2<String>(); 
        //编译报错:java: 不兼容的类型: java.lang.String无法转换为java.lang.Number,因为String不是Number的子类型
        myArray2.setMyArray(0,"onward");
        myArray2.setMyArray(1,"forward");
        String str = myArray2.getMyArray(0);
        System.out.println(str);
    }
    //结果为:
    //123
    //onward
}

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

复杂示例

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

E必须是实现了Comparable接口的

泛型方法

定义方法

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

示例

需求:写一个泛型类。当中有个方法,可以求数组最大值

public class Alg <E>{
    public E findMax(E[] array){
        E max = array[0];
        for (int i = 1; i < array.length - 1; i++) {
            if(array[i] > max){     //编译报错
                max = array[i];
            }
        }
        return max;
    }
}

上述代码会编译报错。由于擦除机制,在运行的时候类型E都会被擦除为Object类
类如果要比较大小,引用存的是地址,无法直接比较,要扩展Comparable接口,才能调用compareTo()方法。

public class Alg <E extends Comparable<E>>{
    public E findMax(E[] array){
        E max = array[0];
        for (int i = 1; i < array.length - 1; i++)
            if (max.compareTo(array[i]) < 0) {
                max = array[i];
            }
        return max;
    }
}
public class Test {
    public static void main(String[] args) {
        Integer[] array = {1,33,67,8,99,4};
        Alg<Integer> alg = new Alg<>();
        System.out.println(alg.findMax(array));
    }
}

使用类型推导和不用类型推导

上述代码还可以这样写将继承接口写在方法上

public class Alg2 <E>{
    public <E extends Comparable<E>>E findMax(E[] array){
        E max = array[0];
        for (int i = 1; i < array.length - 1; i++)
            if (max.compareTo(array[i]) < 0) {
                max = array[i];
            }
        return max;
    }
}
public class Test {
    public static void main(String[] args) {
        Integer[] array = {1,33,67,8,99,4};
        Alg2<Integer> alg2 = new Alg2<>();
        System.out.println(alg2.findMax(array));    //可以前面不写,会自动检查,类型推导
        System.out.println(alg2.<Integer>findMax(array));   //不使用类型推导
    }
}

静态的泛型方法

public class Alg3 <E>{
    //静态的泛型方法 需要在static后用<>声明泛型类型参数
    public static <E extends Comparable<E>> E findMax(E[] array){
        E max = array[0];
        for (int i = 1; i < array.length - 1; i++)
            if (max.compareTo(array[i]) < 0) {
                max = array[i];
            }
        return max;
    }
}
public class Test {
    public static void main(String[] args) {
        Integer[] array = {1,33,67,8,99,4};
        System.out.println(Alg3.findMax(array));
    }
}

关于泛型和包装类我们先了解到这,对泛型的深入了解还需要一定时间的沉淀,才能对其有深刻的理解和掌握,道阻且长,我们一起努力!!!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电脑安装OpenWRT系统

通过网盘分享的文件&#xff1a;OpenWRT 链接: https://pan.baidu.com/s/1nrRBeKgGviD31Omji480qA?pwd9900 提取码: 9900 下面开始教程&#xff1a; 1.先把普通U盘制作成一个PE启动盘&#xff0c;我用的是微PE工具箱&#xff0c;直接安装PE到U盘。 2.把写盘工具和openWRT系统…

项目中使用简单的立体3D柱状图,不用引入外部组件纯css也能实现

在一些项目需求中&#xff0c;可能会遇到下面这种场景&#xff0c;3d柱状图来展示百分比&#xff0c;但是又不想引入外部组件&#xff0c;下面就用纯css给大家封装了一个组件 先赞后看&#xff0c;养成习惯 <template><view class"lui-column-bg" :sty…

DApp开发入门指南:从概念到实践

随着区块链技术的不断发展&#xff0c;去中心化应用&#xff08;DApp&#xff09;逐渐成为科技领域的热门话题。DApp不仅打破了传统应用的中心化控制&#xff0c;还为开发者和用户提供了更高的安全性、透明度和自治性。本文将带你深入了解DApp的开发流程以及如何设计合理的DApp…

SIMCOM-A4767C-4G模块ARM开发板上网配置流程

进入linux系统命令行后。 配置4G网卡 1、打开串口&#xff0c;会进入编辑模式。 busybox microcom /dev/ttyUSB22、设置网络模式&#xff0c;回复OK表示设置成功。 ATDIALMODE03、设置ECM模式&#xff0c;回复OK表示设置成功。设置成功后4g会重启。 AT$MYCONFIG"USB…

Vue3:el-table实现日期的格式化

后端如果返回的是时间戳&#xff0c;需要我们进行日期格式化 例如&#xff1a;2024-09-11T14:19:14 定义一个日期解析的工具组件 export function formatDateAsYYYYMMDDHHMMSS(dateStr: any) {const date new Date(dateStr);const year date.getFullYear();const month S…

【已解决】请教 “Sa-Token 集成 xxl-job,报错:非 web 上下文无法获取 HttpServletRequest” 如何解决

1. xxl-job 报错日志 2024-09-11 17:19:04 [com.xxl.job.core.thread.JobThread#run]-[133]-[xxl-job, JobThread-3-1726046344528] <br>----------- xxl-job job execute start -----------<br>----------- Param: 2024-09-11 17:19:04 [com.xxl.job.core.thread…

使用mingw64 编译 QT开发流程

1. 安装QT5 QT5.12.12 安装时选择mingw的开发包 2. 使用qtdesigner 进行ui设计 生成ui文件 3. 将ui文件转换为.h 文件 uic mywindow.ui -o ui_mywindow.h代码中指向生成的 UI 对象的地方 要改成这个Form 4. 编译 创建mainwindow.cpp #include "mainwindow.h"…

PROTOTYPICAL II - The Practice of FPGA Prototyping for SoC Design

The Art of the “Start” The semiconductor industry revolves around the “start.” Chip design starts lead to more EDA tool purchases, more wafer starts, and eventually to more product shipments. Product roadmaps develop to extend shipments by integrating…

Ton的编译过程(上)

系列文章目录 FunC编写初始准备 文章目录 系列文章目录预先准备第一个FunC合约深入compileFunc的内部compileFunc初探艾丽卡的疑惑package.json 初览index.js 预先准备 首先请大家跟着艾丽卡一步一步的完成FunC编写初始准备 这里面环境的搭建。 接下来&#xff0c;请做好下面…

博弈论中纳什均衡和囚徒困境的探索性分析

一. 纳什均衡求解 纳什均衡&#xff0c;又称为非合作博弈均衡&#xff0c;是博弈论中的一个核心概念。纳什均衡描述的是在非合作博弈中&#xff0c;每个参与者都选择了自己的最优策略&#xff0c;并且考虑到了其他参与者的策略选择。在这种状态下&#xff0c;没有任何一个参与…

HAL库学习梳理——SPI

笔者跟着B站铁头山羊视频学习 STM32-HAL库 开发教程。下面对HAL库有关 SPI 课程知识和应用做一个梳理。 无流可省 1、SPI 总线基本原理 SPI总线&#xff08;Series Peripheral Interface&#xff09;串行外设接口&#xff0c;适用于高速、双向数据传输场景。 MOSI MISO SCK …

提权——Linux

一、系统漏洞提权 #kali的nmap命令 nmap -O 目标ip 通过当前系统的内核版本搜索当前系统的漏洞&#xff0c;进行利用 搜索漏洞 对linux系统的漏洞进行利用&#xff08;脏牛、脏管道等&#xff09; 利用漏洞搜索工具&#xff0c;搜索当前系统是否存在一些漏洞 linux-exp…

单值二叉树--(C语言)

题目如下&#xff1a; 如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。 只有给定的树是单值二叉树时&#xff0c;才返回 true&#xff1b;否则返回 false。 示例 1&#xff1a; 输入&#xff1a;[1,1,1,1,1,null,1] 输出&#xff1a;true示例 2&a…

Linux - 探秘/proc/sys/net/ipv4/ip_local_port_range

文章目录 Pre概述默认值及其意义评估需求如何调整临时修改永久修改测试和验证 修改的潜在影响 Pre Linux - 探秘 Linux 的 /proc/sys/vm 常见核心配置 计划&#xff1a; 简要解释 /proc/sys/net/ipv4/ip_local_port_range 文件的功能和作用。介绍该文件的默认值及其影响。说明…

Java设计模式—面向对象设计原则(三) -----> 依赖倒转原则DIP(完整详解,附有代码+案例)

文章目录 3.3 依赖倒转原则(DIP)3.3.1概述3.3.2 案例 3.3 依赖倒转原则(DIP) Dependency Inversion Principle&#xff0c;DIP 3.3.1概述 高层模块不应该依赖低层模块&#xff0c;两者都应该依赖其抽象&#xff1b;抽象不应该依赖细节&#xff0c;细节应该依赖抽象。简单的说…

C++——深部解析哈希

好久不见给大家分享一张图片吧 目录 前言 二、库文件 1、哈希冲突 2 哈希函数 3、闭散列 三 、闭散列的实现和底层逻辑 1、哈希表&#xff08;闭散列&#xff09;的定义 2、哈希表&#xff08;闭散列&#xff09;的插入 3、哈希表&#xff08;闭散列&#xff09;的查找 4.哈希表…

解决Linux服务器上下载pytorch速度过慢的问题

需要下载的是GPU版本的pytorch&#xff0c;版本torch1.13.1cu116 尝试方法1&#xff1a; pip install torch1.13.1cu116 torchvision0.14.1cu116 torchaudio0.13.1 --extra-index-url https://download.pytorch.org/whl/cu116 但是默认是从官网下载&#xff0c;龟速到200kb/s左…

Vscode中启动Vue2.x项目运行正常但templete部分UI组件红色波浪线报错 ts(2339)

Vscode中启动Vue2.x项目运行正常但templete部分UI组件红色波浪线报错 错误示例 原因 Vue - Official 插件升级导致的问题(具体原因有待查询) 解决方案 打开Vscode软件 —> 找到扩展插件 —> 选择Vue - Official —> 安装特定版本(版本 < V2.0.28就行) —> 重…

linux-L7-linux 查看json文件

输入如下进行查看 cat your_file.json | less