Java高级特性——泛型、反射

583 阅读6分钟

这是我参与8月更文挑战的第19天,活动详情查看:8月更文挑战

一、Java 泛型

Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。

泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。 ”泛型”一词中的泛字可以理解为泛化的意思,即由具体的、个别的扩大为一般的。Oracle对泛型的官方定义是:泛型类型是通过类型参数化的泛型类或接口。一言以蔽之,泛型就是通过类型参数化,来解决程序的通用性设计和实现的若干问题。
Java泛型是1.5版本后引入的特性,它主要被用于解决三类问题:

  1. 编译器类型检查
  2. 强制类型转换
  3. 可读性和灵活性

泛型类

先来看一段这样的代码,里面的使用到了多个泛型的方法,无需关注方法到底做了什么

public class GenericClassTest{
    public static void main(String[] args) {
        //首先定义一个Integer类型的数组
        Integer[] arrInt = {1, 2, 3, 4, 5, 6, 7, 8, 9};
        //将第 1 个和第 9 个位置的元素进行交换

       new GenericClassTest().changeT(arrInt, 0, 8);
        System.out.println("arrInt = " + Arrays.asList(arrInt));

        List<String> list = Arrays.asList("a", "b");
        testIter(list);
    }

    /**
     * @param t           参数类型 T
     * @param firstIndex  第一个下标
     * @param secondIndex 第二个下标
     * @param <T>         表示定义了一个类型 为 T 的类型,否则没人知道 T 是什么,编译期也不知道
     */
    private <T> void changeT(T[] t, int firstIndex, int secondIndex) {
        T tmp = t[firstIndex];
        t[firstIndex] = t[secondIndex];
        t[secondIndex] = tmp;
    }

    /**
     * 遍历集合
     *
     * @param list 集合
     * @param <T>  表示定义了一个类型 为 T 的类型,否则没人知道 T 是什么,编译期也不知道
     */
    private static <T> void testIter(List<T> list) {
        for (T t : list) {
            System.out.println("t = " + t);
        }
    }
}

可以看到里面的 是不是每个方法都需要去申明一次,那要是 100 个方法呢?那是不是要申明 100 次的,这样时候泛型类也就应用而生了。那泛型类的形式是什么样子的呢?请看代码

public class GenericClazz<T>{
    //这就是一个最基本的泛型类的样子
}

所以说这个泛型类中的静态方法直接这么写就可以啦

/**
     * 遍历集合
     *
     * @param list 集合
     */
    private static <K> void testIter(List<K> list) {
        for (K t : list) {
            System.out.println("t = " + t);
        }
    }
  • 多个泛型类型同时使用

我们知道 Map 是键值对形式存在,所以如果对 Map 的 Key 和 Value 都使用泛型类型该怎么办?一样的使用,一个静态方法就可以搞定了,请看下面的代码

public class GenericMap {

    private static <K, V> void mapIter(Map<K, V> map) {
        for (Map.Entry<K, V> kvEntry : map.entrySet()) {
            K key = kvEntry.getKey();
            V value = kvEntry.getValue();
            System.out.println(key + ":" + value);
        }
    }

    public static void main(String[] args) {
        Map<String, String> mapStr = new HashMap<>();
        mapStr.put("a", "aa");
        mapStr.put("b", "bb");
        mapStr.put("c", "cc");
        mapIter(mapStr);
        System.out.println("======");
        Map<Integer, String> mapInteger = new HashMap<>();
        mapInteger.put(1, "11");
        mapInteger.put(2, "22");
        mapInteger.put(3, "33");
        mapIter(mapInteger);
    }
}

类型变量的限定

我们都知道在方法前指定了,那么就是说这个泛型类型和类定义时的泛型类型无关,所以可以在普通类中定义泛型方法,泛型可以限定类型变量必须实现某几种接口或者继承某个雷,多个限定类型通过&分隔,如:

public static <T extends Comparable> T min(T[] a)...

对泛型进行限制,使其只有集成或实现Comparable的类才能使用该方法

(1) ? extends X:表示类型的上界

特点:

  • 限定 ? 为 X 的子类型,但不知道是哪个子类型
  • 可以安全的访问数据,访问X及其子类型
<T extends BoundingType>

T表示绑定类型的子类型,T和绑定类型可以是类或者接口。

一个变量或者通配符可以绑定多个限定,用“&”分开

T extends Comparable & Serializable

若T的限定类型是类,则有且最多只有一个,且放于接口前面

泛型擦除

类型擦除指的是通过类型参数合并,将泛型类型实例关联到同一份字节码上。编译器只为泛型类型生成一份字节码,并将其实例关联到这份字节码上。类型擦除的关键在于从泛型类型中清除类型参数的相关信息,并且再必要的时候添加类型检查和类型转换的方法。

先来看下泛型擦除的定义

 泛型擦除
	因为泛型的信息只存在于 java 的编译阶段,编译期编译完带有 java 泛型的程序后,其生成的 class 文件中与泛型相关的信息会被擦除掉,以此来保证程序运行的效率并不会受影响,也就说泛型类型在 jvm 中和普通类是一样的。

别急,知道你看完概念肯定还是不明白什么叫泛型擦除,举个例子

public class GenericWipe {
    public static void main(String[] args) {
        List<String> listStr = new ArrayList<>();
        List<Integer> listInt = new ArrayList<>();
        List<Double> listDou = new ArrayList<>();

        System.out.println(listStr.getClass());
        System.out.println(listInt.getClass());
        System.out.println(listDou.getClass());

    }
}

Java泛型的处理几乎都在编译器中进行,编译器生成的字节码是不包涵泛型信息的,泛型类型信息将在编译处理是被擦除,这个过程即类型擦除。通常情况下,Java是通过以下方式处理泛型:Java编译器通过Code sharing方式为每个泛型类型创建唯一的字节码表示,并且将该泛型类型的实例都映射到这个唯一的字节码表示上。将多种泛型类形实例映射到唯一的字节码表示是通过类型擦除(type erasue)实现的。

Code sharing:对每个泛型类只生成唯一的一份目标代码;该泛型类的所有实例都映射到这份目标代码上,在需要的时候执行类型检查和类型转换。

二、反射机制

反射是Java语言本身具备的一个重要的动态机制。用一句话来解释反射的定义:自控制,自描述。即通过反射可以动态的获取类、属性、方法的信息,也能构造对象并控制对象的属性和行为。

一、什么是JAVA的反射机制

Java反射是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。

Java反射机制容许程序在运行时加载、探知、使用编译期间完全未知的classes。

换言之,Java可以加载一个运行时才得知名称的class,获得其完整结构。

三、JAVA反射机制提供了什么功能

Java反射机制提供如下功能:

在运行时判断任意一个对象所属的类

在运行时构造任意一个类的对象

在运行时判断任意一个类所具有的成员变量和方法

在运行时调用任一个对象的方法

在运行时创建新类对象

在使用Java的反射功能时,基本首先都要获取类的Class对象,再通过Class对象获取其他的对象。