数据结构:包装类和泛型

目录

一、包装类

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

2、装箱和拆箱

3、自动装箱和自动拆箱

二、泛型

1、什么是泛型

2、泛型语法

3、泛型类

4、擦除机制

5、泛型的上界

6、泛型方法

三、通配符

1、什么是通配符

2、通配符上界

3、通配符下界


📚一、包装类

包装类是对应着各种基本数据类型进行包装后产生的引用数据类型,我们甚至可以称它们为基本数据类型的plus版本。

至于我们为什么要设计包装类这是因为Java是一个面向对象的编程语言,但是Java中的八种基本数据类型却是不面向对象的,为了使用方便和解决这个不足,在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八种基本数据类型对应的类统称为包装类。

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

|-------------|---------------|
| 基本数据类型 | 包装类 |
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| char | Character |
| boolean | Boolean |

🐬 2、装箱和拆箱

java 复制代码
 int i = 10;
//装箱操作,新建⼀个Integer 类型对象,将i 的值放⼊对象的某个属性中
 Integer ii = Integer.valueOf(i);
 Integer ij = new Integer(i);
 //拆箱操作,将Integer 对象中的值取出,放到⼀个基本数据类型中
 int j = ii.intValue();
 

然而我们发现在使用过程中这种装箱和拆箱的过程,带了不少的代码量,于是为了减少开发时的负担,java提供了自动装箱和拆箱机制。

🐬3、自动装箱和自动拆箱

java 复制代码
public class  Test{
    public static void main(String[] args) {
        int i = 10;
        Integer ii = i;//⾃动装箱
        Integer ij = (Integer)i;//⾃动装箱
        
        int j = ii;//⾃动拆箱
        int k = (int)ii;//⾃动拆箱


    }
}

我们可以发现这种自动的装箱和拆箱方式大大的减少了我们的代码量和开发负担

现在我们已经大致了解了包装类了,接下来让我们来看一道题

java 复制代码
public class  Test{
    public static void main(String[] args) {
        Integer a = 127;
        Integer b = 127;
        Integer c = 128;
        Integer d = 128;
        System.out.println(a == b);
        System.out.println(c == d);

    }
}

他的输出结果是什么呢?可能我们下意识认为都是true,但我们看一下运行后给出的结果

我们发现并不都是true这是为什么呢?

首先我们先来了解一下"==","=="在比较基本数据类型的时候,就是字面值的比较,而现在我们并不是基本数据类型,而是由基本类型转变的包装类属于引用类型,而"==" 在比较引用类型的时候,比较的是地址,但是由于Integer是具有范围的-128~127,在这区间的值是被存放在常量池中,所以只有是处于这个区间的相同值进行比较都是相同的,但是128超出了这个范围,因此两个变量的值虽然都等于128,但并不是同一个地址,因此不为true。

📚二、泛型

🐬1、什么是泛型

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

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

我们来看这样一个例子,在数组中存放各种类型的元素

我们知道Object类默认为所有类的父类,所以我们创建的数组为Object类的数组。

创建完后我们发现的确任何的类型我们都能够进行存放了,但是当我们进行元素的取出时我们发现,编译器进行了报错,这时因为我们要取出的虽然本身是字符串,但是我们必须进行强制类型转换。

但是,更多情况下,我们还是希望他只能够持有一种数据类型。而不是同时持有这么多类型。所以,泛型的主要目的:就是指定当前的容器,要持有什么类型的对象,让编译器去做检查。

🐬2、泛型语法

基础写法:

java 复制代码
class 泛型类名称 < 类型形参列表 > {

          // 这里可以使用类型参数

}

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

}

其他写法:

java 复制代码
class 泛型类名称<类型形参列表> extends 继承类/* 这⾥可以使⽤类型参数 */ { 
      // 这⾥可以使⽤类型参数
}

class ClassName<T1, T2, ..., Tn> extends ParentClass<T1> {
       // 可以只使⽤部分类型参数
}
 

因此我们就可以将上述代码写成新的样子

java 复制代码
class MyArray1<T> {
    public Object[] array = new Object[10];
    //public T[] array = (T[]) new Object[10];两种写法都可以
    public T getPos(int pos) {
        return (T)this.array[pos];
    }
    public void setVal(int pos,T val) {
        this.array[pos] = val;
    }
}
public class TestDemo1 {
    public static void main(String[] args) {
        MyArray1<Integer> myArray1 = new MyArray1<>();
        myArray1.setVal(0,10);
        int ret1 = myArray1.getPos(0);
        System.out.println(ret1);


        MyArray1<String> myArray2 = new MyArray1<>();
        myArray2.setVal(0,"hello");
        String ret2 = myArray2.getPos(0);
        System.out.println(ret2);

    }
}

我们发现利用这种方法我们就可以吧不同类型的相同操作只利用一段代码并在主函数中传入不同的类型就可全部使用,这就是我们使用泛型的原因。

当然泛型还是有一些规范的

  1. 类名后的<T>代表占位符,表示当前类是一个泛型类
  2. 类型形参一般使用一个大写字母表示,常用的名称有:
  • E表示Element
  • K表示Key
  • V表示Value
  • N表示Number
  • T表示Type
  • S,U,V等等-第二、第三、第四个类型

🐬3、泛型类

java 复制代码
泛型类<类型实参> 变量名;        // 定义⼀个泛型类引⽤
 
new 泛型类<类型实参>(构造⽅法实参);        //实例化⼀个泛型类对象

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

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

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

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

🐬4、擦除机制

现在我们已经大致了解了泛型的用法了,但是大家有没有想过泛型是怎么在编译器上进行编译的呢,而这就涉及到了我们的擦除机制。

  1. 在编译时,Java编译器会将泛型类型信息从代码中移除,这个过程就叫做类型擦除。

  2. 擦除后,泛型类型会被替换为其边界类型(通常是Object)或者指定的类型。
    擦除过程

  3. 将泛型参数替换为其边界或Object。

  4. 在必要的地方插入类型转换以保持类型安全。

  5. 生成桥接方法以保持多态性。

🐬5、泛型的上界

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

java 复制代码
class 泛型类名称<类型形参 extends 类型边界> {
 ...
}
public class MyArray<E extends Number> {
 ...
}

而类型边界中所谓的上界,其实就是代表我们规定的边界作为父类,而我传入的变量必须是父类本身或者是父类的子类

例如Number的子类有Integer,没有String

java 复制代码
public class MyArray<E extends Number> {
 ...
}

MyArray<Integer> l1;//正常,因为Integer是Number的⼦类型

MyArray<String> l2;//编译错误,因为String不是Number的⼦类型

对于接口也是一样的

java 复制代码
 // E必须是实现了Comparable接⼝的
public class MyArray<E extends Comparable<E>> {
 ...
 }

注意:没有指定类型边界E,可以视为EextendsObject

🐬6、泛型方法

java 复制代码
⽅法限定符 <类型形参列表> 返回值类型 ⽅法名称(形参列表) { ... }

例子:交换元素

📚三、通配符

🐬1、什么是通配符

? 用于在泛型的使用,即为通配符

我们发现相同类型的引用是可以相互指向的,而不同类型的引用是不能相互指向的,那么我们可不可建立一个能够指向多种不同泛型参数的对象呢?这就需要用到我们的通配符了

我们发现当我们使用了通配符?之后就可以指向不同的泛型参数了 。

当然,有些时候我们同样希望它能够像泛型一样只指向像一部分的泛型参数,所以这时我们在通配符上也引用了上界,同时通配符多了一项对下界的限制

? extends 类:设置通配符上限

? super 类:设置通配符下限

🐬2、通配符上界

java 复制代码
 <? extends 上界> 
<? extends Number>//可以传⼊的实参类型Number或者Number的⼦类
 

跟泛型的上界一样,同样将通配符限制为界限的本身和他的子类

🐬3、通配符下界

java 复制代码
 <? super 下界> 
<? super Integer>//代表可以传⼊的实参的类型是Integer或者Integer的⽗类类型

而下界则是,通配符的范围只能界限的本身和他的父类


好了今天的分享就到这里,还请大家多多关注,我们下一篇见!

相关推荐
csdn5659738506 分钟前
Java 实现 Elasticsearch 查询当前索引全部数据
java·elasticsearch·jenkins
码力全開8 分钟前
C 语言奇幻之旅 - 第06篇:C 语言控制流语句
c语言·开发语言·数据库·windows·visualstudio·vim
一弓虽14 分钟前
java基础学习——IO流详细介绍
java·学习·io流
黑客老陈18 分钟前
JAVA XXE 学习总结
java·服务器·开发语言·python·学习·安全·web安全
风清云淡_A21 分钟前
【JAVA基础】Collections方法的具体使用方法
java·后端
华东设计之美30 分钟前
java集合类有哪些?
java·开发语言
heath ceTide32 分钟前
Java项目中集成Github登录
java·开发语言·github
sin220136 分钟前
springmvc--对日期类型如何处理
java·开发语言
java熊猫1 小时前
Ruby语言的正则表达式
开发语言·后端·golang