当前位置:首页 > 手机资讯 > 正文

java返回泛型utils

java返回泛型utils

Java反射、泛型、容器

  • 反射
  • 一、什么是反射?
  • 二、创建Class对象的三种方式
  • 方法一:直接通过一个class的静态变量class获取
  • 方法二:通过该实例变量提供的getClass()方法获取
  • 方法三:通过静态方法Class.forName()获取
  • 拓展
  • 三、通过Class创建对象
  • 无参构造方法
  • 有参构造方法
  • 四、通过反射获取属性值
  • 五、创建一个反射工具类
  • 泛型
  • 什么是泛型?
  • 泛型的基础
  • 类泛型
  • 方法泛型
  • 派生子类
  • 类型通配符
  • 通配符的上限 List
  • 通配符的下限 List
  • 类型擦除和桥接方法
  • 容器
  • ArrayList
  • 新增元素
  • LinkedList
  • 新增元素
  • HashMap
  • hash值的计算
  • 扩容机制
  • 数组下标的计算
  • 链表结构变为红黑树的条件

反射是指将一个类的成员映射成相对应的java类型
Class是反射的核心。
JVM中每个Class实例都指向一个数据类型(class或者interface),而这个实例又包含有class的所有信息。

JVM创建的class 的实例Class具有唯一性。

创建一个实体类Person:

方法一:直接通过一个class的静态变量class获取

方法二:通过该实例变量提供的getClass()方法获取

方法三:通过静态方法Class.forName()获取

拓展

因为Class实例在JVM中是唯一的,所以,上述方法获取的Class实例是同一个实例。可以用==比较两个Class实例:

注意一下Class实例比较和instanceof的差别:

用instanceof不但匹配指定类型,还匹配指定类型的子类。而用双等于判断class实例可以精确地判断数据类型,但不能作子类型比较。
通常情况下,我们应该用instanceof判断数据类型,因为面向抽象编程的时候,我们不关心具体的子类型。只有在需要精确判断一个类型是不是某个个class的时候,我们才使用双等于判断class实例。

不考虑反射,正常场景:

考虑反射,同样也是需要靠构造方法来创建对象的。

无参构造方法

Spring组件即通过反射使用无参构造方法创建bean:

有参构造方法

Spring组件即通过反射使用有参构造方法创建bean:

成员:

  • 属性Field.java
  • 方法(构造函数Constructor.java、静态方法、普通方法Method.java)
  • 包路径 Package.java

补充:

泛型的本质是类型参数化
允许在定义类、接口、方法时使用类型形参,当使用时指定具体类型。
所有使用该泛型参数的地方都被统一化,保证类型一致。
如果未指定具体类型,默认是Object类型。
体系中的所有类都增加了泛型,泛型也主要用在。
泛型优点:

  1. 代码简洁;
  2. 程序更加健壮;
  3. 编码期,可读性提高了。

类泛型

方法泛型

派生子类

定义一个接口泛型:

  1. 子类明确类型
  1. 子类不明确类型

类型通配符

因为通配符既没有extends,也没有super,因此:

  1. 不允许调用set(T)方法并传入引用(null除外);
  2. 不允许调用T get()方法并获取T引用(只能获取Object引用)。
    换句话说,既不能读,也不能写,那只能做一些null判断。
通配符的上限 List

格式:类型名称 对象名称
意义:只能接收该类型及其子类

通配符的下限 List

格式:类型名称 对象名称
意义:只能接收该类型及其父类

对比extends和super通配符
作为方法参数类型和类型的区别在于:

  1. 允许调用读方法T get()获取T的引用,但不允许调用写方法set(T)传入T的引用(传入null除外);
  2. 允许调用写方法set(T)传入T的引用,但不允许调用读方法T get()获取T的引用(获取Object除外)。

一个是允许读不允许写,另一个是允许写不允许读。

泛型是提供给javac编译器使用的,它用于限定的输入类型,让编译器在源代码的级别上,即挡住向中插入非法数据。但编译器编译完带有泛型的java程序后,生成的class文件中将不再带有泛型信息,以此使程序运行效率不受影响,这个过程称之为“擦除”。

由于类型被擦除了,为了维持多态性,所以编译器就自动生成了桥接方法。

jvm不认识泛型,会把泛型擦除掉

ArrayList和Vector的区别是:ArrayList是线程不安全的,当多条线程访问同一个ArrayList时,程序需要手动保证该的同步性,而Vector则是线程安全的

数据结构:数组

新增元素

  1. new ArrayList()时创建一个空的数组。
  1. 执行add()方法时确认数组大小是否需要扩容,需要则进行扩容。
  • 执行calculateCapacity方法时,默认初始化容量为10
  • grow方法执行实际的扩容操作,当添加第11个元素的时候,再次触发grow方法,此时将容量增加原来的一半整数长度;

java返回泛型utils_泛型

数据结构:双向链表

新增元素

  1. new LinkedList()时不进行任何操作;
  1. 执行add()方法,往元素最后链接新元素;

java返回泛型utils_泛型

数据结构:数组+链表+红黑树

java返回泛型utils_反射_03

hash值的计算

把key的hashhcode值,区分为高半区16位和低半区16位;

取异或时,相同为0,不同为1。(实则干扰的是从0位到size-16位的数据,干扰函数)

java返回泛型utils_容器_04

扩容机制

初始化数组大小为16、负载因子为0.75、阈值为16*0.75=12
hashmap中元素个数大于12,或者链表长度大于8时,需要扩容,数组长度和阈值都变为原来的两倍,resize函数中决定

扩容时会将原来的数组元素进行重排。

数组下标的计算

计算数组下标是根据(hash值)和(数组大小-1)的按位与的值。(这就决定了int类型的0、16、···、128、······都在数组下标为0的链表中)

如map.put(0, “a0”),那么hash值为0000,数组大小为16,则下标为(16-1)&0 = 1111&0000 = 0000 = 0;

map.put(16, “a16”),那么hash值为10000,数组大小为16,则下标为(16-1)&16 = 1111&10000 = 0000 = 0;

java返回泛型utils_java返回泛型utils_05

链表结构变为红黑树的条件
  • 链表长度大于8;
  • 数组长度大于等于64(小于64时只扩展数组大小)。

为什么是8 和64?
因为链表长度小于8的时候效率比红黑树高,红黑树的插入需要旋转比较耗时;
数组大于64才转,是防止空的太多(数组上没挂链),防止浪费内存。

红黑树的性质:

  • 每个节点要么是黑色,要么是红色;
  • 根节点是黑色;
  • 每个叶子节点(Nil)是黑色 ;
  • 每个红色节点的两个子节点一定都是黑色;
  • 任意一节点到每个叶子节点的路径都包含数量相同的黑色节点;

最新文章