【数据结构】什么是泛型?为什么要使用泛型?泛型怎么用?那包装类呢?

目录

1. 为什么要有泛型

2. 认识泛型

3. 为什么要有包装类

4. 包装类

4.1 基本类型对应的包装类

4.2 装箱和拆箱

4.3 自动装箱和自动拆箱

4.4 经典面试题


1. 为什么要有泛型

我们先用数组实现一个简易版MyArrar,里面存放int类型的元素

public class MyArray {
    int[] array;
    int size;  //数组中有效元素的个数
    
    public MyArray(int initCapacity){
        if(initCapacity <= 0){
            initCapacity = 10;
        }
        array = new int[initCapacity];
    }
    
    //尾插
    public void add(int e){
        if(size >= array.length){
            System.out.println("MyArray已经存满了");
            return;
        }
        array[size] = e;
        size++;
    }
    
    //获取index位置上的元素
    public int get(int index){
        if(index<0 || index>=size){
            throw new ArrayIndexOutOfBoundsException("数组下标越界");
        }
        return array[index];
    }

    public static void main(String[] args) {
        MyArray myArray = new MyArray(5);
        myArray.add(1);
        myArray.add(2);
    }
}

但是我们不想在往MyArray中添加int类型的元素,会怎么办?

  • 再创建一个类?没有体现代码的复用性,太麻烦
  • 使用Object类型来接收数据?可以 ,我们先创建MyArray,在创建一个Student类和一个Person类,并且添加一些数据,看看使用的时候会暴漏哪些问题?
class Person{
    int age;
    String name;
}
class Student{
    String name;
    int age;
}
public class MyArray {
    Object[] array;
    int size;

    public MyArray(int initCapacity){
        if(initCapacity <= 0){
            initCapacity = 10;
        }
        array = new Object[initCapacity];
    }

    public void add(Object e){
        if(size >= array.length){
            System.out.println("MyArray已经存满了");
            return;
        }
        array[size] = e;
        size++;
    }

    public Object get(int index){
        if(index<0 || index>=size){
            throw new ArrayIndexOutOfBoundsException("数组下标越界");
        }
        return array[index];
    }
    
    public static void main(String[] args) {
        MyArray myArray = new MyArray(10);
        myArray.add(new Person());
        myArray.add(new Person());
        myArray.add(new Student());
        myArray.add(new Student());
    }
}

我想得到一个Person对象

发现会报错,思考一下为什么?

因为MyArray存放的是Object,Object是所有类的父类,所以这里存在向下转型,向下转型是不安全的,所以我们必须强转一下

这个时候这个问题就解决了

但是这种时候会出现一另个问题:

观察一下,发现明明是Person类型,为什么用Student类型接收,编译器不报错?

我们可以运行一下看看结果如何:

ClassCastException为类型转换异常,是一种运行时错误,所以在编译期不会报错,会完成编译,但是运行时就会报错

为什么会出现这种错误?

因为MyArray里面存的东西我们一般也不清楚是什么类型,所以在接收的时候,无法选择正确的类型来接收,所以会导致出现这种错误

所以对于上面问题,使用Object类型来接收可以实现,但不可取,因为会出现向下转型,和类型转换错误,导致我们写起来很麻烦

这个时候,泛型就出现了,使用泛型可以很好的解决以上问题

2. 认识泛型

泛型指的是类型参数化,即在写代码时将类型设置一个特定格式的参数,这个参数可以指定不同的类型来控制形参具体限制的类型

使用泛型对上述代码改造体会泛型的用法

public class MyArray<T> {
    T[] array;
    int size;

    public MyArray(int initCapacity){
        if(initCapacity <= 0){
            initCapacity = 10;
        }
        array = (T[])new Object[initCapacity];
    }

    public void add(T e){
        if(size >= array.length){
            System.out.println("MyArray已经存满了");
            return;
        }
        array[size] = e;
        size++;
    }

    public T get(int index){
        if(index<0 || index>=size){
            throw new ArrayIndexOutOfBoundsException("数组下标越界");
        }
        return array[index];
    }
}

这里注意一个问题:

在new对象的时候,型泛型必须替换为具体类,这里使用Object并且在强制转换一下

往里边插入Person和Student对象

        MyArray<Student> myArray1 = new MyArray<>(10);
        myArray1.add(new Student());
        MyArray<Person> myArray2 = new MyArray<>(10);
        myArray2.add(new Person());

当泛型参数指定成Student类型时,如果在往里边插入Person对象时会发生什么? 

发现编译器直接报错,说明类型一旦确定,就不能往里边在插入别的类型的对象了 

注意:

  • 泛型是作用在编译期间的一种机制
  • 泛型在代码运行期间,会进行类型擦除,底层实际用的是Object实现的,但是用户不用在代码中做强制类型准换的事了,让编译器在编译阶段检查

查看字节码:

3. 为什么要有包装类

我们如果想要往MyArray中插入int类型的数据呢?

发现将泛型参数替换为int会报错,原因只有引用类型能替换泛型参数,而int为基本类型,那这时候怎么办呢?

基本类型对应的包装类就很好的解决了这个问题 

4. 包装类

4.1 基本类型对应的包装类

基本数据类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

我们只记Integer和Character,因为其它都是基本类型首字母大写

4.2 装箱和拆箱

  • 装箱:就是将基本类型转化为对应的包装类型
        int i = 10;
        Integer in1 = Integer.valueOf(i);
        Integer in2 = new Integer(i);
  • 拆箱:就是将包装类型转化为对应的基本类型 
        Integer in3 = new Integer(10);
        int a = in3.intValue();

4.3 自动装箱和自动拆箱

从上面装箱和拆箱操作中可以看出,装箱和拆箱给我们增加了繁琐的代码量,所以为了减少开发的负担,Java提供了自动装箱和自动拆箱机制

        int n = 10;
        
        Integer in6 = n; //自动装箱
        
        int m = in6; //自动拆箱

4.4 经典面试题

看看下面代码会输出什么?

public class test2 {
    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);
    }
}

这是为什么呢?

看一下Integer底层源码:

 

发现Integer底层维护了一个数组,这个数组值的范围为[-128,127],如果Integet对象的值在这个范围内,直接从cache数组中拿,类似于字符串常量池,就是Integer类型的引用直接指向数组对应值的地址,如果Integer对象的值超过这个范围,会创建新的对象

简单总结[-128,127],Integer直接比较值,超出这个范围会创建新对象

再看看上面的题:

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

X_H学Java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值