Bootstrap

Java---包装类与泛型

1.包装类

1.1 包装类

在Java中,由于基本数据类型不是继承Object类,为了在泛型代码中可以支持基本数据类型,Java给每个基本数据类型各自提供了·一个包装类。

如下图

除了char和int基本数据类型的包装类型有点特别,其他的都是首字母大写 

1.2 装箱与拆箱

1. 装箱

装箱就是将基本数据类型的数据转化成包装类,装箱分为自动装箱和显示拆箱。

public static void main(String[] args) {
        int a=10;
        Integer b=Integer.valueOf(a);//显示拆箱
        Integer c=a;//自动拆箱
    }

其实自动拆箱和显示拆箱的底层原理是一样的,都是调用了Integer.valueOf()方法。

2. 拆箱

拆箱就是将包装类的数据类型转换转换成基本数据类型,拆箱也分为自动拆箱和显示拆箱。

public static void main1(String[] args) {
        Integer a=10;
        int b=a.intValue();//显示拆箱
        int c=a;//自动拆箱
    }

 2.面试题

了解装箱与拆箱,我们来看一道面试题

public static void main(String[] args) {
        Integer a=100;
        Integer b=100;
        System.out.println(a == b);//打印true
        Integer c=200;
        Integer d=200;
        System.out.println(c == d);//打印false

    }

为什么会打印不同的结果呢?

我们来看Integer.ValueOf()方法的原码

我们发现,在进行装包操作的时候,会根据装包的数据的大小来返回不同类型的数据。

当要装包的数据的范围在 [-127~128] 之间时,valueOf 方法就会返回数组中的一个数据(整数)。

如下图

当数据不在上面的范围时,就会返回一个新的实例化的对象。 

 所以,由于100在【128~127】这个范围内,所以两者的比较是两个整数之间的比较。

 由于200超出了以上范围,所以c和d的比较实际上是两个对象之间的比较,有==来比较两个对象,返回值当然是false。

2. 泛型

2.1 泛型的概念

一般类和方法,只能使用具体的数据类型,要么是基本数据类型或者是引用数据类型。当我们要编写设计多种数据类型的程序时,一旦我们将数据类型固定,那么对于后续的编程的限制会很大。所以在Java中提出了泛型的概念,所谓泛型,就是将数据类型参数化。

2.2 引出泛型

我们先来看一道题:实现一个类,该类中有一个可以存储任何数据类型的数组,并且可以通过方法来设置数组中的值和获取数组中对应的内容。

代码如下图

在类中,我们创建了一个Object类型的数组,这样就可以存储任何数据类型了。但是,后面我们发现,我们可以通过方法来直接设置数组中的内容,但是我们通过方法来获取数据类型的时候,却发现会报错,这是因为发生了向下转型,我们需要进行强制类型转换才能正确获得数组中的任何数据。这样想想就很奇怪了,我明明可以存储任何数据类型,但却不能直接1获取数组中的数据。为了解决这个问题,我们就可以使用泛型。

如以下代码

class DataBase<E>{
    Object[] array=new Object[10];
    public void setArray(int pos,E obj){
        array[pos]=obj;
    }
    public E getArray(int pos){
        return (E)array[pos];
    }
}
public class Test {
    public static void main(String[] args) {
        DataBase<Integer> dataBase=new DataBase<>();
        dataBase.setArray(0,10);
        Integer a=dataBase.getArray(0);
        DataBase<String> dataBase1=new DataBase<String>();
        dataBase1.setArray(1,"man");
        String str=dataBase1.getArray(1);
    }
}

< E >就是泛型的用法 ,可以发现,当我们使用泛型之后,我们就可以直接来获取数据了,不用进行强制转换了。

简单来说,使用泛型,就行我们在创建类的时候,通过泛型,我们可以将数据类型转换为参数来进行类的创建。如上图,我们在实例化database对象的时候,我们将Integer的数据类型作为参数传过去,所以此时,对于字母E就代表Integer数据类型,实例化database1的时候,我们将String数据类型作为参数传过去,此时,对于database1来说,字母E就代表String类型。

注意事项:<E>可以理解为一个标识符,代表该类为泛型类。

2.3 泛型的语法

class 类名<T>{

}

类名后的<T>是一个标识符,表示当前类为泛型类。 其中< >里面也可以是其他字母,常见的有T和E。

3.泛型类的使用

3.1 语法格式

泛型类名<类型实参> 变量名=new 泛型类名<类型实参>();

举例

 

class MyFunc<T>{

}
public class Test {
    public static void main(String[] args) {
        MyFunc<Integer> myFunc=new MyFunc<Integer>();
        MyFunc<String> myFunc1=new MyFunc<>();
    }
    
}

在创建泛型类对象时,后面的new< >里面的包装类可以不写,编译器会根据前面的包装类来推导后面的包装类类型。

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

 4. 裸类型(了解)

裸类型是一种不带参数的泛型类型,它是为了兼容以前老版本JDK没有泛型的版本。例如MyArrayList就是一个裸类型。

5. 擦除机制

泛型是如何进行编译的呢?

泛型是编译时期的机制,代码在运行的时候没有泛型的概念。

这就涉及到擦处机制:在编译完成后,所有的传给泛型类的数据类型,最终都会被擦除为Object类。所以,编译之后的字节码无泛型,只有Object类。

6. 泛型的上界

在定义泛型类时,有时需要对传入的类型变量进行限制,这时候可以通过类型边界来限制。

6.1 语法

class 泛型类名称<类型形参 extends 类型边界> {
    ...
}

举例

 这时定义的泛型类时传入的类型参数的上界为Number,传如的类型参数必须是Number的子类,由于String类不是Number的子类,所以会报错。

注意事项:没有泛型边界的,默认边界为Object。

6.2 复杂例子

题目要求:创建一个类,里面有一个方法来获取数组中的最大值。

我们会很直接的写下以下代码

当我们写出上图的代码之后,我们发现会报错。这是因为泛型类的E代表很多数据类型,我们不能用平常的数学思维去比较。这时候我们可以对传入的类型参数实现comparable接口来解决问题。

class Alg<E extends Comparable<E>>{
    public E FindMax(E[] array){
        E max=array[0];
        for(int i=1;i<array.length;i++){
            if(array[i].compareTo(max)>0){
                max=array[i];
            }
        }
        return max;
    }
}
public class Test {
    public static void main(String[] args) {
        Integer[] array={1,2,3,4,5};
        Alg alg=new Alg();
        int ret=alg.FindMax(array);
    }
}

这时,由于我们在定义泛型类的时候使用了Comparable接口,所以,此时,传入的类型参数必须实现Comparable接口,否则会报错。

 7. 泛型方法

7.1 语法格式

方法修饰限定符 <类型参数> 返回值类型 方法名(){

}

举例

public static<E extends Comparable<E>> E FindMax(E[] array)

 以上题目,获取最大值的另一种代码形式

class Alg{
    public static<E extends Comparable<E>> E FindMax(E[] array){
        E max=array[0];
        for(int i=1;i<array.length;i++){
            if(array[i].compareTo(max)>0){
                max=array[i];
            }
        }
        return max;
    }
}
public class Test {
    public static void main(String[] args) {
        Integer[] array={1,2,3,4,5};
        Alg alg=new Alg();
        Integer ret=Alg.FindMax(array);//使用类型推导
        Integer ret2=Alg.<Integer>FindMax(array);//不使用类型推导
    }
}

感谢观看。

 

;