• 包装类和泛型


    包装类和泛型严格来说算得上是JavaSE的内容,为什么他们要放在数据集合中?

    这和集合类有关,我们在集合类中将会用到大量的泛型和包装类。

    1. 包装类

    基本介绍

    包装类(wrapper)是针对八大基本数据类型相应的引用类型。

    既然我们叫他包装类,那么类中肯定有方法:

    例如:Integer

     那么包装类与基本数据类型的根本区别就在于能够调用方法,与此同时体现了面向对象。

    我们先来看看吧大基本类型对应的包装类:

    基本数据类型包装类
    byteByte
    shortShort
    intInteger
    longLong
    floatFloat
    doubleDouble
    charCharacter
    booleanBoolean

    除了 Integer 和 Character, 其余基本类型的包装类都是首字母大写。

    包装类和基本数据类型的转换

    以int和Integer为例

    1.jdk5前是手动装箱和拆箱的,装箱:基本类型 ——>包装类型。反之即为拆箱。

    2.jdk5(含jdk5)是自动装箱和拆箱的。

    3.自动装箱底层调用了valueOf方法。

    1. public class demo {
    2. public static void main(String[] args) {
    3. int a = 10;
    4. Integer val =a;
    5. }
    6. }

    我们来看这段代码,它是如何进行自动拆箱和装箱的。

    1. 先运行一遍程序,使之生成字节码文件

    2.鼠标右键改Java文件,点击Explorer,如下图:

    点击后: 

    返回上一级目录,找到进入out目录下:

     再在目录表中输入cmd进入终端:

      3.进入终端后输入指令进入反汇编:javap -c demo(你的.Java文件名) 

     我们在反汇编中找到了valuOf()方法。

    4. 在idea上选中Integer 按住ctrl + B 进入底层找到valuOf()方法

     传进来一个参数i ,返回时new 了一个Integer(i)。

     所以:

    1. int a = 10;
    2. //Integer val =a;//自动装箱
    3. Integer val = Integer.valueOf(a);//显示装箱
    4. Integer val2 = new Integer(a);//显示装箱

    我们用这两种方法显示装箱。

    我们来看看拆箱:

    1. public static void main(String[] args) {
    2. Integer val =10;
    3. int a = val;
    4. System.out.println(a);
    5. }

     我们找到intValue :

    2. 泛型(Generic)

    泛型的引出

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

    比如:在ArrayList(顺序链表下一章就会讲)中添加数据,我们利用之前学的去添加,并不能对添加的数据类型进行约束(不安全)。

    例如:在ArrayList中添加多条狗,但是一不小心传递了一只猫,我们再对其进行遍历,调用foreach需要向下转型(转为Dog),这再运行就会报异常

    不使用泛型的情况下:

    使用泛型后: 

     它就自动检查想要添加进来的类型。

    基本介绍

    1. 泛型又称参数化类型,是jdk5.0以后出现的新特征,解决数据类型安全问题。

    2. 再类声明或实例化时只要指定好需要的具体类型即可。

    3.Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会发生类型转换异常,同时代码更加健壮整洁。

    4.泛型的作用是:可以在类声明是通过一个标识符表示某个属性的类型,或者某个方法的放回类型或者参数类型。

    例如:

    1. public class Main {
    2. public static void main(String[] args) {
    3. Person<Integer> integerPerson = new Person<>(123);
    4. Person<String> bit = new Person<>("bit");
    5. }
    6. }
    7. class Person<E> {
    8. E s ;//E表示S的数据类型,该数据类型在定义Person时指定,
    9. // 即在编译期间确定E是什么类型
    10. public Person(E s) {
    11. this.s = s;
    12. }
    13. public E f() {
    14. return s;
    15. }
    16. }

    语法声明

    class 泛型类名称<类型形参列表> {
    // 这里可以使用类型参数
    }
    class 类 {
    }
    class 泛型类名称<类型形参列表> extends 继承类/* 这里可以使用类型参数 */ {
    // 这里可以使用类型参数
    }
    class 类 extends 类 {
    // 可以只使用部分类型参数
    }

    interface 接口 {

    说明:

    1. K、T、V不代表值,而是表示类型

    2. 可以使用任意字母表示:

    E 表示 Element
    K 表示 Key
    V 表示 Value
    N 表示 Number
    T 表示 Type
    S, U, V 等等 - 第二、第三、第四个类型

    3.类名后的 代表占位符,表示当前类是一个泛型类

    泛型的使用细节和注意事项

    1.interface 接口 { } 和public class HashSet{ } 

    T和E只能是同类型的引用

    2. 在给泛型指定具体类型后,可以传入该类型或其子类型

    例:若指定类型为A类,又需要添加B类,可以B类先继承A类再添加

    3.泛型的使用形式

    方法1:

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

    在实际应用中我们一般这么写:

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

    在我最开始学习面向对象的时候,看起来没有用到,其实默认是Object

    1. Pig pig = new Pig();
    2. //看起来啥都没有写,其实默认是<Objec>
    3. //因为Objec 默认是所有类型的父类
    4. //这也就是个裸类型

    裸类型(Raw Type)(了解)

    说明:

    裸类型是一个泛型类但没有带着类型实参,例如 MyArrayList 就是一个裸类型

    MyArray list = new MyArray();

    注意: 我们不要自己去使用裸类型,裸类型是为了兼容老版本的 API 保留的机制
    下面的类型擦除部分,我们也会讲到编译器是如何使用裸类型的。

    泛型的编译

    那么,泛型到底是怎么编译的?这个问题,也是曾经的一个面试问题。泛型本质是一个非常难的语法,要理解好他还是需要一定的时间打磨。

    代码:以泛型数组为例:

    1. public class demo {
    2. public static void main(String[] args) {
    3. MyArray<Integer> myArray = new MyArray<Integer>();
    4. }
    5. }
    6. class MyArray {
    7. public E[] obj = (E[])new Object[3];
    8. public E[] getObj() {
    9. return obj;
    10. }
    11. public void setObj(E[] obj) {
    12. this.obj = obj;
    13. }
    14. }

    我们进入终端,输入指令:javap -c  demo(.java文件名)

     简单讲解一下反汇编的代码:

     那么在程序编译好了以后跑进JVM,就没有了E[ ] 的概念了,我们泛型是在编译时期才存在的,一但程序运行起来以后,就不存在泛型这个概念了。E[ ] 在编译完成以后,就被擦除为了Object。

     泛型在编译期间只做两件事:

    1. 存储数据时,帮我们进行自动类型检查

    2. 获取元素时,帮我们完成自动类型转换

    有关泛型擦除机制的文章截介绍:
    Java泛型擦除机制之答疑解惑 - 知乎 (zhihu.com)

    自定义泛型

    介绍:

    1. 一般由单个字母大写表示;

    2. 可以有多个大写字母,如:

    3. 普通成员也可以使用泛型

    4. 使用泛型的数组不可以初始化(实例化),也就是不可以直接new;因为数组在new时无法确定T的类型,也就无法准确的开辟空间大小。

    5. 静态的方法(属性)不可以使用泛型,在类加载时,自定义泛型还未创建(在创建对象时创建),所以JVM无法识别自定义泛型

    自定义泛型接口

    基本语法:

    interface 接口名 { }

    注意细节:

    1. 接口中,静态成员也不可以使用泛型(原理如上);

    2.泛型接口的类型在继承或者实现接口是确定

    3. 如不指定则为默认的Object

    自定义泛型方法:

    例:

    1. class Car {
    2. public void fly(T t, R r) {
    3. //实现的代码
    4. }
    5. }

    泛型的继承和通配符说明 

    1. 泛型不具备继承性

    例如:

     MyArray<Integer> myArray = new MyArray<Object>();

    2. 如果需要对传入的类型变量做一定的约束,可以通过类型边界来约束;我们称之为泛型的上界
    语法:

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

    还可以使用通配符,通配符类型(?)

    例如:

    //表示支持A类以及A的子类

    ?的默认是实现是,表示?是继承Object的任意类型。

    3. 有泛型的上界,也就有泛型的下界

    例如:

    // 表示支持A类以及A类的父类,并且不局限于A的直接父类

    4. List<?>表示任意泛型类型均可接受

    举个例子:

    1. import java.util.ArrayList;
    2. import java.util.List;
    3. public class demo {
    4. public static void printCollection1(List<?> C) {
    5. for (Object object:C) {
    6. System.out.println(object);
    7. }
    8. }
    9. public static void printCollection2(List<? extends AA> C) {
    10. for (Object object:C) {
    11. System.out.println(object);
    12. }
    13. }
    14. public static void printCollection3(List<? super BB> C) {
    15. for (Object object:C) {
    16. System.out.println(object);
    17. }
    18. }
    19. public static void main(String[] args) {
    20. ArrayList<Object> list1 = new ArrayList<>();
    21. ArrayList<String> list2 = new ArrayList<>();
    22. ArrayList<AA> list3 = new ArrayList<>();
    23. ArrayList<BB> list4 = new ArrayList<>();
    24. }
    25. }
    26. class BB {
    27. }
    28. class AA extends BB {
    29. }

    我们的list1、list2、list3、list4均可以放入printCollection1()中;

    我们的list3、list4均可以放入printCollection2()中;

    我们的list1、list2均可以放入printCollection3()中;

  • 相关阅读:
    Service详解
    【JDBC】----封装工具类和ORM
    Jenkins 添加 Slave Agent 节点时报类文件不匹配错误
    Go 使用mencached缓存
    新南威尔士大学研究团队延长量子相干时间实现基准增长100倍
    FL STUDIO水果21版本新主题、插件、功能介绍
    rabbitmq消费端auto和manual区别;处理mq的requeue
    猿创征文 第二季| #「笔耕不辍」--生命不息,写作不止#
    Elasticsearch的初步学习
    9.java定时器
  • 原文地址:https://blog.csdn.net/weixin_67807492/article/details/128090072