【java数据结构】泛型

news2024/9/27 18:55:25

【java数据结构】泛型

  • 一、包装类
      • 1.1 基本数据类型对应的包装类
      • 1.2 装箱和拆箱
  • 二、泛型
      • 2.1 引出泛型
      • 2.2 什么是泛型
      • 2.3 语法
          • 2.3.1 泛型类
          • 2.3.2 泛型接口
          • 2.3.3 泛型方法
      • 2.4 擦除机制
      • 2.5 泛型通配符
          • 2.5.1 <?>无限定的通配符
          • 2.5.2 <? extends T>上界的通配符
          • 2.5.3 <? super T>下界的通配符

此篇博客希望对你有所帮助(帮助里了解包装类,以及自动拆装箱,更重要的了解java的泛型),不懂的或有错误的也可在评论区留言,错误必改评论必回!!!

一、包装类

在Java中,包装类(Wrapper Classes)是一种特殊的类,它们将基本数据类型封装成对象。这样做的目的主要是为了让基本数据类型拥有对象的特性。

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

在这里插入图片描述

1.2 装箱和拆箱

装箱:是基本数据类型自动转换为包装类对象;拆箱:是包装类对象自动转换为基本数据类型。
从Java 5(Java 1.5)开始,引入了自动装箱(Autoboxing)和拆箱(Unboxing)机制。

         int i=100;
         Integer a=Integer.valueOf(i);//手动装箱
         Integer b=100;//自动装箱
        
        int c=a.intValue();//手动拆箱
        int d=b;//自动拆箱

拆箱和装箱还提供额外的功能:

// 将字符串转换为整数  
int number = Integer.parseInt("123");  
  
// 将整数转换为字符串  
String strNumber = String.valueOf(123);  
  
// 也可以利用包装类的valueOf方法  
String anotherStrNumber = Integer.valueOf(123).toString();

注意事项:缓存:Java的某些包装类(如Integer、Boolean等)对特定范围内的值进行了缓存,这意味着在特定范围内,使用valueOf方法得到的可能是缓存中的对象,而不是新创建的对象。这种机制提高了性能,但也需要注意它可能带来的副作用。

示例:

        Integer a=100;
        Integer b=100;
        System.out.println(a == b);//输出结果:true

        Integer c=200;
        Integer d=200;
        System.out.println(c == d);//输出结果:false

解释:由于100在-128到127的缓存范围内,Java虚拟机(JVM)会直接从缓存中返回相同的Integer对象给a和b。因此,a和b实际上引用的是内存中的同一个对象;200它不在-128到127的缓存范围内。因此,每次创建Integer对象时(通过自动装箱),JVM都会创建一个新的Integer实例。所以,c和d虽然值相同,但它们分别引用了不同的对象。

二、泛型

在java继承那学习到,Object为所有类的父类,那么数组是否可以创建成Object类?如果能创建成功,是不是这个数组里面能放任何类型的数据?
答案:数组可以创建成Object类,并且里面可以存放任何类型的数据。

2.1 引出泛型

class Test{
    public Object[] array=new Object[10];

    public Object getPos(int pos){
        return array[pos];
    }
    public void setValues(int pos,Object value){
        array[pos]=value;
    }
}

public class Main {
    public static void main(String[] args) {
        Test test=new Test();
        test.setValues(1,10);
        test.setValues(2, "hello");

        String str= (String) test.getPos(2);
        int a= (int)test.getPos(1);
        System.out.println(a);
        System.out.println(str);
    }

因为数组是Object类型的,当我们要用到这些数据的时候,还需要我们进行强转。
弊端:当我们创建Object数组,要用到数组中的数据时,我们还得事先知道数组这个下表的数据类型是什么类型,才可以使用。

2.2 什么是泛型

概念:泛型本质即“参数化类型”,也就是说所操作的数据类型被指定为一个参数,在使用时再从外部传入一个数据类型;而一旦传入了具体的数据类型后,传入变量(实参)的数据类型如果不匹配,编译器就会直接报错。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法。
主要目的:泛型就是指定当前的容器,要持有什么类型的对象,让编译器去做检查。

2.3 语法

2.3.1 泛型类
class 泛型类名称<类型形参列表>{
...
}

尖括号 <> 中的 泛型标识被称作是类型参数,用于指代任何数据类型。
泛型标识是任意设置的(如果你想可以设置为 Hello都行),Java 常见的泛型标识以及其代表含义如下:

T :代表一般的任何类。
E :代表 Element 元素的意思,或者 Exception 异常的意思。
K :代表 Key的意思。
V :代表 Value 的意思,通常与 K 一起配合使用。
S :代表 Subtype 的意思,文章后面部分会讲解示意。

在泛型类中,类型参数定义的位置有三处,分别为:

1.非静态的成员属性类型
2.非静态方法的形参类型(包括非静态成员方法和构造器)
3.非静态的成员方法的返回值类型

在创建泛型类的对象时,必须指定类型参数 T 的具体数据类型,即尖括号 <> 中传入的什么数据类型,T 便会被替换成对应的类型。如果 <> 中什么都不传入,则默认是 < Object >。

class Test<T> {
    public Object[] array = new Object[10];

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

    public void setValues(int pos, T value) {
        array[pos] = value;
    }
}

public class Main {
    public static void main(String[] args) {
        Test<Integer> test = new Test<Integer>();
        test.setValues(1, 10);
        test.setValues(2, "hello");

        String str = (String) test.getPos(2);
        int a = (int) test.getPos(1);
        System.out.println(a);
        System.out.println(str);
    }
}

这里就发现当我们传入Integer数据类型后,再插入String类型的数据,编译器就会报错!
在这里插入图片描述

Test<Integer> test = new Test<>();

这里可以不用写后面<>中的Integer,这个会根据前面<>中写的包装类自动补充这里的类型!
注意:

泛型类不只接受一个类型参数,它还可以接受多个类型参数!

class Test<T,E> {
    public Object[] array = new Object[10];

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

    public void setVal(int pos, E value1) {
        array[pos] = value1;
    }
    public void setValues(int pos, T value) {
        array[pos] = value;
    }
}
2.3.2 泛型接口

它允许你在定义接口时指定一个或多个类型参数(也称为类型变量)。这样,当实现接口时,你就可以指定这些类型参数的具体类型了。泛型接口提高了代码的复用性、类型安全性和可读性。

public interface 接口名<类型参数> {
    ...
}

示例:

// 定义一个泛型接口Pair,它有两个类型参数T和U  
public interface Pair<T, U> {  
    // 定义一个方法,该方法返回第一个元素  
    T getFirst();  
      
    // 定义一个方法,该方法返回第二个元素  
    U getSecond();  
}  
  
// 实现Pair接口的一个类,具体指定了类型参数为String和Integer  
public class StringIntPair implements Pair<String, Integer> {  
    private String first;  
    private Integer second;  
  
    public StringIntPair(String first, Integer second) {  
        this.first = first;  
        this.second = second;  
    }  
  
    @Override  
    public String getFirst() {  
        return first;  
    }  
  
    @Override  
    public Integer getSecond() {  
        return second;  
    }  
}  
  
// 测试类  
public class Test1 {
    public static void main(String[] args) {
          StringIntPair  StringIntPair = new StringIntPair("Hello", 123);
//        Pair<String, Integer> StringIntPair = new StringIntPair("Hello", 123);
        System.out.println(StringIntPair.getFirst() + ", " + StringIntPair.getSecond());
    }
}

运行结果:在这里插入图片描述

2.3.3 泛型方法

当在一个方法签名(方法签名主要由方法名称和参数列表(包括参数的类型和顺序)组成,但不包括方法的返回类型、访问修饰符和抛出的异常)中的返回值前面声明了一个 < T > 时,该方法就被声明为一个泛型方法。< T >表明该方法声明了一个类型参数 T,并且这个类型参数 T 只能在该方法中使用。当然,泛型方法中也可以使用泛型类中定义的泛型参数。

public <类型参数> 返回类型 方法名(类型参数 变量名) {
    ...
}

(1)只有在方法签名中声明了< T >的方法才是泛型方法,仅使用了泛型类定义的类型参数的方法并不是泛型方法。

 class Test2<U> {
    // 该方法只是使用了泛型类定义的类型参数,不是泛型方法
    public void testMethod(U u){
        System.out.println(u);
    }

    // <T> 真正声明了下面的方法是一个泛型方法
    public <T> T testMethod1(T t){
        return t;
    }
}

(2)泛型方法中可以同时声明多个类型参数。

public class TestMethod<U> {
	public <T, S> T testMethod(T t, S s) {
		return null;
	}
}

(3)泛型方法中也可以使用泛型类中定义的泛型参数。

public class TestMethod<U> {
	public <T> U testMethod(T t, U u) {
		return u;
	}
}

(4)特别注意的是:泛型类中定义的类型参数和泛型方法中定义的类型参数是相互独立的,它们一点关系都没有。泛型方法始终以自己声明的类型参数为准。

public class Test<T> {
	public void testMethod(T t) {
		System.out.println(t);
	}
	
	public <T> T testMethod1(T t) {
		return t;
	}
}

前面在泛型类的定义中提到,在静态成员中不能使用泛型类定义的类型参数,但我们可以将静态成员方法定义为一个泛型方法。

public class Test2<T> {   
	// 泛型类定义的类型参数 T 不能在静态方法中使用
	// 但可以将静态方法声明为泛型方法,方法中便可以使用其声明的类型参数了
    public static <E> E show(E one) {     
        return null;    
    }    
}  

示例:

public class GenericMethods {
    // 这是一个泛型方法,它接受一个泛型参数T
    public static <T> void printArray(T[] inputArray) {
        // 显示数组元素
        for (T element : inputArray) {
            System.out.printf("%s ", element);
        }
        System.out.println();
    }

    public static void main(String args[]) {
        // 创建不同类型数组:Integer, Double 和 Character
        Integer[] intArray = {1, 2, 3, 4, 5};
        Double[] doubleArray = {1.1, 2.2, 3.3, 4.4};
        Character[] charArray = {'H', 'E', 'L', 'L', 'O'};

        System.out.println("整型数组元素为:");
        printArray(intArray);   // 传递一个整型数组

        System.out.println("\n双精度型数组元素为:");
        printArray(doubleArray); // 传递一个双精度型数组

        System.out.println("\n字符型数组元素为:");
        printArray(charArray);   // 传递一个字符型数组
    }
}

运行结果:
在这里插入图片描述

2.4 擦除机制

Java 5之前是没有泛型概念的,为了保持与旧版本的兼容性,Java引入了泛型擦除机制。

编译时期,通过<>中的T进行类型检查和类型转换;编译完成以后T被擦除为Object类型。

class Test<T> {
    public Object[] array = new Object[10];

    public T getPos(int pos) {
        return (T) array[pos];
    }
    public void setValues(int pos, T value) {
        array[pos] = value;
    }
}

public class Main {
    public static void main(String[] args) {
        Test<Integer> test1=new Test<>();
        Test<String> test2=new Test<>();
        System.out.println(test1);
        System.out.println(test2);
    }

预期的运行结果:

System.out.println(test1);//预期运行结果:Test<Integer>......
 System.out.println(test2);//预期运行结果:Test<String>......

实际运行结果:
在这里插入图片描述

2.5 泛型通配符

在编码中, 处理未知类型或需要限制类型范围时。因此引出了泛型通配符这个概念。

泛型通配符有 3 种形式:

  1. <?> :被称作无限定的通配符。
  2. <? extends T> :被称作有上界的通配符。
  3. <? super T> :被称作有下界的通配符。
2.5.1 <?>无限定的通配符

如字面意思一样,没有任何限制,当你想表达“任何类型都可以”时,可以使用无界通配符。但是,它限制了你可以对该类型进行的操作,因为你不能往这个集合中添加元素(除了 null),因为编译器不知道具体的类型是什么。

List<?> list = new ArrayList<String>();  
// list.add(new Object()); // 编译错误  
list.add(null); // 允许 

注意:Object 本身也算是一种数据类型,但却不能代表任何一种数据类型,所以 ArrayList< Object > 和 ArrayList<?> 的含义是不同的,前者类型是 Object,也就是继承树的最高父类,而后者的类型完全是未知的;ArrayList<?> 是 ArrayList< Object > 逻辑上的父类。

2.5.2 <? extends T>上界的通配符

上界通配符 <? extends T>:T 代表了类型参数的上界,<? extends T>表示类型参数的范围是 T 和 T 的子类。

语法:

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

示例:

class Test1<T extends Number> {
    public Object[] array = new Object[10];

    public T getPos(int pos) {
        return (T) array[pos];
    }
    public void setValues(int pos, T value) {
        array[pos] = value;
    }
}
public class Test {
    public static void main(String[] args) {
        Test1<Integer> test1=new Test1<>();
        //Test1<String> test2=new Test1<String>();//类型参数java.lang.String不在类型变量T的范围内
        test1.setValues(1,10);//编译成功
    }
}

原因:T继承的是Number,Number就是这个集合的上界,Integer是Number的子类,String不是Number的子类。

总结:可以读,不能写因为有上界,所以你读取的时候,里面的数据不会超过你数据类型的上界;但是在写入时,你不直达他具体的数据类型,所以不能写。

2.5.3 <? super T>下界的通配符

表示可以是 T 类型或 T 类型的父类型,适用于只写或读写但写入元素类型固定的场景。

class Test1<Number> {
    public Object[] array = new Object[10];

    public Number getPos(int pos) {
        return (Number) array[pos];
    }
    public void setValues(int pos, Number value) {
        array[pos] = value;
    }
}
public class Test {
    public static void main(String[] args) {
        Test1<? super Number> test1=new Test1<>();
        test1.setValues(1,10.2);//编译成功
        test1.setValues(2,1);//编译成功
    }

总结:可以写,不能读因为他有下界,所以可以写入下界数据类型的数据,但是读的时候你不知道他具体是什么类型。

       List<? super Integer> list = new ArrayList<>();
// 编译失败,因为无法确定是否是Integer,有可能是Number
        Integer i = list.get(0);
// 编译失败,因为无法确定是否是Number,有可能是Object
        Number m = list.get(1);
// 编译正确,Object可以接受任意值
        Object o = list.get(2);

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

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

相关文章

【算法篇】二叉树类(2)(笔记)

目录 一、Leetcode 题目 1. 左叶子之和 &#xff08;1&#xff09;迭代法 &#xff08;2&#xff09;递归法 2. 找树左下角的值 &#xff08;1&#xff09;广度优先算法 &#xff08;2&#xff09;递归法 3. 路径总和 &#xff08;1&#xff09;递归法 &#xff08;2…

移动端自适应/适配方案【详解】(含多种方案对比,推荐 viewport 方案,postcss-px-to-viewport-8-plugin 的使用等)

为什么移动端需要自适应/适配 &#xff1f; 因移动端 屏幕尺寸不同屏幕分辨率不同横竖屏 移动端自适应/适配方案 【必要】设置 meta 标签 <meta name"viewport" content"widthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalable0">…

N930X音乐芯片,声光报警器语音方案:“您已进入警戒区域”

随着科技的飞速发展&#xff0c;城市规模不断扩大&#xff0c;人口密集度显著增加&#xff0c;各类安全隐患也随之而来。从商业楼宇到居民小区&#xff0c;从工业园区到交通枢纽&#xff0c;每一个角落都需要高效、可靠的安防系统来守护人们的生命财产安全。 声光报警器&#…

【ADC】SAR 型 ADC 和 ΔΣ ADC 的噪声源以及输入信号驱动和电压基准驱动电路

本文学习于TI 高精度实验室课程&#xff0c;简要介绍 SAR 型 ADC 和 ΔΣ ADC 的输入信号驱动和电压基准驱动电路&#xff0c;并介绍 SAR 和 Delta-Sigma 转换器的内在和外在噪声源。 文章目录 一、ADC 的外部噪声1.1 50/60 Hz 工频干扰1.2 混叠与抗混叠滤波器1.3 射频&#xf…

博主回归!数据结构篇启动

目录 1>>闲话 2>>数据结构前言 3>>复杂度的概念 4>>时间复杂度 5>>大O渐进表示法 6>>总结 1>>闲话 家人们好久不见&#xff0c;小编军训终于是结束了&#xff0c;大一事情太多了&#xff0c;这几天没时间健身&#xff0c;没时间…

WT2605C蓝牙语音芯片智能对话模型 人机互动 让机械设备更智能

随着人工智能技术的飞速发展&#xff0c;AI语音芯片在机械设备领域的应用日益广泛。WT2605C作为一款集成了在线TTS&#xff08;Text-To-Speech&#xff0c;文本到语音&#xff09;功能的蓝牙语音芯片&#xff0c;凭借其卓越的性能和广泛的应用前景&#xff0c;为机械设备产品带…

Apache Log4j2 远程代码执行漏洞(CVE-2021-44228)

漏洞描述&#xff1a; 当用户输入信息时&#xff0c;应用程序中的log4j 2组件会将信息记录到日志中 假如日志中包含有语句${jndi:ldap:attacker:1099/exp}&#xff0c;log4j就会去解析该信息&#xff0c;通过jndi的lookup() 方法去解析该url&#xff1a;ldap:attacker:1099/e…

vue-实现rtmp直播流

1、安装vue-video-player与videojs-flash npm install vue-video-player -S npm install videojs-flash --save 2、在main.js中引入 3、组件中使用 这样就能实现rtmp直播流在浏览器中播放&#xff0c;但有以下几点切记&#xff0c;不要入坑 1.安装vue-video-player插件一定…

Java.反射

目录 1.获取class 的三种方式 2.利用反射获取构造方法 3.利用反射获取成员变量 4.利用反射获取成员方法 1.获取class 的三种方式 全类名怎么找? 全类名报名&#xff0b;类名 package MyReflect;public class Student {private String id;private String name;private int…

LeetCode Hot100 C++ 哈希 1.两数之和

LeetCode Hot100 C 1.两数之和 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案&#xff0c;并且你不能使用两次相同的元素。 你可以按…

HTML5实现唐朝服饰网站模板源码

文章目录 1.设计来源1.1 网站首页-界面效果1.2 唐装演变-界面效果1.3 唐装配色-界面效果1.4 唐装花纹-界面效果1.5 唐装文化-界面效果 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作者&#xff1a;xcL…

【DP解密多重背包问题】:优化策略与实现

文章目录 什么是多重背包问题&#xff1f;多重背包问题的数学模型 例题多重背包问题Ⅰ多重背包问题Ⅱ 总结 什么是多重背包问题&#xff1f; 多重背包问题是一个经典的组合优化问题。与标准背包问题不同&#xff0c;在多重背包问题中&#xff0c;每种物品可以选择多个&#xf…

数据链路层之以太网

目录 ​前言 什么是以太网&#xff1f; 以太网帧格式 6位源地址和目的地址 什么是MAC地址&#xff1f; MAC地址和IP地址的区别 2位类型 ARP协议 ARP协议的作用 ARP协议的工作流程 数据长度 MTU对IP协议的影响 CRC校验和 前言 在前面&#xff0c;我们已经讲了在TC…

安卓好软-----手机屏幕自动点击工具 无需root权限

工具可以设置后自动点击屏幕。可以用于一些操作。例如自动刷视频等等哦 工具介绍 一款可以帮你实现自动操作的软件。软件中你可以根据实际需要设置点击位置&#xff0c;可以是屏幕上的特定位置&#xff0c;也可以是按钮或控件。功能非常强大&#xff0c;但是操作非常简单&…

7个不为人知的实用软件推荐

今天再给大家分享7款不常被提及但又很好用的小众宝藏软件&#xff0c;强大实用&#xff0c;值得被更多的人看见&#xff01; 1.向日葵——电脑远程控制 下载链接&#xff1a;https://sunlogin.oray.com/ 对于很多电脑小白来说&#xff0c;其实很多软件安装、电脑调试之类的操…

Leetcode面试经典150题-383.赎金信

给你两个字符串&#xff1a;ransomNote 和 magazine &#xff0c;判断 ransomNote 能不能由 magazine 里面的字符构成。 如果可以&#xff0c;返回 true &#xff1b;否则返回 false 。 magazine 中的每个字符只能在 ransomNote 中使用一次。 示例 1&#xff1a; 输入&#…

联宇集团:如何利用CRM实现客户管理精细化与业务流程高效协同

在全球化的浪潮中&#xff0c;跨境电商正成为国际贸易的新引擎。作为领先的跨境电商物流综合服务商&#xff0c;广东联宇物流有限公司(以下称“联宇集团”)以其卓越的物流服务和前瞻的数字化战略&#xff0c;在全球市场中脱颖而出。本文将基于联宇集团搭建CRM系统的实际案例&am…

链表以及字符串数据求和及乘积问题

目录 ​编辑 <->本篇简介&#xff1a; <二>题目解析解答&#xff1a; 21大数乘法问题&#xff1a; ①题目&#xff1a; ②思路汇总&#xff1a; ③解答代码&#xff1a; 22 大数加法问题&#xff1a; ①题目&#xff1a; ②思路汇总&#xff1a; ③解答…

【数据结构中的哈希】

泛黄的春联还残留在墙上.......................................................................................................... 文章目录 前言 一、【哈希结构的介绍】 1.1【哈希结构的概念】 1.2【哈希冲突】 1.3【哈希函数的设计】 1.4【应对哈希冲突的办法】 一、…

PostgreSQL 一张表多个字段关联另一张表

event_catalog 表 event 表 sql SELECT event.event_uuid, event.event_case_id, event.event_status, event.event_catalog_1, event.event_catalog_2, event.event_catalog_3, event.event_title, event.event_content, event.event_source, event.event_purpose, event.eve…