【JavaSE】泛型的那些事(泛型考古、泛型擦除、包装类)

在这里插入图片描述
希望通过博客和大家相互交流,相互学习,如有错误,请评论区指正

一. 什么是泛型

泛型(generic type)其本质是将类型参数化,也就是说所操作的数据类型被指定为一个参数这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

二. 为什么要有泛型 ?

之前写过MyArrayList顺序表,这个类当时自己在实现的时候只能用一种类型来表示,也就是用的时候自己实现的MyArrayList只能应用于一种类型,要想应用于其他类型,就得再写一个其他类型的MyArrayList,这样就比较麻烦,而有了泛型之后就很好的解决了这个问题,让类型参数化,使得自己写的MyArrayList可以适用于多种场景

通过以下代码对比就可发现泛型的方便之处

示例

不带泛型

public class MyArrayList {
    
    
    private int[] array;
    private int size;    // 有效数据个数
    public MyArrayList() {
    
    
        this.array = new int[10];
        this.size = 0;
    }
    public void add(int x) {
    
        // 暂不考虑扩容
        this.array[size] = x;
        this.size++;
    }
}

带泛型

import org.omg.CORBA.Object;

public class MyArrayList2<E> {
    
    
    // 在类的实现中,可以直接将类当成一种数据类型来使用。在实例化该类的时候这个类型才被确定
    private E[] array;
    private int size;   // 有效数据个数
    public MyArrayList2() {
    
    
        this.array = (E[])new Object[10];   // 注意:Java中泛型不允许定义数组
        this.size = 0;
    }
    public void add(E e) {
    
        // 不考虑扩容
        this.array[size] = e;
        this.size++;
    }
}
// 带泛型的顺序表元素类型是一个“变量”
// E就是变量的名称

通过上面这两段代码的对比,就可以发现,带泛型的顺序表和不带泛型的顺序表只是表示类型的部分不一样,代码所实现的逻辑是一样的

类的实例化与使用

public static void main(String[] args) {
    
    
    MyArrayList myArrayList = new MyArrayList();
    myArrayList.add(1);
    myArrayList.add(2);
																	// 这里其实就相当于是将String类型赋值给E
    MyArrayList2<String> stringMyArrayList2 = new MyArrayList2<String>();    // 等号右边的<>内可以省略
    stringMyArrayList2.add("1");                                           // 要想用基本类型,要写基本类型对应的包装类
    stringMyArrayList2.add("2");
}

对于泛型来说,类型是在使用该类时才明确定义出来的

三、泛型考古

要知道其实刚开始Java体系中是没有泛型的,是在JDK1.5之后才才有了泛型

在JDK1.5之前是利用Object引用可以指向任意类型的对象实现类似泛型的效果

  1. Object类是所有类的祖先类
  2. 上层引用可以指向下层对象

如下代码:

public class MyArrayList3 {
    
    
    private Object[] array;
    private int size;
    public MyArrayList3() {
    
    
        this.array = new Object[10];
        this.size = 0;
    }
    public void add(Object e) {
    
    
        this.array[size] = e;
        size++;
    }
}

这种做法的缺点也很明显,就是new了一个MyArrayList,里面打算存int类型的数据,但由于手滑,存入了一个String类型的数据,这样的话编译器是不会报错的,程序也能正常运行,直到需要取list中的元素的时候程序才会抛出异常。

泛型就很好的解决了这个问题,只要类型不匹配,错误会在第一时间暴露出来

四、泛型擦除

虽然在 JDK1.5 之后有了泛型,但其实JDK的底层实现还是用的上面Object来实现的

如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sB60DwZz-1646265098596)(C:\Users\LY\AppData\Roaming\Typora\typora-user-images\image-20211210185132787.png)]
我们虽然用的是泛型,但其实泛型只存在于编译阶段,在编译过程中也伴随着泛型擦除,在生成.class文件的时候泛型信息就已经不存在了,成了JDK1.5之前的Object方式

查看MyArrayList的字节码文件就可以看出来
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KCvl7iYr-1646265098598)(C:\Users\LY\AppData\Roaming\Typora\typora-user-images\image-20220123202400988.png)]
所以Java的泛型只存在于编译阶段(Java核心原理:Object引用可以指向任意类型的对象)

五、包装类

由于8种基本类型不是类型,Java专门为它们定义了各自的包装类

如下:

byte          java.lang.Byte
short         java.lang.Short
char          java.lang.Character
int           java.lang.Integer
long          java.lang.Long
float         java.lang.Float
double        java.lang.Double
boolean       java.lang.Boolean

六、装箱、拆箱

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dsPrxxd0-1646265098599)(C:\Users\LY\AppData\Roaming\Typora\typora-user-images\image-20220212173816723.png)]
Java中提供了便利,如果没有歧义,Java 会帮我们自动装箱,自动拆箱

Integer a = 10;   // 把int赋值给Integer类型,隐含着发生了装箱过程
int b = a;      // 把Integer赋值给int类型,隐含着发生了拆箱过程

隐式装箱和拆箱过程只存在于编译阶段,编译完成就变成显式装箱,显式拆箱了

猜你喜欢

转载自blog.csdn.net/weixin_46531416/article/details/123244588