包装类&认识泛型

目录

一、包装类

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

 1.2 装箱和拆箱

 二、泛型

2.1 语法

三、泛型类的使用 

3.1 语法

 3.2 示例

3.3 类型推导

3.4 裸类型

四、泛型如何编译的

4.1 擦除机制

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

五、泛型的上界

5.1 语法

5.2 示例

5.3 复杂示例

六、泛型方法

6.1 定义语法

 6.2 示例


一、包装类

定义:在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 示例

public class MyArray<E extends Number>{
  ...
}

此时只接受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 定义语法

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

 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());//只能直接输出
    }
}

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
String的包装类java.lang.String类本身。在Java中,String类是一个特殊的引用类型,用于表示字符串。它是不可变的,即一旦创建就不能被修改。String类提供了许多用于操作字符串的方法,比如拼接、截取、替换等。由于String类是非线程安全的,所以在多线程环境下使用时需要注意线程同步的问题。\[1\] 与其他基本数据类型的包装类不同,String类没有对应的包装类,因为它本身就是一个引用类型。所以,我们不需要使用包装类来表示String类型的对象。\[2\] 与包装类中的equals方法一样,String类的equals方法也是重写了Object类中的equals方法,用于比较字符串的内容而不是地址。而"=="运算符比较的是引用变量的地址。当包装类型和与之相对应的基本类型进行"=="比较时,会先进行自动拆箱处理。\[3\] #### 引用[.reference_title] - *1* *2* [2022-07-22 第八小组 常竞文 String类型和八大包装类](https://blog.csdn.net/cjwtsl/article/details/126001449)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Java基础学习第七讲:Java中的String类、常用类及包装类](https://blog.csdn.net/u010611510/article/details/122348845)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值