Java认识泛型类

news2024/12/23 14:29:16

一、包装类

认识泛型类之前先来认识一下包装类

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

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

514bc8b4f8ac42f7a529af966a662c3b.png

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

        2、 装箱和拆箱

装箱:把基本类型变成引用类型

 public static void main(String[] args) {
        //包装类
        int a = 10;
        Integer ii2=Integer.valueOf(a);//显示装箱
        Integer ii=a;//自动装箱,底层调用也是valueOf
    }

拆箱:把引用类型变成基本类型

 public static void main(String[] args) {
        Integer ii=10;//装箱
        int a = ii;//自动拆箱
        int aa = ii.intValue();//手动拆箱
        double d=ii.doubleValue();

    }

问题:看下列代码输出结果是什么

 public static void main(String[] args) {
        Integer a = 127;
        Integer b = 127;
        System.out.println(a == b);

        Integer c = 128;
        Integer d = 128;
        System.out.println(c == d);
    }

运行结果 

3102f98b79d74d98828db3e772812285.png

我们发现都是比较Integer类型的数据,那么为什么会出现结果不同的情况呢?

首先因为上面都是装箱操作,底层调用valueOf方法,所以来看看这个方法是怎么实现的

22373c5e49a3492ab5b78ab729781f99.png

71a771333bcf4935aef02fe1ab1db7b4.pnglow是-128

ab51b18b22dd479f9a480c2589d6e8fd.pnga32b2cd50b3841d6bf3d6aefe75e8bd0.pnghigh是127

总共256个数字,因此数组大小是256

 if语句判断是如果i是在某一个范围内就数组取值,相反如果不在范围内就实例化一个新的对象

a和b都是127,在数组范围内,可以直接去取对应数组值,c和d虽然都是128,但是不在数组范围内,因此都会实例化一个新对象,所以结果不同

二、泛型

        1、什么是泛型

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

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

总的来说泛型主要做的是把类型参数化,意味着可以传指定的类型参数

        2、如何实现

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

首先我们想到的是Object类,可以接受任何数据类型

class MyArray{
   public Object[] array=new Object[10];
   
    public void setValue(int pos,Object x) {
        array[pos] = x;
    }

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

}
 public static void main(String[] args) {
        MyArray myArray = new MyArray();
        myArray.setArray(0,1);
        myArray.setArray(1,"hello");
        myArray.setArray(2,2.3);

        String s = (String)myArray.getVal(1);//需要强转
        System.out.println(s);
    }

虽然任何类型数据都可以存放,但1号下标本身就是字符串,编译出现报错。必须进行强制类型转换,并且数据多了的话就不知道某个位置的数据是什么类型,不知道强转为什么。

所以,泛型的主要目的:就是指定当前的容器,要持有什么类型的对象,让编译器去做检查。此时,就需要把类型,作为参数传递。需要什么类型,就传入什么类型。

//<T>代表当前类是泛型类
//E表示Element
//K表示key
//V表示value
//N表示Number
//T表示Type

class MyArray<T>{
   public Object[] array=new Object[10];//以后这么写
   // public T[] array = (T[]) new Object[10];勉强可以,但是也会存在问题
    //public T[] ts = new T[5];不对的
    public void setArray(int pos,T x) {
    //放T类型的x
        array[pos] = x;
    }

    public T getVal(int pos){
        return (T)array[pos];//强转成T类型
    }
}

使用方法:类名后面写<>,里面写要传给这个类的类型

 public static void main(String[] args) {

        MyArray<Integer> myArray= new MyArray<>();//当编译器可以根据上下文推导出类型实参时,可以省略类型实参的填写
        myArray.setArray(0,2);
        Integer x = myArray.getVal(0);

        //尖括号里只能放类类型,不能放基本类型,例如int,char,boolean等
        MyArray<String> myArray1 = new MyArray<>();
        myArray1.setArray(1,"hello");
        System.out.println(myArray1.getVal(1));
    }

用泛型方法优点:

1、编译的时候:每次存储数据的时会自动检查你存入的数据是不是和你指定的数据类型一样。

2、接收得到的值时,不需要我们进行强转。

注意:

1、运行的时候没有泛型这样的概念,代码在JVM中运行,也就是说在JVM中没有泛型的概念。

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

        3、泛型如何编译

擦除机制:在编译的过程当中,将所有的T替换为Object这种机制。

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

问题:为什么T[] ts = new T[5]; 是不对的,编译的时候,替换为Object,不是相当于:Object[] ts = newObject[5]吗?

答:编译的时候不知到T是什么类型的数组,只有擦完之后才知道,所以new数组是要写具体的数组类型,并且java规定new数组要写具体类型。

另一种存在问题的创建数组写法:实例化泛型类型的数组

Object类是所有类的父类,但是Object[]不是所有数组的父类,Object[]不是Stri(ng的父类,但Object是String的父类,因此下面代码中的getArray()只能通过Object[]接收。

class MyArray<T>{
   //实例化泛型类型的数组
    public T[] array = (T[]) new Object[10];//勉强通过但是也有问题,就是下面所写的问题
  
    public void setValue(int pos,T x) {
        array[pos] = x;
    }

    public T getVal(int pos){
        return (T)array[pos];//强转成T类型
    }
    public T[] getArray(){
        return (T[])array;
    }
}    
        public static void main3(String[] args) {
        MyArray<String> myArray = new MyArray<>();
        String[] strings = (String[]) myArray.getArray();//错误
        //Object类是所有类的父类,但是Object[]不是所有数组的父类,Object[]不是String的父类,但Object是String的父类
        //只能通过Object[]接受
        Object[] strings1 = myArray.getArray();//只能使用Object[]接收
    }

三、泛型的上界

        1、定义泛型类

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

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

 只接受 Number 的子类型作为 E 的类型实参或者是Number自己

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

 例:定义一个泛型类,找到数组中的最大值

class Alg<T extends Comparable<T>>{
    //T实现了Comparable接口,就可以使用compareTo和T类型的max比较
    public T findMax(T[] array){
        T max = array[0];
        for (int i = 0; i < array.length; i++) {
            if(array[i].compareTo(max)>0){
                //引用类型不能直接比较,需要用Comparable<>
                max = array[i];
            }
        }
        return max;
    }
}  
 public static void main4(String[] args) {
        //泛型的上界
        //定义一个泛型类,找到数组中的最大值
       Alg<Integer> alg = new Alg<>();//Integer实现了Comparable接口
        Integer[] array = {1,2,3,4};//装箱
        Integer x = alg.findMax(array);
        System.out.println(x);

    }

        2、泛型方法 

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

class Alg2{
    public <T extends Comparable<T>>T findMax(T[] array){
        T max = array[0];
        for (int i = 0; i < array.length; i++) {
            if(array[i].compareTo(max)>0){
                //引用类型不能直接比较,需要用Comparable<>
                max = array[i];
            }
        }
        return max;
    }//泛型方法,也可以是静态的就不需要实例化对象了
}
public class test {
    public static void main(String[] args) {
        Alg2 alg2 = new Alg2();
        Integer[] array = {1,2,3,4,5};
        Integer x = alg2.findMax(array);
        Integer x2 = alg2.<Integer>findMax(array);//<Integer>可以省略
        System.out.println(x);
    }

 四、裸类型

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

MyArray list = new MyArray();

我们不要自己去使用裸类型,裸类型是为了兼容老版本的 API 保留的机制下面的类型擦除部分。

 

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

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

相关文章

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单实战案例 之九 简单闪烁效果

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单实战案例 之九 简单闪烁效果 目录 Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单实战案例 之九 简单闪烁效果 一、简单介绍 二、简单闪烁效果实现原理 三、简单闪烁效果案例实现简单步骤 四、注意事项 一、简单…

玩电脑突然停电对电脑有影响吗

在现代社会中&#xff0c;电脑已成为我们日常生活和工作中不可或缺的一部分。然而&#xff0c;当我们正在专注于工作或娱乐时&#xff0c;突然停电可能会给我们带来不小的困扰。那么&#xff0c;玩电脑时突然停电会对电脑产生哪些影响呢&#xff1f;本文将深入探讨这一问题&…

Groovy基础入门

一、Groovy简介 Groovy是运行在JVM中的一种动态语言&#xff0c;可以在Java平台上进行编程&#xff0c;使用方式基本与使用Java代码的方式相同&#xff0c;它的语法与Java语言的语法很相似&#xff0c;与Java相比&#xff0c;Groovy更加灵活、简洁&#xff0c;而且完成同样的功…

ChatGPT与传统搜索引擎的区别:智能对话与关键词匹配的差异

引言 随着互联网的快速发展&#xff0c;信息的获取变得比以往任何时候都更加便捷。在数字化时代&#xff0c;人们对于获取准确、及时信息的需求愈发迫切。传统搜索引擎通过关键词匹配的方式为用户提供了大量的信息&#xff0c;然而&#xff0c;这种机械式的检索方式有时候并不…

SpringMVC第一个helloword项目

文章目录 前言一、SpringMVC是什么&#xff1f;二、使用步骤1.引入库2.创建控制层3.创建springmvc.xml4.配置web.xml文件5.编写视图页面 总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; SpringMVC 提示&#xff1a;以下是本篇文章正文内容&#xf…

Linux(CentOS)安装Redis教程_简单快捷

一、安装依赖 因为redis是用C语言开发的&#xff0c;所以在安装之前需要确定是否安装gcc环境&#xff08;gcc -v&#xff09;&#xff0c;如果没有安转可以执行一下命令进行安装 [rootlocalhost ~]# yum install -y gcc 二、下载安装包 1.在官网先进行下载 官网地址&#x…

linux 内存介绍

大致共有四类&#xff1a;VSS、RSS、PSS、USS &#xff0c;通常情况下&#xff0c;VSS > RSS > PSS > USS 1.VSS(Virtual Set Size)虚拟耗用内存&#xff08;包含共享库占用的内存&#xff09; VSS表示一个进程可访问的全部内存地址空间的大小。这个大小包括了进程已…

网络:udptcp套接字

目录 协议 网络传输基本流程 网络编程套接字 udp套接字编程 udp相关代码实现 sock函数 bind函数 recvfrom函数 sendto函数 udp执行指令代码 popen函数 udp多线程版收发消息 tcp套接字编程 tcp套接字代码 listen函数 accept函数 read/write函数 connect函数 recv/…

Gitlab CI---could not read username for xxx: no such device or address

0 Preface/Foreword 项目开发中&#xff0c;经常会使用第三方的算法或者功能&#xff0c;那么就需要把对应的repo以子模块的方式添加到当前repo中。 添加命令&#xff1a; git submodule add <URL> 1 问题表现 子模块添加成功&#xff0c;但是GitLab CI阶段&#xff…

如何在Apache Arrow中定位与解决问题

如何在apache Arrow定位与解决问题 最近在执行sql时做了一些batch变更&#xff0c;出现了一个 crash问题&#xff0c;底层使用了apache arrow来实现。本节将会从0开始讲解如何调试STL源码crash问题&#xff0c;在这篇文章中以实际工作中resize导致crash为例&#xff0c;引出如何…

ChatGLM3:AttributeError_ can‘t set attribute ‘eos_token‘

最近在微调 ChatGLM3-6b 时&#xff0c;训练好模型之后&#xff0c;调用inference_hf.py函数验证模型的时候报了如下错误&#xff0c;下面是解决方案。 我在训练时使用的是ptuning_v2.yaml配置文件&#xff0c;训练运行代码如下&#xff1a; CUDA_VISIBLE_DEVICES1 python fi…

llama-index 结合chatglm3-6B 利用RAG 基于文档智能问答

简介 llamaindex结合chatglm3使用 import os import torch from llama_index.core import VectorStoreIndex, ServiceContext from llama_index.core.callbacks import CallbackManager from llama_index.core.llms.callbacks import llm_completion_callback from llama_ind…

Mini-Gemini: Mining the Potential of Multi-modality Vision Language Models

Mini-Gemini: Mining the Potential of Multi-modality Vision Language Models 相关链接&#xff1a;arxiv 关键字&#xff1a;Vision Language Models、Multi-modality、High-Resolution Visual Tokens、High-Quality Data、VLM-guided Generation 摘要 在这项工作中&#x…

软件工程学习笔记12——运行维护篇

运行维护篇 一、版本发布1、关于软件版本2、版本发布前&#xff0c;做好版本发布的规划3、规范好发布流程&#xff0c;保障发布质量 二、DevOps工程师1、什么是 DevOps 三、线上故障1、遇到线上故障&#xff0c;新手和高手的差距在哪里2、大厂都是怎么处理线上故障的 四、日志管…

探索 2024 年 Web 开发最佳前端框架

前端框架通过简化和结构化的网站开发过程改变了 Web 开发人员设计和实现用户界面的方法。随着 Web 应用程序变得越来越复杂&#xff0c;交互和动画功能越来越多&#xff0c;这是开发前端框架的初衷之一。 在网络的早期&#xff0c;网页相当简单。它们主要以静态 HTML 为特色&a…

插入排序、归并排序、堆排序和快速排序的稳定性分析

插入排序、归并排序、堆排序和快速排序的稳定性分析 一、插入排序的稳定性二、归并排序的稳定性三、堆排序的稳定性四、快速排序的稳定性总结在计算机科学中,排序是将一组数据按照特定顺序进行排列的过程。排序算法的效率和稳定性是评价其优劣的两个重要指标。稳定性指的是在排…

【NLP笔记】大模型prompt推理(提问)技巧

文章目录 prompt概述推理&#xff08;提问&#xff09;技巧基础prompt构造技巧进阶优化技巧prompt自动优化 参考链接&#xff1a; Pre-train, Prompt, and Predict: A Systematic Survey of Prompting Methods in Natural Language Processing预训练、提示和预测&#xff1a;NL…

植物大战僵尸Javascript版web游戏源码

源码介绍 植物大战僵尸Javascript版web游戏源码&#xff0c;非常强大&#xff0c;1比1还原电脑版植物大战僵尸游戏&#xff0c;带背景音乐&#xff0c;玩法和原版一模一样。 源码截图 下载地址 https://download.csdn.net/download/huayula/89048275

UE RPC 外网联机(2)

外网联机配置测试 一、网络配置 开放外网端口开放端口是为了可以进行外网访问;端口包含一个预案管理服务器端口和多个预案服务器端口;(预案管理服务器类似于大厅,预案服务器类似于房间,大厅管理多个房间;) (1)预案管理服务器端口;(如:23001) (2)预案服务器端口…

UDP send 出现大量“Resource temporarily unavailable”

背景 最近排查用户现场环境&#xff0c;查看日志出现大量的“send: Resource temporarily unavailable”错误&#xff0c;UDP设置NO_BLOCK模式&#xff0c;send又发生在进程上下文&#xff0c;并且还设置了SO_SNDBUF 为8M&#xff0c;在此情况下为什么还会出现发送队列满的情况…