【Java基础】JavaCore核心-泛型技术

简介: 【Java基础】JavaCore核心-泛型技术

5004e8c5ec9a48059370cbc5c92813db.jpg

1.什么是泛型

  • 是在定义类、接口和方法时,可以在声明时通过一定的格式指定其参数类型
  • 使用时再指定具体的类型,从而使得类、接口和方法可以被多种类型的数据所实例化或调用
  • 这种可以在编译时进行参数类型检查的技术被称为泛型,是 JDK 5 中引入的一个新特性
  • 本质是参数化类型,给类型指定一个参数,在使用时再指定此参数具体的值,那这个类型就可以在使用时决定
  • 优点
  • 把运行时的错误,提前到编译时,这样就可以在编译时把错误提示出来,避免了运行时出现错误
  • 使用泛型可以提高代码的复用性,因为它可以支持多种类型的数据。

2.为什么要用泛型

  • 在没有泛型之前,从集合中读取到的每一个对象都必须进行类型转换
  • 如果插入了错误的类型对象,在运行时的转换处理就会出错
  • 集合容器里面如果没指定类型,默认都是Object类型,那什么到可以插入
  • 减少了源代码中的强制类型转换,代码更加可读
  • 案例
  • 7d0dbbf637a842f9aeee1d379fd5cced.jpg

3.泛型的分类

  • 可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法
  • 泛型字母通常类型参数都使用大写的单个字母
  • T:任意类型 type
  • E:集合中元素的类型 element
  • K:key-value形式 key
  • V: key-value形式 value

4.泛型类和泛型接口案例实战

(1)什么是泛型类

  • 泛型类型必须是引用类型,即类类型(不能使用基本数据类型)
  • 在类名后添加一对尖括号,并在尖括号中填写类型参数
  • 如果参数可以有多个,多个参数使用逗号分隔

(2)泛型类的定义

public class 类名 <泛型类型,...> {
   private 泛型类型 变量名
   public 泛型类型 方法名(){ }
   public 返回值 方法名(泛型类型 t){ }
   ....
}
  • JDK1.7后,结尾的具体类型不用写
类名<具体数据类型> 对象名 = new 类名< >();

注意

  • 泛型类创建的使用没有指定类型,则默认是object类型
  • 泛型类型从逻辑上看是多个类型,实际都是相同类型
  • Java 可以创建对应的泛型对象和泛型数组引用,但不能直接创建泛型对象和泛型数组
  • Java 有类型擦除,任何泛型类型在擦除之后就变成了 Object 类型
  • 因此创建泛型对象就相当于创建了一个 Object 类型的对象
  • 所以直接创建泛型对象和泛型数组也的行为被编译器禁止

(3)案例实战(T是外部使用的时候来指定的类型)

package com.lixiang;
public class GenericTest<T> {
    private Object[] arr;
    private int num;
    public GenericTest(int size){
        arr = new Object[size];
        num = 0;
    }
    public T get(int index){
        if(index>=arr.length){
            return null;
        }
        return (T) arr[index];
    }
    public void put(T data){
        if(num>arr.length){
            return;
        }
        arr[num++] = data;
    }
    public static void main(String[] args) {
        GenericTest<String> genericTest = new GenericTest<>(5);
        genericTest.put("lixiang");
        genericTest.put("lixiang");
        System.out.println(genericTest.get(0));
    }
}

2af05e62c61b402f8e1ea1683a30e3a9.jpg

  • 如果泛型类的子类也是泛型类,那父类和子类的类型要一致;
  • 如果子类泛型有多个,那需要包括父类的泛型类型

33fd0248b3b64c19b6fbc203d0b8f912.jpg

(4)泛型接口

  • 规则和泛型类一样
  • 如果实现类是泛型类,那接口和实现类的泛型类型要一致;如果实现类泛型有多个,那需要包括接口的泛型类型
  • 如果实现类不是泛型类,那接口要明确泛型类的类型
  • 格式
interface 接口名称 <泛型类型1,...> {
   泛型类型 方法名();
   ....
}

5.泛型方法案例实战

(1)什么是泛型方法

  • 调用方法的时候指定泛型的具体类型
  • 格式
修饰符 <T,E,...> 返回值类型 方法名( 参数列表 ){ 
方法体
....
}
  • 修复符和返回值中间的有 才是泛型方法 泛型类里面的普通返回值类型不是

(2)声明泛型方法

  • 泛型类的类型和泛型方法的类型是互相独立的,同名也不影响
  • 声明了【泛型方法】在参数列表和方法体里面才可以用对应的泛型
    public static  <E> E getRandomElement(List<E> list){
        Random random = new Random();
        return list.get(random.nextInt(list.size()));
    }
    //测试
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李祥");
        list.add("王武");
        String randomElement = getRandomElement(list);
        System.out.println(randomElement);
    }

3734bf928e4d42d4886c855a19aa4e9d.jpg

  • 注意
  • 使用了类泛型的成员方法,不能定义为静态方法;使用了泛型方法的才可以定义为静态方法85b2b8dfc9cf4d6d9bb5dc5405717511.jpg

8ca0c77a2b45456cb76d0ed8d688e062.jpg


(3)可变参数的泛型方法

    public static <T> void print(T...arr){
        for(T t:arr){
            System.out.println(t);
        }
    }
    //测试
    public static void main(String[] args) {
        print("李祥","张三","李四");
    }

50561005c1594c5f9d8832b88908abea.jpg

6.泛型通配符案例实战

(1)什么是泛型通配符

  • Java泛型的通配符是用于解决泛型之间引用传递问题的特殊语法
//表示类型参数可以是任何类型
public class CustomCollection<?>{}
//表示类型参数必须是A或者是A的子类
public class CustomCollection<T extends A>{}
//表示类型参数必须是A或者是A的超类型
public class CustomCollection<T supers A>{}

分类

  • 通用类型通配符 < ? >,如List < ? >
  • 主要作用就是让泛型能够接受未知类型的数据
  • 可以把 ?看成所有泛型类型的父类,是一种真实的类型,类型通配符是实参,不是形参
  • 固定上边界的通配符 采用的形式
  • 使用固定上边界的通配符的泛型, 只能够接受指定类及其子类类型的数据。
  • 采用的形式, 这里的E就是该泛型的上边界
  • 注意: 这里虽然用的是extends关键字, 却不仅限于继承了父类E的子类, 也可以代指显现了接口E的类
  • 固定下边界的通配符,采用的形式
  • 使用固定下边界的通配符的泛型, 只能够接受指定类及其父类类型的数据。
  • 采用的形式, 这里的E就是该泛型的下边界.
  • 可以为一个泛型指定上边界或下边界, 但是不能同时指定上下边界

(2)泛型通配符案例实战

    //使用泛型通配符,复用性更强
    public static void print(NumberCollection<?> collection){
        Object collectionValue = collection.getValue();
        System.out.println(collectionValue);
    }
public class NumberCollection<T> {
    private T value;
    public NumberCollection(T value){
        this.value = value;
    }
    public T getValue() {
        return value;
    }
    public void setValue(T value) {
        this.value = value;
    }
    //使用泛型通配符,复用性更强
    public static void print(NumberCollection<?> collection){
        Object collectionValue = collection.getValue();
        System.out.println(collectionValue);
    }
    public static void main(String[] args) {
        NumberCollection<Integer> integerNumberCollection = new NumberCollection<>(1);
        NumberCollection<Long> longNumberCollection = new NumberCollection<>(23L);
        //通用性更强
        print(integerNumberCollection);
        print(longNumberCollection);
    }
}

7e08209936954a1bb9c0b56db7740437.jpg

(3)泛型上边界通配符

    //使用泛型通配符, 固定上边界的通配符,不能任意元素,只能是Number的子类
    public static void printUp(NumberCollection<? extends Number> collection){
        Number collectionValue = collection.getValue();
        System.out.println(collectionValue);
    }
     //字符串类型,测试报错
     NumberCollection<String> stringNumberCollection = new NumberCollection<>("springboot");
     printUp(stringNumberCollection);

(4)泛型下边界通配符

    //只是是integer或者integer的父类
    public static void printDown(NumberCollection<? super Integer> collection){
        Object object = collection.getValue();
        System.out.println(object);
    }
    //测试
     NumberCollection<Long> longNumberCollection = new NumberCollection<>(23L);
    //报错,类型不支持,需要ineter或其父类
    // printDown(longNumberCollection);
    NumberCollection<Integer> integerNumberCollection = new NumberCollection<>(1);
    printDown(integerNumberCollection);
    NumberCollection<Number> numberCollection = new NumberCollection<>(55L);
    printDown(numberCollection);

7.泛型类型擦除

(1)什么是泛型类型擦除

  • 泛型是jdk1.5后出现的,但泛型代码和常规版本代码可以兼容,主要原因是泛型信息是在代码编译阶段
  • 代码编译完成后进入JVM运行前,相关的泛型类型信息会被删除,这个即泛型类型擦除
  • 作用范围:类泛型,接口泛型,方法泛型

(2)无类型限制泛型擦除测试

//没指定类型则擦除后是Object最顶级父类
public class Generic <T,K> {
    private T age;
    private K name;
    public static void main(String[] args) {
        Generic generic = new Generic<Integer,String>();
        //反射获取字节码文件class对象
        Class<? extends Generic> aClass = generic.getClass();
        //获取所有成员变量
        Field[] declaredFields = aClass.getDeclaredFields();
        for(Field field : declaredFields){
            //获取每个属性名称和类型
            System.out.println(field.getName() +",类型="+field.getType().getSimpleName());
        }
    }
}

4d83737d70914a3d95eeb0201372527f.jpg

8.泛型数组的创建

  • 在 Java 中是不能直接创建泛型对象和泛型数组的
  • 主要原因是 Java 有类型擦除,任何泛型类型在擦除之后就变成了 Object 类型或者对应的上限类型
  • 那定义的类中如果需要用到泛型数组,如何解决这个问题?
  • 需求:创建一个类里面支持泛型数组和返回全部数组的方法
public class GenericsArray<T> {
    private  T[] array;
    public GenericsArray(Class<T> clz ,int capacity) {
        array = (T[]) Array.newInstance(clz,capacity);
    }
    public T[] getAll() {
        return array;
    }
    public void put(int index, T item) {
        array[index] = item;
    }
    public T get(int index) {
        return (T)array[index];
    }
    public static void main(String[] args) {
        GenericsArray<String> genericsArray = new GenericsArray(String.class,3);
        genericsArray.put(0,"springcloud");
        genericsArray.put(1,"springboot");
        String value = genericsArray.get(0);
        System.out.println(value);
        //下面代代码运行不报错,虽然有泛型的擦除,但在构造器中传递了类型标记Class,从擦除中恢复,使得可以创建实际类型的数组
        String[] all = genericsArray.getAll();
        System.out.println(all);
    }
}

806ddc3767a24981892f6a041e8b9cda.jpg


相关文章
|
11天前
|
JSON 前端开发 JavaScript
java-ajax技术详解!!!
本文介绍了Ajax技术及其工作原理,包括其核心XMLHttpRequest对象的属性和方法。Ajax通过异步通信技术,实现在不重新加载整个页面的情况下更新部分网页内容。文章还详细描述了使用原生JavaScript实现Ajax的基本步骤,以及利用jQuery简化Ajax操作的方法。最后,介绍了JSON作为轻量级数据交换格式在Ajax应用中的使用,包括Java中JSON与对象的相互转换。
24 1
|
16天前
|
SQL Java 数据库连接
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率。本文介绍了连接池的工作原理、优势及实现方法,并提供了HikariCP的示例代码。
30 3
|
16天前
|
SQL 监控 Java
Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面
本文探讨了Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面。同时,结合最佳实践,介绍了如何选择合适的连接池库、合理配置参数、使用监控工具及优化数据库操作,以实现高效稳定的数据库访问。示例代码展示了如何使用HikariCP连接池。
10 2
|
18天前
|
Java 数据库连接 数据库
优化之路:Java连接池技术助力数据库性能飞跃
在Java应用开发中,数据库操作常成为性能瓶颈。频繁的数据库连接建立和断开增加了系统开销,导致性能下降。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接,显著减少连接开销,提升系统性能。文章详细介绍了连接池的优势、选择标准、使用方法及优化策略,帮助开发者实现数据库性能的飞跃。
25 4
|
16天前
|
Java 数据库连接 数据库
深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能
在Java应用开发中,数据库操作常成为性能瓶颈。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能。文章介绍了连接池的优势、选择和使用方法,以及优化配置的技巧。
16 1
|
16天前
|
算法 Java 数据库连接
Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性
本文详细介绍了Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性。连接池通过复用数据库连接,显著提升了应用的性能和稳定性。文章还展示了使用HikariCP连接池的示例代码,帮助读者更好地理解和应用这一技术。
31 1
|
18天前
|
SQL Java 数据库连接
打破瓶颈:利用Java连接池技术提升数据库访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,避免了频繁的连接建立和断开,显著提升了数据库访问效率。常见的连接池库包括HikariCP、C3P0和DBCP,它们提供了丰富的配置选项和强大的功能,帮助优化应用性能。
37 2
|
6月前
|
SQL Java 数据库连接
Java从入门到精通:3.1.2深入学习Java EE技术——Hibernate与MyBatis等ORM框架的掌握
Java从入门到精通:3.1.2深入学习Java EE技术——Hibernate与MyBatis等ORM框架的掌握
|
6月前
|
存储 设计模式 算法
Java从入门到精通:2.1.1深入学习Java核心技术——掌握Java集合框架
Java从入门到精通:2.1.1深入学习Java核心技术——掌握Java集合框架
|
6月前
|
算法 Java 程序员
论文翻译 | 【深入挖掘Java技术】「底层原理专题」深入分析一下并发编程之父Doug Lea的纽约州立大学的ForkJoin框架的本质和原理
本文深入探讨了一个Java框架的设计、实现及其性能。该框架遵循并行编程的理念,通过递归方式将问题分解为多个子任务,并利用工作窃取技术进行并行处理。所有子任务完成后,其结果被整合以形成完整的并行程序。 在总体设计上,该框架借鉴了Cilk工作窃取框架的核心理念。其核心技术主要聚焦于高效的任务队列构建和管理,以及工作线程的管理。经过实际性能测试,我们发现大多数程序的并行加速效果显著,但仍有优化空间,未来可能需要进一步研究改进方案。
83 3
论文翻译 | 【深入挖掘Java技术】「底层原理专题」深入分析一下并发编程之父Doug Lea的纽约州立大学的ForkJoin框架的本质和原理