【java数据结构】泛型

devtools/2024/10/3 22:25:14/

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)机制。

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

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

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

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

示例:

java">        Integer a=100;Integer b=100;System.out.println(a == b);//输出结果:trueInteger 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 引出泛型

java">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 泛型类
java">class 泛型类名称<类型形参列表>{
...
}

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

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

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

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

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

java">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类型的数据,编译器就会报错!
在这里插入图片描述

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

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

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

java">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 泛型接口

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

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

示例:

java">// 定义一个泛型接口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 只能在该方法中使用。当然,泛型方法中也可以使用泛型类中定义的泛型参数。

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

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

java"> class Test2<U> {// 该方法只是使用了泛型类定义的类型参数,不是泛型方法public void testMethod(U u){System.out.println(u);}// <T> 真正声明了下面的方法是一个泛型方法public <T> T testMethod1(T t){return t;}
}

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

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

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

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

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

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

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

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

示例:

java">public class GenericMethods {// 这是一个泛型方法,它接受一个泛型参数Tpublic 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 和 CharacterInteger[] 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类型。

java">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);}

预期的运行结果:

java">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),因为编译器不知道具体的类型是什么。

java">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 的子类。

语法:

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

示例:

java">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 类型的父类型,适用于只写或读写但写入元素类型固定的场景。

java">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);//编译成功}

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

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

http://www.ppmy.cn/devtools/119137.html

相关文章

DnsDiag:一款针对DNS的故障排除和安全审计工具

关于DnsDiag DnsDiag是一款针对DNS的故障排除和安全审计工具&#xff0c;在该工具的帮助下&#xff0c;广大研究人员可以轻松检测DNS基础设施的安全性。 你是否曾怀疑过你的 ISP 是否劫持了你的 DNS 流量&#xff1f;你是否曾观察到 DNS 响应有任何异常行为&#xff1f;你是否…

Linux操作系统如何定时关机?

在日常使用电脑的过程中&#xff0c;一般都会有软件升级、系统杀毒的工作&#xff0c;可能还需要电脑的定时关机、提醒事项功能。对于Linux操作系统&#xff0c;可以使用几种任务计划工具来指定相应的任务计划&#xff0c;使这些需求自动在后台运行。 一、at命令 at命令的作用…

【Linux】基于驱动框架的程序编写测试

【Linux】基于驱动框架的程序编写测试 字符设备驱动工作原理☆ 驱动程序开发驱动程序开发步骤驱动代码框架驱动框架设计流程 编译与测试编译测试 参考博文&#xff1a; 【Linux】基于框架编写驱动代码、驱动代码编译和测试 Linux驱动&#xff08;驱动程序开发、驱动框架代码编…

Spire.PDF for .NET【页面设置】演示:对PDF 文件进行分页

PDF 分页不仅允许用户在 PDF 文件中添加页码&#xff0c;还可以将 PDF 文档分成几部分&#xff0c;以便添加一些附加信息&#xff0c;例如 PDF 文件封面、简介或参考资料。因此&#xff0c;PDF 分页为管理大型文件或组织书籍提供了极大的便利。本节将向您展示一种通过C#、VB.NE…

Excel 获取某列不为空的值【INDEX函数 | SMALL函数或 LARGE函数 | ROW函数 | ISBLANK 函数】

〇、需求 Excel 获取某列不为空的值(获取某列中第一个非空值 或 获取某列中最后一个非空值)。 一、知识点讲解 INDEX函数 和 SMALL函数 两个函数搭配使用都可以实现上述需求 获取某列中第一个非空值 。 INDEX函数 和 LARGE函数 两个函数搭配使用都可以实现上述需求 获取某…

【CKA】二、节点管理-设置节点不可用

2、节点管理-设置节点不可用 1. 考题内容&#xff1a; 2. 答题思路&#xff1a; 先设置节点不可用&#xff0c;然后驱逐节点上的pod 这道题就两条命令&#xff0c;直接背熟就行。 也可以查看帮助 kubectl cordon -h kubectl drain -h 参数详情&#xff1a; –delete-empty…

关于音频噪音处理【常见问题、解决方案等】

1、降噪 【1】动物叫声处理 问题描述 原视频的人声与动物叫声重合&#xff0c;想清除动物的声音。 解决方案 [1] 声音模型噪音消除 1、将音频文件导入Adobe Audition 2、打开频谱图&#xff0c;找到相应噪音的特征&#xff0c;用画笔工具选择所有噪音区域 3、使用“学习声音模…

【Golang】Go语言中type关键字到底是什么?

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…