数据结构--泛型与包装类

一丶关于泛型

所谓泛型,主要就是为了解决类型锁死的问题,什么意思?
就是说在一个类中,如果我们设置了这个类成员变量的类型,这个时候如果想要接收其他类型,那么就不行,也就是说类型转换,转换不了的情况。
关于这一点我们用一个案例来进行解释,因为ArrayList可以接收任意类型参数,所以:

 public static void main(String[] args) {
        //使用ArrayList来接收任意类型的参数
        ArrayList arrayList = new ArrayList();
        arrayList.add("hello");
        arrayList.add("world");
        arrayList.add(10086);
        //全部转化为字符串来进行查看
        String s = null;
        for(int i = 0;i < arrayList.size();i++){
            s  = (String)arrayList.get(i);
        }
        System.out.println(s.length());
    }

这里运行下来的话,会报出一个异常:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

虽然说这个问题在运行阶段被检测了出来,但是如果说是编译阶段就可以直接检测出来不好吗?

那么为了解决这个问题我们怎么办呢?

那么我们在初始设置这个变量的时候,我们就让它的类型不确定,然后使用它的时候再定义。那个时候我们用的是什么类型他就是什么类型。如果这个时候类型异常,就是类型转换异常,这个时候编译器会直接报错。

是不是感觉有点绕?
那么我们用一个例题来进行具体解释。

泛型例题

public class Generic {
    private int[] array;
    int size;

    //构造器直接初始化数组
    public Generic(int size) {
        array = new int[size];
        size = 0;
    }

    //添加元素
    public void add(int data){
        //先检测是否有位置进行添加
        if(size == array.length){
            System.out.println("位置已满");
        }
        //如果没满就进行尾插
        array[size++] = data;
    }

    //获取元素
    public int get(int data){
        if(data > size){
            System.out.println("下标越界了");
        }

        return array[data];
    }
    //获取数组长度
    public int size(){
        return size;
    }

    public static void main(String[] args) {
        Generic gen = new Generic(10);
        gen.add(1);
        gen.add(2);
        gen.add(3);
        gen.add(4);
        System.out.println(gen.get(3));
        System.out.println(gen.size());
    }
}

很明显这个最后运行结果是:

4
4

那么这里问题来了,如果说我想要给这个数组中储存其他类型我要怎么办呢?
没办法,我在刚开始定义的时候,我就定义的是int类型,后面也只能使用int类型来进行填入。
那么如果要重新获取其他类型就要重写了,比如:

public class MyTest {
    Persion[] array;
    int size;

    //构造器直接初始化数组
    public MyTest(int size) {
        array = new Persion[size];
        size = 0;
    }

    //添加元素
    public void add(Object data){
        //先检测是否有位置进行添加
        if(size == array.length){
            System.out.println("位置已满");
        }
        //如果没满就进行尾插
        array[size++] = (Persion) data;
    }

    //获取元素
    public Persion get(int data){
        if(data > size){
            System.out.println("下标越界了");
        }

        return array[data];
    }
    //获取数组长度
    public int size(){
        return size;
    }

    public static void main(String[] args) {
        MyTest myA
        那可能rray = new MyTest(10);
        myArray.add(new Persion("HanMeiMei", "女", 12));
        myArray.add(new Persion("LiLeiLei", "男", 13));
        myArray.add(new Persion("LiLeiLei", "男", 13));
        myArray.add(new Persion("LiLeiLei", "男", 13));
        System.out.println(myArray.size());
    }

}

可以发现这里特别麻烦,因为每一次的引入都要重写一个类然后再使用。
那可能会有小伙伴说我们直接写一个类,然后类型参数设置为Object就可以了。
这样真的可以吗?我们来试试:

关于Object类型

还是直接给代码:

public class MyArray {
    private Object[] array;
    private int size;
    
    public MyArray(int initCap){
        array = new Object[initCap];
        size = 0;
    }
    
    public void add(Object p){
        if(size == array.length){
            System.out.println("元素已经存满了,无法再添加元素");
        }
        array[size++] = p;
    }

    public Object get(int index){
        if(index >= size){
            throw new IndexOutOfBoundsException("数组下标越界");
        }
        return array[index];
    }
    public int size(){
        return size;
    }
    public static void main(String[] args) {
        MyArray myArray = new MyArray(10);
        myArray1.add(new Person("HanMeiMei", "女", 12));
        myArray1.add(new Person("LiLeiLei", "男", 13));
        myArray1.add(new Person("LiLeiLei", "男", 13));
        myArray1.add(new Person("LiLeiLei", "男", 13));
        System.out.println(myArray1.size());
        MyArray myArray2 = new MyArray(10);
        myArray2.add(new Animal("小七", "金黄色"));
        myArray2.add(new Animal("元宝", "灰黑色"));
        System.out.println(myArray2.size());
    }
}
//上述代码和前面差不多一样,就是参数是Object类就是了,然后下面补上两种引用类型

class Person {
    String name;
    String gender;
    int age;

    public Person(String name, String gender, int age) {
        this.name = name;
        this.gender = gender;
        this.age = age;
    }
}

class Animal {
    String name;
    String color;

    public Animal(String name, String color) {
        this.name = name;
        this.color = color;
    }
}

运行结果如下:
在这里插入图片描述
好像是没有什么太大的问题,毕竟Object是可以接收一切的类型。
但是真的没有问题嘛?

问题一:转型

作为Object类,我们可以用他来接收一切类型,那么就意味着我们可以向上转型和向下转型,可是向下转型是不安全的,很容易出错。
我们修改原来的main方法:

 public static void main(String[] args) {
        MyArray myArray1 = new MyArray(10);
        myArray1.add(new Person("HanMeiMei", "女", 12));
        myArray1.add(new Person("LiLeiLei", "男", 13));
        myArray1.add(new Person("LiMing", "男", 13));
        myArray1.add(new Person("Wangtiezhu", "男", 13));
        myArray1.add(new Animal("小七", "金黄色"));
        myArray1.add(new Animal("元宝", "灰黑色"));
        System.out.println(myArray1.size());

        Person p1 = (Person) myArray1.get(1);
        System.out.println(p1.name);
        Animal a1 = (Animal)myArray1.get(4);
        System.out.println(a1.name);
    }

然后运行此代码

在这里插入图片描述
可以看出代码是没有什么问题的,但是如果说Object数组里面元素特别多,你这个时候 就 很 难 分 辨 出 来 到 底 是 那 个 子 类 对 象 的 引 用 指 向 了 父 类 对 象 \color{red}{就很难分辨出来到底是那个子类对象的引用指向了父类对象}
所以这个时候,就很容易出现错误。

问题二:关于基础数据类型

这里其实就是一个问题,Object可以接收int等基础数据类型嘛?
答案是:

不可以

关于这一点的解答,我们接下来继续往后看。

关于泛型的使用

那么为了解决上述的问题一,我们这里就需要使用泛型,就是说我们在定义的时候就先不要给它定义类型。然后用的时候再说,具体如下:

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

    //建造构造器
    public MyArray(int size){
        array = (E[]) new Object[size];
        size = 0;
    }

    //构造添加方法
    public void add(E e){
        if(size == array.length){
            throw new ArrayIndexOutOfBoundsException("没地方添加啦!");
        }
        array[size++] = e;
    }

    //得到该数组下标
    public E get(int index){
        if(index > size){
            throw new ArrayIndexOutOfBoundsException("数组下标越界啦!");
        }
        return array[index];
    }

    public int size(){
        return size;
    }
}

class Person {
    String name;
    String gender;
    int age;

    public Person(String name, String gender, int age) {
        this.name = name;
        this.gender = gender;
        this.age = age;
    }
}

class Animal {
    String name;
    String color;

    public Animal(String name, String color) {
        this.name = name;
        this.color = color;
    }
}

测试泛型

我们接下来进行测试,也就是准备添加main方法。

在这里插入图片描述
这里的话因为使用array1时候定义的是Person类型,所以说添加Animal类型就不行。这里注意了:

1. 泛型是作用在编译期间的一种机制,即运行期间没有泛型的概念
2. 泛型代码在运行期间,会将类型擦除掉,底层实际使用的也是Object实现的,
但是用户不需要在代码中做强制类型转换的事情了,让编译器在编译阶段检查。

二丶关于包装类

这里的包装类就是为了解决上述问题二,就是关于接收int类型问题。
int不是一个类,所以不能使用Object来进行接收,这个时候我们提供了一个类叫做包装类
包装类也就是单纯为了接收基础数据类型而生的。具体如下:
在这里插入图片描述
除了int类型和char类型其他类型包装都是原形。

具体使用还是用咋们测试泛型的代码:
在这里插入图片描述
关于包装类这里需要说的其实就是两点。也就是上面代码的隐藏问题,array.add添加的真的是int嘛?

自动装箱

这里其实指的就是基础数据类型到对应的包装类

 public static void main(String[] args) {
 		//看一下这三种创建方式有区别嘛?
        Integer a = Integer.valueOf(1);
        Integer b = 1;
        Integer c = (Integer) 1;
        System.out.println(a == b && b== c );
    }

其实是没有的,直接看结果。
在这里插入图片描述

自动拆箱

自动拆箱就是包装类到对应基础数据类型的过程
看一下下面的三种拆箱方法:

public static void main(String[] args) {
        Integer a = Integer.valueOf(1);
        Integer b = 1;
        Integer c = (Integer) 1;
        System.out.println(a == b && b== c );
        
		//看这里!!!!看这里!!!看下面代码!!!
        int i = a.intValue();
        int j = a;
        int k = (int)a;
        System.out.println(i == j && j == k);
    }
}

在这里插入图片描述

关于面试题

看这段代码:

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;
false;

为什么呢?
这里其实就是一个基础数据类型到对应包装类的自动装箱的过程,在这个过程内他会使用IntegerCache 来缓存一定范围的值,IntegerCache 默认情况下范围为:-128~127。
本题中的 127 命中了 IntegerCache,所以 c 和 d 是相同对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值