包装类&认识泛型

简介: 包装类&认识泛型

一、包装类

定义:在Java中,由于基本类型不是继承自Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了一个包装类型。

1.1 基本数据类型和对应的包装类

1.2 装箱和拆箱

        //装箱:将一个基本数据类型转换为引用数据类型
        int a=10;
        Integer value=a;//自动装箱
        Integer value1=Integer.valueOf(20);//显示装箱
        Integer value2=new Integer(20);//显示装箱
        System.out.println(value+" "+value1+" "+value2);
 
        //拆箱:将Integer对象中的值取出,放到一个基本数据类型中
        int num=value1;//自动拆箱
        int num1=value1.intValue();

在上述使用过程中,装箱和拆箱带来不少的代码量,所以为了减少开发者负担,Java提供了自动机制。

面试题

    public static void main(String[] args) {
        Integer a=127;
        Integer b=127;
        System.out.println(a==b);
        Integer c=128;
        Integer d=128;
        System.out.println(c==d);
        Integer e=Integer.valueOf(2);
    }
 
//结果:
true
false

上述结果输出不同是因为在装箱过程中返回的引用类型不一样:

二、泛型

泛型的主要目的:就是指定当前的容器,要持有什么类型的对象。让编译器去做检查。此时,就需要把类型,作为参数传递。需要什么类型,就传入什么类型。

2.1 语法

class 泛型类名称<类型形参列表>{
  //这里可以使用类型参数
}
 
class ClassName<T1,T2,T3...,Tn>{
}
 
class 泛型类名称<类型形参列表>extends 继承类/*这里可以使用类型参数*/{
  //这里可以使用类型参数
}
 
class ClassName<T1,T2,T3,...Tn>extends ParentClass<T1>{
  //这里可以使用部分类型参数
}

代码实现:

class MyArray<T>{
    //T[]kk=new T[10];//语法错误,泛型当中不能实例化泛型类型的数组
    public T[]array=(T[])new Object[10];
    public T getPos(int pos){
        return this.array[pos];
    }
    public void setArray(int pos,T val){
        this.array[pos]=val;
    }
}
public class Test {
    public static void main(String[] args) {
        MyArray<Integer>myArray=new MyArray<>();//1 <Integer>指定当前类型
        myArray.setArray(0,1);
        //myArray.setArray(1,"akai");//此时因为在注释1处指定类当前类型,此时编译器会在存放元素时帮助我们进行类型检查
        System.out.println(myArray.getPos(0));
    }
}

代码解释:

1.类名后的<T>代表占位符,表示当前类是一个泛型类

了解:【规范】类型形参一般使用一个大写字母,常用的名称有:

·E表示Element

·K表示Key

·V表示Value

·N表示Number

·T表示Type

·S,U,V 等等-第二、第三、第四个类型

三、泛型类的使用

3.1 语法

泛型类<类型实参>变量名;//定义一个泛型类引用

new 泛型类<类型实参>(构造方法实参);//实例化一个泛型类对象

3.2 示例

MyArray<Integer>list=new MyArray<Integer>();

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

3.3 类型推导

当编译器可以根据上下文推导出类型实参时,可以省略类型室参的填写

MyArray<Integer>list=new MyArray<>();//可以推导出实例化需要的类型实参为Integer

3.4 裸类型

裸类型是一个泛型类但没有类型实参,例如:

MyArray list=new MyArray();

注:裸类型是为了兼容老版本的API保留的机制下面的类型擦除部分

小结:

1.泛型是将数据类型参数化,进行传递

2.使用<T>表示当前类是一个泛型类。

3.泛型类目前位置的优点:数据类型参数化,编译时自动进行类型检查和转换

四、泛型如何编译的

4.1 擦除机制

在编译过程当中,将所有的T替换为Obeject这种机制,我们成为:擦除机制。

Java的泛型机制是在编译级别实现的。编译器生成的字节码文件但在运行期间并不包含泛型的类型信息。


4.2 为何不能实例化泛型类型数组

class MyArray<T>{
    public T[]array=(T[])new Object[10];
 
    public T getPos (int pos){
        return this.array[pos];
    }
    public void setVal(int pos,T val){
        this.array[pos]=val;
    }
    public T[]getArray(){
        return array;
    }
}
 
public class Test {
    public static void main(String[] args) {
        MyArray<Integer>myArray1=new MyArray<>();
        Integer[]Strings =myArray1.getArray();
    }
}
 
/*Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
        at Test.main(Test.java:27)*/

原因:替换后的方法为(由于运行时没有泛型机制,因此擦除机制在编译时期将T转换为Object):将Object[]分配给Intefer[]引用,程序报错。

public Object[]getArray{
  return array;
}

通俗讲就是:返回的Object数组里面,可能存放的是任何的数据类型,可能是String,可能是Person,运行的时候,直接转给Integer类型的数组,编译器认为是不安全的。

正确的使用方法:【了解即可】

class MyArray<T> {
    public T[] array;
    public MyArray() {
    } 
    //涉及到映射
    public MyArray(Class<T> clazz, int capacity) {
        array = (T[]) Array.newInstance(clazz, capacity);
    }
    public T getPos(int pos) {
        return this.array[pos];
    }
    public void setVal(int pos,T val) {
        this.array[pos] = val;
    }
    public T[] getArray() {
        return array;
    }
}
public class Test {
    public static void main(String[] args) {
        MyArray<Integer> myArray1 = new MyArray<>(Integer.class,10);
        Integer[] integers = myArray1.getArray();
    }
}

泛型的常用方法:

public  Objiect[] obj=new Object [3];
 
public E getPos(int pos){
    return (E)obj[pos];
}

五、泛型的上界

5.1 语法

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

5.2 示例

1. public class MyArray<E extends Number>{
2.   ...
3. }

此时只接受Number的子类作为E的类型实参


MyArray<Integer>a1;//正常,因为Integer 是Number的子类型


MyArray<String>a2;//编译错误,因为String 不是Number的子类型


注: 没有指定类型边界的E,会默认擦除为Object类型。

5.3 复杂示例

//class Alg<E> 1
//E必须是实现了Com[arable方法的
class Alg<E extends Comparable<E>>{
    public E findMax(E []array){
        E max=array[0];
        for (int i = 1; i < array.length; i++) {
            //if(max<array[i])
            //if(max.compareT0(array[i])<0) //此时 E 类型为object类型,object中没有compare方法  2
            if(max.compareTo(array[i])<0)
            max=array[i];
        }
        return max;
    }
}
 
public class Test {
    public static void main(String[] args) {
        Alg<Integer> alg=new Alg<>();
        Integer []array={1,3,5,6,2};
        Integer val=alg.findMax(array);
        System.out.println(val);
    }
}

六、泛型方法

6.1 定义语法

1. 方法限定符<类型形参列表>返回类型 方法名称 (形参列表){
2.   ...
3. }

6.2 示例

public class Test {
    //返回类型可以为任意类型,E也可以
    public static <E> void swap(E[]array,int i,int j){
     E t=array[i];
     array[i]=array[j];
     array[j]=t;
    }
}

代码示例:

public class Test{
    //返回类型可以为任意类型,E也可以
    public static <E> void swap(E[]array,int i,int j){
     E t=array[i];
     array[i]=array[j];
     array[j]=t;
    }
 
    public static void main(String[] args) {
        Integer []a={1,3,5,6,2};
        Integer []b={2,3,2,1,5,6};
        //可以a的类型推导E,因此<>中可以省略
        Test.swap(a,2,3);
        Test.<Integer>swap(b,2,1);
        System.out.println(a[3]);
        System.out.println(b[1]);
    }
 
}

七、 通配符

用于在泛型使用,即为通配符

7.1  通配符用于解决什么问题

class Message<T>{
    private T message;
    public T getMessage(){
        return message;
    }
    public void setMessage(T message){
        this.message=message;
    }
}
 
public class Common {
    public static void main(String[] args) {
        Message<String> message=new Message<>();
        message.setMessage("zhonghuizhaodaoni");
        func(message);
    }
    //如果此时泛型的类型设置的不是String,而是Integer,就会出现错误,而此时使用通配符 ? 就会很好的解决这个问题
    public static void func(Message<String> temp){
        System.out.println(temp.getMessage());
    }
}

在"?"的基础上产生了两个字通配符:

<? extends 上界>

<? extends Number> //可以传入的实参类型是Number或者Number的子类

7.2 通配符的上界

语法:

<? extends 上界>

<? extends Number> //可以传入的实参类型是Number或者Number的子类

class Food {
}
class Fruit extends Food {
}
class Apple extends Fruit {
}
class Banana extends Fruit {
 
}
class Plate<T>{
    private T message;
    public T getMessage(){
        return message;
    }
    public void setMessage(T message){
        this.message=message;
    }
}
public class Common {
    public static void main(String[] args) {
        
        Plate<Apple> plate=new Plate<>();
        plate.setMessage(new Apple());
        func(plate);
        
        Plate<Banana> plate1=new Plate<>();
        plate1.setMessage(new Banana());
        func(plate1);
    }
    //此时使用通配符"?"描述的是它可以接收任意类型,但是由于不确定类型,多以无法修改
    public static void func(Plate<? extends Fruit> temp){
        //temp.setMessage(new Banana());//仍然无法修改
        //temp.setMessage(new Apple());//仍然无法修改
        System.out.println(temp.getMessage());
    }
}

此时无法确定fun函数中对temp进行设置元素,因为temp接收的是Fruit和它的子类,此时存储的元素应该是哪个子类无法确定。所以添加会报错!但是可以获取元素。

    public static void func(Plate<? extends Fruit> temp){
        //temp.setMessage(new Banana());//仍然无法修改
        //temp.setMessage(new Apple());//仍然无法修改
        //System.out.println(temp.getMessage());
        Fruit b=temp.getMessage();
        System.out.println(b);
    }

通配符的上界,不能进行写入数据,只能进行读取数据

8.3 通配符下界

语法:

<? super 下界>

<? super Integer> //代表可以传入的实参的类型是Integer或者Integer的父类类型

class Food {
}
class Fruit extends Food {
}
class Apple extends Fruit {
}
class Banana extends Fruit {
 
}
class Plate<T>{
    private T message;
    public T getMessage(){
        return message;
    }
    public void setMessage(T message){
        this.message=message;
    }
}
public class Common {
    public static void main(String[] args) {
 
        Plate<Fruit> plate=new Plate<>();
        plate.setMessage(new Fruit());
        func(plate);
 
        Plate<Apple> plate1=new Plate<>();
        plate1.setMessage(new Apple());
        //func(plate1);//添加的必须是Fruit或者其父类
    }
    //此时使用通配符"?"描述的是它可以接收任意类型,但是由于不确定类型,多以无法修改
    public static void func(Plate<? super Fruit> temp){
        //此时可以修改!添加的是Fruit或者Fruit的子类,所以可以向上转型
        temp.setMessage(new Banana());
        temp.setMessage(new Fruit());
        //temp.setMessage(new Food());//添加失败,因为若是Fruit的父类,则需要向下转型,向下转型是不安全的
        //Fruit fruit=  temp.getMessage();//不能接收,这里无法确定是哪个父类,需要向下转型为Fruit,此时也是不安全的
        System.out.println(temp.getMessage());//只能直接输出
    }
}

通配符的下界,不能进行读取数据,只能写入数据

相关文章
|
9月前
|
Java
包装类的使用
包装类的使用
38 0
|
1月前
|
Java 编译器 语音技术
包装类&简单认识泛型
包装类&简单认识泛型
|
1月前
什么是泛型,泛型的具体使用?
什么是泛型,泛型的具体使用?
14 0
|
10月前
|
缓存
包装类
包装类
43 0
|
10月前
|
存储 Java
包装类和基本数据类型
包装类和基本数据类型
48 0
|
1月前
|
Java
什么是泛型, 泛型的具体使用
什么是泛型, 泛型的具体使用
16 0
|
1月前
|
存储 Java
什么是泛型, 泛型的具体使用?
什么是泛型, 泛型的具体使用?
|
10月前
|
存储 安全 Java
泛型的相关知识
泛型的相关知识
75 0
|
10月前
|
缓存 Java
基本数据类型包装类
基本数据类型包装类
47 0
|
11月前
|
Oracle Java 关系型数据库
八种基本数据类型的大小,以及他们的封装类
八种基本数据类型的大小,以及他们的封装类
53 0