Java学习【深入探索包装类和泛型】

发布于:2024-06-30 ⋅ 阅读:(18) ⋅ 点赞:(0)

在这里插入图片描述
在Java的学习中,包装类和泛型是两个重要的概念,它们不仅丰富了Java的数据类型,还提高了代码的可读性和安全性。下面,我们将深入探讨这两个主题。

🚀包装类

包装类是Java提供的一种特殊类,它们将Java的基本数据类型(如int、double、char等)封装成对象。这样做的好处是可以将基本数据类型作为对象来处理,使用对象所特有的属性和方法。

Java提供了8种基本数据类型的包装类:
Integer → int
Double → double
Byte → byte
Short → short
Long → long
Float → float
Character → char
Boolean → boolean

获取包装类对象的方式

使用valueOf()创建

以Integer为例,直接通过调用valueOf方法,把值传入到方法中

Integer i1 = Integer.valueOf(127);

下面看一个特殊的例子:

        Integer i1 = Integer.valueOf(127);
        Integer i2 = Integer.valueOf(127);
        System.out.println(i1 == i2); // true

        Integer i3 = Integer.valueOf(128);
        Integer i4 = Integer.valueOf(128);
        System.out.println(i3 == i4); // false

对于引用数据类型,因为" == " 是比较地址值的,i3 , i4不是同一个对象可以理解,但是为什么 i1 和 i2 是一样的呢?
在实际开发中,-128~127之间的数字用的比较多,为了节省内存,Java提前创建好了这些对象,用的时候直接调取,不会再创建新的对象

直接赋值

自动装箱和自动拆箱
当使用包装类的时候,该怎么去进行计算呢,首先要把对象转化为基本数据类型,再进行计算,再转化为引用数据类型,这样手动的去转化就非常麻烦,所以在JDK5的时候就出现了自动装箱和自动拆箱的机制
自动装箱:把基本数据类型变为对应的包装类
自动拆箱:把包装类转化为对应的基本数据类型

       //自动拆箱
        Integer i = 1;
        //自动装箱
        int j = i;
        System.out.println(i);
        System.out.println(j);
        System.out.println(i + j);

Integer成员方法

在这里插入图片描述
返回值为String类型

        //进制转换
        System.out.println(Integer.toBinaryString(10));//1010
        System.out.println(Integer.toOctalString(10));//12
        System.out.println(Integer.toHexString(10));//a

类型转换:

Integer i5 = Integer.parseInt("123");
        System.out.println(i5 + 1);

        Scanner sc = new Scanner(System.in);
        String s = sc.nextLine();//键盘录入一行,遇到空格不会停止
        System.out.println(s);
        System.out.println(Integer.parseInt(s) + 1);

除了 Character,都有对应的转换方法

🚀泛型

在Java编程中,泛型是一个强大的工具,它允许我们在编写代码时定义灵活的、可重用的结构,而无需关心具体的数据类型。

引出泛型

问题:实现一个类,类中包含一个数组成员,使得数组中可以存放任意类型的数据,也可以根据成员方法访问返回数组中下标的值
如果是任意类型的话,可以考虑Object,因为它是所有类型的父类,接着试着实现一下这个问题

class MyArray{
    private Object[] arr = new Object[10];

    public void set(int index,Object value){
        arr[index] = value;
    }
    public Object get(int index){
        return arr[index];
    }
}

public class Demo1 {
    public static void main(String[] args) {
        MyArray myArray = new MyArray();
        myArray.set(0,"AA");
        myArray.set(1,1);
        String s = (String) myArray.get(0);//因为返回的是Object类型,所以需要强制类型转换
        int i = (int)myArray.get(1);
        System.out.println(s+" "+i);
    }
}

上面的代码就实现了这个要求,但是此时发现一个弊端,如果数据过多的话就显得特别杂乱,各种类型都有,都需要强制类型转换
泛型的主要目的就是:指定当前容器要持有什么类型的对象,接着让编译器去检查类型,此时就是把类型作为参数传递,需要什么类型就传入什么类型
格式: <数据类型>
注意: 泛型只能支持引用数据类型

泛型的擦除:
类型擦除是Java编译器在编译泛型代码时的一个步骤。在编译过程中,编译器会将泛型信息从代码中擦除,并在需要的地方插入类型转换和类型检查代码。这样,运行时的字节码不包含任何泛型类型信息,只包含原始类型和必要的类型转换。

ArrayList<Integer> arrayList = new ArrayList<>();

例如在创建集合时,当数据真正的添加到集合里边的时候,集合还是会把它们当作Object类处理,只不过往外获取时会进行相应的强制类型转换

泛型类

当一个类中,某个变量的类型不确定,就可以定义带有泛型的类

格式:
修饰符 class 类名 <类型>{
}

class MyArray <E>{
    private Object[] arr = new Object[10];

    public void set(int index,E value){
        arr[index] = value;
    }
    public E get(int index){
        return (E) arr[index];//
    }
}

public class Demo1 {
    public static void main(String[] args) {
        MyArray<Integer> myArray = new MyArray<>(); //已经确定好类型了
        //myArray.set(0,"Str");  自动类型检查,要与上面的类型一致
        myArray.set(0,1);
        int i = myArray.get(0);
        System.out.println(i);

        //创建String类型
        MyArray<String> myArray2 = new MyArray<>();
        myArray2.set(0,"hello");
        String str = myArray2.get(0);
        System.out.println(str);
    }
}

上面的MyArray就是一个泛型类,通过使用泛型,就对传入的数据类型进行了约束,同时,也实现了可以传入不同的类型参数

泛型方法

当一个类中只有一个方法中要用到不确定的类型,就只需要把这个方法定义为泛型方法即可

格式:
修饰符 <类型> 返回值类型 方法名(类型 变量名){
}

class ListUtil{
    public void show(){
        System.out.println("其他方法···");
    }
    public static <E> void addAll(ArrayList<E> list,E e1,E e2,E e3){
        list.add(e1);
        list.add(e2);
        list.add(e3);
    }

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        addAll(list,"a","b","c");
        System.out.println(list);
        
        ArrayList<Integer> list2 = new ArrayList<>();
        addAll(list2,1,2,3);
        System.out.println(list2);
    }
}

泛型接口

格式:
修饰符 interface 接口名<类型>{
}

例如Java中的List接口就是一个泛型接口:
在这里插入图片描述
泛型接口的使用方法:
1.实现类给出具体类型
在这里插入图片描述
在这里插入图片描述
2.实现类延续泛型,创建对象时再确定类型
在这里插入图片描述

泛型的继承和通配符

泛型不具备继承性,但数据具备继承性
什么意思呢
在这里插入图片描述
首先定义了两个具有继承关系的类,method方法里边所限定的类型是Fu 类型,它的子类型所创建的对象并不能使用该方法,如果想要子类型也能使用,就需要把方法定义为泛型方法,但是如果是其他类型也可以使用,怎么去限定只有这种具有继承关系的类才能使用
这时就可以使用通配符来实现

通配符:?
也表示不确定类型,但是可以限定类型,
? extend E: 表示可以传递E 或 E所有的子类类型
? super E: 表示可以传递E 或 E 所有的父类类型

在这里插入图片描述

泛型的上界

上面介绍的通过extend进行类型限制就是指定了泛型的上界,下面还有一种复杂的示例
在这里插入图片描述
例如:
写一个泛型类,定义一个方法,可以求数组的最大值

在这里插入图片描述
这时候就需要用到compareTo方法,就要实现comparable接口

public class Alg<E extends Comparable<E>> { //对泛型进行了限制,必须实现Comparable接口的类型才能传入
    public E findMax(E[] array){
        E max = array[0];
        for(int i = 0;i < array.length;i++){
            if(max.compareTo(array[i]) < 0){
                max = array[i];
            }
        }
        return max;
    }

    public static void main(String[] args) {
        Integer[] arr = {1,2,3,4,5};
        Alg<Integer> alg = new Alg<>();
        int a = alg.findMax(arr);
        System.out.println(a); //5
    }
}

比如再定义一个Person类,因为它没有实现Comparable接口,所以泛型里边不能传入Student
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到