JavaSE(四)反射与泛型
发布日期:2021-06-28 21:06:47 浏览次数:3 分类:技术文章

本文共 9509 字,大约阅读时间需要 31 分钟。

反射与泛型

反射

Class类

class本身是一种数据类型(Type),class/interface的数据类型是Class,JVM为每个加载的class创建唯一的Class实例。

Class实例包含该class的所有信息,通过Class实例获取class信息的方法称为反射(Reflection)

获取一个class的Class实例:	1)Class cls = String.class;	2)Class cls = "str".getClass();	3)Class cls = Class.forName("java.lang.String");注意 Class的==比较和instanceof的区别
Integer n = new Integer(123);boolean b3 = n instanceof Integer; // trueboolean b4 = n instanceof Number; // true// 精确判断时boolean b1 = n.getClass() == Integer.class; // trueboolean b2 = n.getClass() == Number.class; // false
从Class实例获取class信息:	1)getName():获取完整类名	2)getSimpleName():获取简单的类名	3)getPackage():获取包名从Class实例判断class类型:	1)isInterface():判断是否是接口类型	2)isEnum():判断是否是枚举类型	3)isArray():判断是否是数组类型	4)isPrimitive():判断是否是基本类型创建class实例:	cls.newInstance()	JVM总是动态加载class,可以在运行期根据条件控制加载class
// commons logging优先使用Log4jLogFactory factory;if(isClassPresent("org.apache.logging.log4j.Logger")){
factory = createLog4j();}else{
factory = createJdkLog();}boolean isClassPresent(String name){
try{
Class.forName(name); return true; }catch(Exception e){
return false; }}

访问字段

通过Class实例获取字段field信息:	1)getField(name)——获取某个public的field(包括父类)	2)getDeclaredField(name)——获取当前类的某个field(不包括父类)	3)getFields()——获取所有public的field(包括父类)	4)getDeclaredFields()——获取当前类的所有field(不包括父类)Field对象包含一个field的所有信息	1)getName():字段名称	2)getType():字段定义的数据类型	3)getModifiers():字段修饰符获取和设置field的值	1)get(Object obj)	2)set(Object, Object)通过反射访问Field需要通过SecurityManager设置的规则通过设置setAccessible(true)来访问非public字段

调用方法

通过Class实例方法获取Method信息:	1)getMethod(name, Class ...):获取某个public的method(包括父类)	2)getDeclaredMethod(name, Class ...):获取当前类的某个method(不包括父类)	3)getMethods():获取所有public的method(包括父类)	4)getDeclaredMethods():获取当前类的所有method(不包括父类)Method对象包含一个method的所有信息	1)getName():获取方法名	2)getReturnType():获取返回值类型	3)getParameterTypes():获取方法参数类型	4)getModifiers():获取方法修饰符调用Method:	Object invoke(Object obj, Object ...args)通过设置setAccessible(true)来访问非public方法反射调用Method也遵守多态的规则

调用构造方法

Class.newInstance()只能调用public无参数构造方法	String s = (String) String.class.newInstance();	// ^_^	Integer n = (Integer) Integer.class.newInstance();  // ERROR通过Class实例获取Constructor信息:	1)getConstructor(Class...):获取某个public 的 Constructor	2)getDeclaredConstructor(Class...):获取某个Constructor	3)getConstructors():获取所有public的Constructor	4)getDeclareConstructors():获取所有的Constructor通过Constructor实例可以创建一个实例对象	newInstance(Object ... parameters)通过设置setAccessible(true)来访问非public构造方法

获取继承关系

获取父类的Class:	1)Class getSuperclass()	2)Object的父类是null	3)interface的父类是null获取当前类直接实现的interface:	1)Class[] getInterfaces()	2)getInterfaces()获取的不包括间接实现的interface;如果想获取所有的,可以递归调用获取	3)没有interface的class返回空数组	4)interface返回继承的interface判断一个向上转型是否成立:	boolean isAssignableFrom(Class)

注解 Annotation

使用注解

注解是放在Java源码的类、方法、字段、参数前的一种标签注解本身对代码逻辑没有任何影响,如何使用注解由工具决定编译器可以使用的注解	@Override:让编译器检查该方法是否正确实现了覆写	@Deprecated:告诉编译器该方法已经标记为“作废”	@SuppressWarnings:让编译器忽略警告注解可以定义配置参数和默认值	配置参数由注解类型定义	配置参数可以包括:		所有基本类型		String		枚举类型		数组	配置参数必须是常量

定义注解

使用@interface定义注解,例如
@Target(ElementType.METHOD)public @interface Report {
int type() default 0; String level() default "info"; String value() default "";}

使用元注解(meta annotation)定义注解

1)@Target
2)@Retention
3)@Repeatable
4)@Inherited

@Target

使用@Target定义Annotation可以被应用于源码的哪些位置:	类或接口:ElementType.TYPE	字段:ElementType.FIELD	方法:ElementType.METHOD	构造方法:ElementType.CONSTRUCTOR	方法参数:ElementType.PARAMETER

@Retention

使用@Retention定义Annotation的生命周期	仅编译器:RetentionRolicy.SOURCE	仅class文件:RetentionRolicy.CLASS	运行期:RetentionRolicy.RUNTIME如果@Retention不存在,则该Annotation默认为CLASS通常自定义的Annotation都是RUNTIME

@Repeatable

使用@Repeatable定义Annotation是否可重复JDK>=1.8

@Inherited

使用@Inherited定义子类是否可继承父类定义的Annotation	仅针对@Target为TYPE类型的Annotation	仅针对class的继承	对interface的继承无效

定义Annotation的步骤:

1)用@interface定义注解2)用元注解配置注解	Target:必须设置	Retention:一般设置为RUNTIME	通常不必写@Inherited,@Repeatable等3)定义注解参数和默认值

处理注解

注解本身对代码逻辑没有任何影响,SOURCE类型的注解在编译期就被丢掉了,CLASS类型的注解仅保存在class文件中,RUNTIME类型的注解在运行期可以被读取,如何使用注解由工具决定

Q:如何读取RUNTIME类型的注解?

Annotation也是class,所有Annotation继承自java.lang.annotation.Annotation,可以使用反射API

使用反射API读取Annotation:isAnnotationPresent、getAnnotation

isAnnotationPresent

判断某个注解是否存在

Class.isAnnotationPresent(Class)	Field.isAnnotationPresent(Class)	Method.isAnnotationPresent(Class)	Constructor.isAnnotationPresent(Class)

getAnnotation

获取某个注解信息

Class.getAnnotation(Class)	Field.getAnnotation(Class)	Method.getAnnotation(Class)	Constructor.getAnnotation(Class)

getParameterAnnotations

获取参数类型的注解:getParameterAnnotations()

Annotation[][] annos = m.getParameterAnnotations();
可以在运行期通过反射读取RUNTIME类型的注解	不要漏写@Retention(RetentionPolicy.RUNTIME)可以通过工具处理注解来实现相应的功能:	1)对JavaBean的属性值按规则进行检查	2)JUnit会自动运行@Test标记的测试方法

练习

请根据注解;	@NotNull:检查该属性为非null	@Range:检查整型介于min~max,或者检查字符串长度介于min~max	@ZipCode:检查字符串是否全部由数字构成,且长度恰好为value实现对Java Bean的属性值检查。如果检查未通过,抛出异常

泛型

什么是泛型

泛型(Generic)就是定义一种模板,例如ArrayList
在代码中为用到的类创建对应的ArrayList
<类型>
ArrayList
strList = new ArrayList
();编译器会针对泛型作检查,要注意泛型的继承关系 可以吧ArrayList
向上转型为List
(T不能变) 不能把ArrayList
向上转型为ArrayList
或List
ArrayList
和ArrayList
两者没有继承关系

使用泛型

定义泛型类型
: List
的泛型接口为强类型 void add(Number) Number get(int)不指定泛型参数类型时,编译器会给出警告,且只能将
视为Object类型

编写泛型

泛型一般用在集合类中,编写类型时,需要定义泛型类型
public class Pair
{...}静态方法不能引用泛型类型
,必须定义其他类型
来实现泛型 public static
Pair
create(K first, K last){ ... }泛型可以同时定义多种类型

擦拭法

Java的泛型是采用擦拭法(Type Erasure)实现的,编译器把类型视为Object,再根据实现安全的强制转型

擦拭法是局限:	1)
不能是基本类型,例如int 2)Object字段无法持有基本类型 3)无法取得带泛型的Class 4)无法判断带泛型的Class 5)不能实例化T类型,因为擦拭后实际上是new Object()

可以继承自泛型类

public class IntPair extends Pair
{
}Class
clazz = IntPair.class;Type t = clazz.getGenericSuperclass();if(t instanceof ParameterizedType){
ParameterizedType pt = (ParameterizedType)t; Type[] types = pt.getActualTypeArguments(); Type firstType = types[0]; Class
typeClass = (Class
) firstType; System.out.println(typeClass); // Integer}

父类类型是Pair<Integer>;子类的类型是IntPair;子类可以获取父类的泛型类型

继承关系:
在这里插入图片描述

extends通配符

public class Pair
{
... }public class PairHelper {
static int get(Pair
p) {
Number first = p.getFirst(); Number last = p.getLast(); return first.intValue() + last.intValue(); }}PairHelper.get(new Pair
(1, 2)); // √PairHelper.get(new Pair
(1, 2)); // ×

Pair<Integer>不是Pair<Number>的子类;get()不接受Pair<Integer>,所以可以使用<? extends Number>使方法接收所有泛型类型为Number或Number类的Pair类

public class PairHelper {
static int get(Pair
p) {
Number first = p.getFirst(); Number last = p.getLast(); return first.intValue() + last.intValue(); }}PairHelper.get(new Pair
(1, 2)); // √PairHelper.get(new Pair
(1, 2)); // √
对Pair
调用getFirst()方法: 方法签名:? extends Number getFirst() 可以安全赋值给Number类型的变量:Number x = p.getFirst(); 不可预测实际类型就是Integer:Integer x = p.getFirst();对Pair
调用setFirst()方法会报错: 方法签名:void setFirst(? extends Number) 无法传递任何Number类型给setFirst(? extends Number)

<? extends Number> 的通配符:

允许调用get方法获得Number的引用
不允许调用set方法传入Number的引用
唯一例外:可以调用setFirst(null)
<T extends Number> 的通配符
限定定义Pair<T>时只能是Number或Number的子类

super通配符

public class Pair
{
... }public class PairHelper {
static int set(Pair
p, Integer first, Integer last) {
p.setFirst(first); p.setLast(last); }}PairHelper.set(new Pair
(1, 2), 3, 4); // √PairHelper.set(new Pair
(1, 2), 3, 4); // ×

Pair<Integer>不是Pair<Number>的子类;set()不接受Pair<Number>,所以可以使用<? super Integer>使方法接收所有泛型类型为Integer或Integer超类的Pair类

public class PairHelper {
static int set(Pair
p, Integer first, Integer last) {
p.setFirst(first); p.setLast(last); }}PairHelper.set(new Pair
(1, 2), 3, 4); // √PairHelper.set(new Pair
(1, 2), 3, 4); // √
对Pair
调用setFirst()方法: 方法签名:void setFirst(? super Integer) 可以安全传入Integer类型的变量:p.setFirst(new Integer(123));对Pair
调用getFirst()方法会报错: 方法签名:? super Integer getFirst() 无法赋值给Integer类型的变量

<T super Integer> 的通配符

允许调用set方法传入Integer的引用
不允许调用get方法获得Integer的引用
唯一例外:可以获取Object引用Object o = p.getFirst();
<T extends Number> 的通配符
限定定义Pair时只能是Integer或Integer的超类
extends和super通配符的区别

允许调用方法获取T的引用
允许调用方法传入T的引用
public class Collections{
// 把src的每个元素复制到dest中 public static
void copy(List
dest, List
src){
for(var i=0; i

无限定通配符<?>

不允许调用set方法(null除外)只能调用get方法获取Object引用可以用
消除

泛型与反射

部分反射API是泛型	Class
Constructor
可以声明带泛型的数组,但不能直接创建带泛型的数组,必须强制转型,例如
Pair
[] ps = null; // √Pair
[] ps = new Pair
[2]; // ×Pair
[] ps = (Pair
[])new Pair[2]; // √
// 不安全地使用带泛型的数组==>通过引用Pair[] arr = new Pair[2];Pair
[] ps = (Pair
[]) arr;ps[0] = new Pair
("a", "b");arr[1] = new Pair
(1, 2);// ClassCastExceptionPair
p = ps[1];String s = p.getFirst();
// 安全地使用带泛型的数组 ==> 不要使用arr的引用@SuppressWarnings("unchecked")Pair
[] ps = (Pair
[]) new Pair[2];
不能直接创建 T[] 数组:	因为擦拭后变为new Object[5];必须借助Class
可以通过Array.newInstance(Class
, int)创建T[]数组,需要强制转型
T[] createArray(Class
cls){
return (T[]) Array.newInstance(cls, 5);}
public class ArrayHelper{
@SafeVarargs // 消除编译器警告 static
T[] asArray(T... objs){
return objs; }}String[] ss = ArrayHelper.asArray("a", "b", "c");Integer[] ns = ArrayHelper.asArray(1, 2, 3);

转载地址:https://blog.csdn.net/yangwei234/article/details/84977315 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:JavaSE(五)集合
下一篇:JavaSE(三)异常处理

发表评论

最新留言

表示我来过!
[***.240.166.169]2024年04月06日 16时58分50秒