这是我参与8月更文挑战的第19天,活动详情查看:8月更文挑战
一、Java 泛型
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
”泛型”一词中的泛字可以理解为泛化的意思,即由具体的、个别的扩大为一般的。Oracle对泛型的官方定义是:泛型类型是通过类型参数化的泛型类或接口。一言以蔽之,泛型就是通过类型参数化,来解决程序的通用性设计和实现的若干问题。
Java泛型是1.5版本后引入的特性,它主要被用于解决三类问题:
- 编译器类型检查
- 强制类型转换
- 可读性和灵活性
泛型类
先来看一段这样的代码,里面的使用到了多个泛型的方法,无需关注方法到底做了什么
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对象获取其他的对象。