【数据结构】_包装类与泛型

目录

1. 包装类

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

1.2 (自动)装箱和(自动)拆箱

1.2.1 装箱与拆箱

1.2.2 自动(显式)装箱与自动(显式)拆箱

1.3 valueOf()方法

2. 泛型类

2.1 泛型类与Object类

2.2 泛型类语法

2.3 泛型类示例

2.4 裸类型(Raw Type)

2.5 泛型类的编译

2.5.1 擦除机制

2.5.2 泛型类型数组的实例化

2.6 泛型的上界

2.6.1  N为接口

 2.6.2   Number为类

3. 泛型方法

3.1 语法

3.2 泛型方法示例

3.2.1 普通类的普通泛型方法

3.2.2 普通类的静态泛型方法


1. 包装类

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

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

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

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

1.2 (自动)装箱和(自动)拆箱

1.2.1 装箱与拆箱

装箱(装包):把基本数据类型变为对应的包装类型;

拆箱(拆包):把引用类型变为基本数据类型;

1.2.2 自动(显式)装箱与自动(显式)拆箱

代码示例1:自动装箱与显式装箱:

        int a = 10;// 自动装箱Integer val = a;System.out.println(val);// 显式装箱Integer val2 = Integer.valueOf(a);Integer val3 = new Integer(a);System.out.println(val2);System.out.println(val3);

 代码示例2:自动拆箱:

        Integer val1 = 10;// 自动拆箱int a = val1;System.out.println(a);// 显式拆箱// 拆为int类型int a2 = val1.intValue();System.out.println(a2);// 拆为double类型double a3 = val1.doubleValue();System.out.println(a3);

 注:(1)自动装箱的底层逻辑:Integer包装类的valueOf()方法: 

(2)自动拆箱的底层逻辑:Integer包装类的intValue()方法:

(3)显式拆箱时,Integer包装类不止提供了intValue()方法,还提供了doubleValue()等方法:

可以根据所需的基本类型对应调用其方法,在上文示例中,输出结果为:

1.3 valueOf()方法

试运行以下代码:

        Integer a = 127;Integer b = 127;System.out.println(a==b);Integer c = 128;Integer d = 128;System.out.println(c==d);

输出结果为:

原理注解:

(1)Integer包装类的valueOf方法源代码与方法内部相关量的值:

(2)方法含义:

如果i在[low, high]即[-128, 127]之间,则返回cache缓存数组中下标为i+128位置的元素:

如i=0时存储在cache[128],i=-128时则存储在cache[0],i=127时则存储在cache[255];

即在缓存数组中存储了256个数字;

在该范围内的i调用valueOf方法调用到的是同一个元素,故而a=b;

在该范围外的i调用valueOf方法会new Integer对象,作为引用类型,==判断的是引用类型是否是对一个对象,故而a!=b;

2. 泛型类

一般的类和方法只能使用具体的类型:基本类型或自定义类,泛型就是适用于多种类型,即对参数实现了参数化;

2.1 泛型类与Object类

现要求实现一个不限制元素种类的数组,联想到Object类,示例代码如下:

class MyArray{public Object[] obj = new Object[3];public Object getPos(int pos){return obj[pos];}public void setPos(int pos,Object val){obj[pos]=val;}
}
public class Demo1 {public static void main(String[] args) {MyArray myArray = new MyArray();myArray.setPos(0,10);myArray.setPos(1,"hello");myArray.setPos(2,10.0);double ret = myArray.getPos(2);System.out.println(ret);}
}

报错如下:

 如要创建double类型变量对数组元素进行接收,则需要进行强转:

double ret = (double)myArray.getPos(2);

上文设计的数组具有以下特点:

① 任何数据都可以存储; ② 获取数据时必须进行强制类型转换;

这并非我们需要实现的泛型数组,通常需要实现的泛型数组需求是:存储一种类型的数据;

泛型编程是将类型作为参数传递的编程方式,目的是指定当前容器后可以存放某一种类型的数据;

2.2 泛型类语法

// 泛型的一般使用方法
class 泛型类名称<类型形参列表>{
}
class ClassName<T1,T2...Tn>{
}// 泛型类的继承
class 泛型类名称<类型形参列表> extends 继承类{
}
class ClassName<T1,T2,...Tn> extends ParentClass<T1>{
}

注:① 类名后的<T>代表占位符,表示当前类是一个泛型类,

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

E表示Element,K表示Key,V表示Value,N表示Number,T表示Type;

泛型当中不能实例化一个泛型类型的数组,如:

T[] ts = new T[5];

是错误的;

③ 泛型类实例化的同时会指定当前泛型类的指数参数类型,如果二者冲突,就会报错,

这也是泛型编程的一个意义所在:泛型编程在程序编译时,存储数据时自动进行类型检查; 

2.3 泛型类示例

class MyArray<T>{public T[] obj = (T[])new Object[3];public T getPos(int pos){return obj[pos];}public void setObj(int pos, T val){obj[pos]=val;}
}
public class Demo1 {public static void main(String[] args) {// 实例化对象的同时指定当前泛型类的指定参数类型// 指定参数的类型必须是引用类型,如int非法而Integer合法MyArray<Integer> myArray1 = new MyArray<Integer>();myArray1.setObj(0,10);myArray1.setObj(1,78);myArray1.setObj(2,66);double ret1= myArray1.getPos(1);System.out.println(ret1);System.out.println("-------------------");MyArray<String> myArray2 = new MyArray<String>();myArray2.setObj(0,"hello");myArray2.setObj(1,"world");myArray2.setObj(2,"java");String ret2 = myArray2.getPos(1);System.out.println(ret2);}
}

输出结果为:

2.4 裸类型(Raw Type)

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

class MyArray<T>{public T[] obj = (T[])new Object[3];public T getPos(int pos){return obj[pos];}public void setObj(int pos, T val){obj[pos]=val;}
}
public class Demo1 {public static void main(String[] args) {MyArray list = new MyArray();}
}

但请注意:裸类型是为了兼容老版本的API,在编程时请避免裸类型的使用;

2.5 泛型类的编译

2.5.1 擦除机制

class MyArray<T>{public T[] obj = (T[])new Object[3];public T getPos(int pos){return obj[pos];}public void setObj(int pos, T val){obj[pos]=val;}
}
public class Demo1 {public static void main(String[] args) {MyArray<Integer> myArray = new MyArray<Integer>();myArray.setObj(0,85);int ret = myArray.getPos(0);System.out.println(ret);}
}

查看上文代码的字节码文件:

在编译完成后,运行过程中是没有泛型的概念的,此时所有的T都被擦除为Object,通过上述的编译器生成的字节码文件中是不包含泛型的类型信息的;

2.5.2 泛型类型数组的实例化

java语法规定:在实例化数组时必须提供具体的元素类型,故而不能直接实例化泛型类型数组

方法1:(上文擦除机制部分示例代码的在泛型类中创建数组的方法):

class MyArray<E>{public E[] obj = (E[]) new Object[3];
}

但该种写法存在问题:试运行以下代码:

class MyArray<E>{public E[] obj = (E[]) new Object[3];public E getPos(int pos){return obj[pos];}public void setObj(int pos,E val){obj[pos]=val;}// 返回数组的方法public E[] getArray(){return obj;}
}
public class Demo1 {public static void main(String[] args) {MyArray<Integer> myArray = new MyArray<Integer>();myArray.setObj(0,10);myArray.setObj(1,85);Integer[] integers = myArray.getArray();}
}

报错及分析如下:

在继承与多态章节已经提到过,向下转型是不安全的;

java的数组实现非常特殊,

此处可以理解为:jvm认为使用一个固定类型如Integer或String等等类型的对象来接收Object类型对象是不安全的;

正确代码为:(使用反射)

class MyArray<E>{public E[] obj;public MyArray(Class<E>clazz, int capacity){// 参数为数组元素类型与容量obj = (E[])Array.newInstance(clazz, capacity);}public E getPos(int pos){return obj[pos];}public void setObj(int pos, E val){obj[pos]=val;}public E[] getArray(){return obj;}
}
public class Demo1 {public static void main(String[] args) {MyArray<Integer> myArray = new MyArray<>(Integer.class,10);myArray.setObj(0,1997);myArray.setObj(1,85);Integer[] integers = myArray.getArray();}
}

但以上方法并不常用,更推荐方法2:; 

继承与多态文章链接如下:

CSDN

方法2:参考java的ArrayList源码的get方法:

对于泛型类型数组的实例化,不能直接使用泛型类型作为数组元素的类型,故而可以使用Object类结合强制类型转换实现泛型类型数组的实例化:

class MyArray<E>{public Object[] obj = new Object[3];public E getPos(int pos){return (E)obj[pos];}public void setPos(int pos, Object val){obj[pos] = val;}
}

2.6 泛型的上界

2.6.1  <E extends N> N为接口

表示实例化对象时指定的参数类型一定已经实现了N接口;

// 写一个泛型类求一个数组的最大值
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 Demo2 {public static void main(String[] args) {Alg<Integer> alg = new Alg<Integer>();Integer[] array = {1,4,2,10,9,8,17,5};System.out.println("Array is:");for(Integer x:array){System.out.print(x+" ");}System.out.println();Integer val =  alg.findMax(array);System.out.println("The max element in the array is "+val);}
}

输出结果为:

   

 2.6.2  <E extends Number> Number为类

表示E是Number的子类或E就是Number本身;

参考Number (Java Platform SE 8 ) (oracle.com)

 Integer、Double、Long、Short等等都是Number常见的直接子类:

class A<E extends Number>{A<Number> a1 = new A<>();A<Integer> a2 = new A<>();A<Double> a3 = new A<>();// A<String> a4 = new A<>();
}

3. 泛型方法

3.1 语法

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

3.2 泛型方法示例

3.2.1 普通类的普通泛型方法

class Alg2{public<E extends Comparable> E findMax(E[] array){E max = array[0];for(int i=0;i<array.length;i++){if(max.compareTo(array[i])<1){max=array[i];}}return max;}
}
public class Demo4 {public static void main(String[] args) {Alg2 alg2 = new Alg2();Integer[] arr = {19,9,7,85,25};System.out.println("Array is: ");for(Integer x:arr){System.out.print(x+" ");}System.out.println();Integer val = alg2.<Integer>findMax(arr);System.out.println("The max element in the array is "+val);}
}

输出结果为:

 注:调用泛型方法时泛型类型可以省略:

 Integer val = alg2.<Integer>findMax(arr);

 上行代码可以简写为:

 Integer val = alg2.findMax(arr);

编译器会根据传递给泛型方法的参数arr的类型判断E的类型;

3.2.2 普通类的静态泛型方法

class Alg2{public static<E extends Comparable> E findMax(E[] array){E max = array[0];for(int i=0;i<array.length;i++){if(max.compareTo(array[i])<1){max=array[i];}}return max;}
}
public class Demo4 {public static void main(String[] args) {Integer[] arr2 = {19,9,7,85,25};System.out.println("Array is: ");for(Integer x:arr2){System.out.print(x+" ");}System.out.println();Integer val = Alg2.findMax(arr2);System.out.println("The max element in the array is "+val);}
}

输出结果为:

静态泛型方法不再依赖于类的对象存在,可以使用类名直接调用;

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

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

相关文章

【深度学习笔记】计算机视觉——目标检测和边界框

目标检测和边界框 前面的章节&#xff08;例如 sec_alexnet— sec_googlenet&#xff09;介绍了各种图像分类模型。 在图像分类任务中&#xff0c;我们假设图像中只有一个主要物体对象&#xff0c;我们只关注如何识别其类别。 然而&#xff0c;很多时候图像里有多个我们感兴趣…

某大型制造企业数字化转型规划方案(附下载)

目录 一、项目背景和目标 二、业务现状 1. 总体应用现状 2. 各模块业务问题 2.1 设计 2.2 仿真 2.3 制造 2.4 服务 2.5 管理 三、业务需求及预期效果 1. 总体业务需求 2. 各模块业务需求 2.1 设计 2.2 仿真 2.3 制造 2.4 服务 2.5 管理 四、…

数字化转型导师坚鹏:证券公司数字化营销

证券公司数字化营销 ——借力数字化技术实现零售业务的批量化、精准化、场景化、智能化营销 课程背景&#xff1a; 很多证券公司存在以下问题&#xff1a; 不知道如何提升证券公司数字化营销能力&#xff1f; 不知道证券公司如何开展数字化营销工作&#xff1f; 不知道…

Java虚拟机 - JVM

JVM的内存区域划分 JVM它其实也是一个进程,进程运行的过程中,会从操作系统中申请一些资源.内存就是其中的一种.这些内存就支撑了java程序的运行.JVM从系统中申请的一大块内存,会根据实际情况和使用用途来划分出不同的空间,这个就是区域划分.它一般分为 堆区, 栈区, 程序计数器…

springboot240基于Spring boot的名城小区物业管理系统

基于Spring boot的名城小区物业管理系统的设计与实现 摘要 当下&#xff0c;正处于信息化的时代&#xff0c;许多行业顺应时代的变化&#xff0c;结合使用计算机技术向数字化、信息化建设迈进。以前相关行业对于物业信息的管理和控制&#xff0c;采用人工登记的方式保存相关数…

InnoDB存储引擎对MVCC的实现

MVCC MVCC的目的 在搞清楚MVCC之前,我们要搞懂一个问题,MVCC到底解决的是什么问题? 我用一句话概括,那就是为了解决读-写可以一起的问题! 在我们的印象里,InnoDB可以读读并发,不能读写并发,或者写写并发 这是很正常的想法,因为如果读写并发的化,会有并发问题 而对于写写…

构建安全的REST API:OAuth2和JWT实践

引言 大家好&#xff0c;我是小黑&#xff0c;小黑在这里跟咱们聊聊&#xff0c;为什么REST API这么重要&#xff0c;同时&#xff0c;为何OAuth2和JWT在构建安全的REST API中扮演着不可或缺的角色。 想象一下&#xff0c;咱们每天都在使用的社交媒体、在线购物、银行服务等等…

Sqli-labs靶场第16关详解[Sqli-labs-less-16]自动化注入-SQLmap工具注入

Sqli-labs-Less-16 #自动化注入-SQLmap工具注入 SQLmap用户手册&#xff1a;文档介绍 - sqlmap 用户手册 以非交互式模式运行 --batch 当你需要以批处理模式运行 sqlmap&#xff0c;避免任何用户干预 sqlmap 的运行&#xff0c;可以强制使用 --batch 这个开关。这样&#xff0…

GC机制以及Golang的GC机制详解

要了解Golang的GC机制,就需要了解什么事GC,以及GC有哪几种实现方式 一.什么是GC 当一个电脑上的动态内存不再需要时&#xff0c;就应该予以释放&#xff0c;以让出内存&#xff0c;这种内存资源管理&#xff0c;称为垃圾回收&#xff08;Garbage Collection&#xff09;&#x…

最长上升子序列(LIS)简介及其例题分析

一.最长上升子序列&#xff08;LIS&#xff09;的相关知识 1.最长上升子序列&#xff08;Longest Increasing Subsequence&#xff09;&#xff0c;简称LIS&#xff0c;也有些情况求的是最长非降序子序列&#xff0c;二者区别就是序列中是否可以有相等的数。假设我们有一个序…

【论文笔记】Initializing Models with Larger Ones

Abstract 介绍权重选择&#xff0c;一种通过从预训练模型的较大模型中选择权重子集来初始化较小模型的方法。这使得知识从预训练的权重转移到更小的模型。 它还可以与知识蒸馏一起使用。 权重选择提供了一种在资源受限的环境中利用预训练模型力量的新方法&#xff0c;希望能够…

【Linux】软件管理yum | 编辑器vim | vim插件安装

目录 1. Linux软件管理yum 1.1 什么是软件包 1.2 查看软件包 1.3 如何安装软件 1.4 如何卸载软件 2. Linux编辑器vim 2.1 vim的基本概念 2.2 vim的基本操作 2.3 vim正常模式命令集 2.4 vim末行模式命令集 2.5 简单vim配置 2.6 插件安装 1. Vim-Plug 3. coc.nvim …

如何自己系统的学python

学习Python是一项很好的投资&#xff0c;因为它是一种既强大又易于学习的编程语言&#xff0c;适用于多种应用&#xff0c;如数据分析、人工智能、网站开发等。下面是一个系统学习Python的步骤建议&#xff1a; 基础准备 安装Python&#xff1a; 访问Python官网下载最新版本的…

微信小程序-生命周期

页面生命周期 onLoad: 页面加载时触发的方法&#xff0c;在这个方法中可以进行页面初始化的操作&#xff0c;如获取数据、设置页面状态等。 onShow: 页面显示时触发的方法&#xff0c;在用户进入页面或从其他页面返回该页面时会调用此方法。可以在此方法中进行页面数据刷新、动…

Onenote软件新建笔记本时报错:无法在以下位置新建笔记本

报错现象&#xff1a; 当在OneNote软件上&#xff0c;新建笔记本时&#xff1a; 然后&#xff0c;尝试重新登录微软账户&#xff0c;也不行&#xff0c;提示报错&#xff1a; 解决办法&#xff1a; 打开一个新的记事本&#xff0c;复制粘贴以下内容&#xff1a; C:\Users\Adm…

Mysql中的事务

什么是事务&#xff1a; 多条sql语句&#xff0c;要么全部成功&#xff0c;要么全部失败。 事务的特性&#xff1a; 1&#xff1a;原子性(Atomic)&#xff1a; 组成一个事务的多个数据库操作是一个不可分割的原子单元&#xff0c;只有所有操作都成功&#xff0c;整个事务才会…

C++调用lua函数

C 调用Lua全局变量(普通) lua_getglobal(lua, "width");int width lua_tointeger(lua,-1);lua_pop(lua,1);std::cout << width << std::endl;lua_close(lua); 这几行代码要放到lua_pcall(lua, 0,0,0);之后才可以. C给lua传递变量 lua_pushstring(lua, …

Python 操作 Excel,如何又快又好?

➤数据处理是 Python 的一大应用场景&#xff0c;而 Excel 则是最流行的数据处理软件。因此用 Python 进行数据相关的工作时&#xff0c;难免要和 Excel 打交道。Python处理Excel 常用的系列库有&#xff1a;xlrd、xlwt、xlutils、openpyxl ◈xlrd &#xff0d; 用于读取 Exce…

Rocky 9 安装 R-CytoTRACE

官网给出的详细指南&#xff0c;只是可能大家打不开或者懒得去看E文。 第一步&#xff0c;下载CytoTRACE安装包。 wget https://cytotrace.stanford.edu/CytoTRACE_0.3.3.tar.gz 第二步&#xff0c;打开R或者Rstudio-server # 安装依赖包 if (!requireNamespace("Bioc…

视频云平台——搭建SRS5平台支持GB28181视频流的推送

&#x1f4e2;欢迎点赞 &#xff1a;&#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff0c;赐人玫瑰&#xff0c;手留余香&#xff01;&#x1f4e2;本文作者&#xff1a;由webmote 原创&#x1f4e2;作者格言&#xff1a;新的征程&#xff0c;我们面对的不仅…