要梳理这个包,就必须要整理一下反射了。为了方便描述,我们假定已经写好了一个普通类,com.
反射API
接口 AnnotatedElement GenericArrayType GenericDeclaration InvocationHandler Member ParameterizedType Type TypeVariable WildcardType 类 AccessibleObject (代表访问检查的能力) Array (代表数组) Constructor (代表构造方法) Field (代表类的成员变量,类属性) Method (代表类的方法) Modifier Proxy ReflectPermission 异常 InvocationTargetException MalformedParameterizedTypeException UndeclaredThrowableException 错误 GenericSignatureFormatError
对于反射,除了上述java.lang.reflect包下的类外,还有一个非常重要的 java.lang.Class<T>。上面标黑的就是反射里最最常用的类了。下面先从Class说起
1.java.lang.Class
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。
Class
没有公共构造方法。Class
对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass
方法自动构造的。
Class 的类关系图如下:其中,除了Serializable以外,其他三个接口都是java.lang.reflect包下的接口
获取一个类的Class对象一般有2种方法:
Class userClass=User.class; //类名.class Class<?> userClass = Class.forName("com.study.reflect.User"); //这种方法必须参数必须使用类全名
Class.forName还有一个重载方法是可以指定ClassLoader的。比如上面的写法相当于
Class<?> userClass = Class.forName("com.study.reflect.User",true, ClassLoader.getSystemClassLoader());
基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void不能够使用forName获取,只能用.class获取,比如:int.class,void.class
对于数组类型,直接写int[].class,Object[].class
Class c=int[].class; c=Object[].class;
2.java.lang.reflect.Constructor
一般情况下,我们通过new Object()来生成一个类的实例,但有时我们没法直接new,只能通过反射动态生成。
通过反射生成对象有下面三种方法:
//方法一
Class<?> userClass2 = Class.forName("com.study.reflect.User"); User user = (User)userClass2.newInstance();
//方法二 User user2 = (User)userClass2.getConstructor().newInstance();
//方法三 User user3 = (User)userClass2.getConstructor(String.class).newInstance("张三");
上面的userClass2.getConstructor() 就是在获取User的构造方法。可以有参数,也可以无参数。Class也可以直接获取一个类的所有public构造方法
public Constructor<?>[] getConstructors() throws SecurityException
再次强调一下,Class获取的构造方法全是public的。而且一般我们也都是通过Class方法来获取Constructor对象的。Constructor类本身没有public构造方法。
当然,也有获取非public修饰的构造方法的办法,可以使用
//获取某个构造方法
Class.getDeclaredConstructor(Class<?>... parameterTypes);
比如:User.class.getDeclaredConstructor(String.class);
//获取所有构造方法 Class.getDeclaredConstructors();
比如:User.class.getDeclaredConstructors()
Constructor的类继承关系图如下:
Constructor常用的方法主要是下面几类:
1.获取构造方法上的注解,主要有三个方法。
public Annotation[] getAnnotations(); public Annotation[] getDeclaredAnnotations(); public <T extends Annotation> T getAnnotation(Class<T> annotationClass);
前两个都是获取方法上的所有注解。而且从源码看起来效果应该是完全一样的,下面是getAnnotations的源码:
/** * @since 1.5 */ public Annotation[] getAnnotations() { return getDeclaredAnnotations(); }
2.获取此构造方法对应的类。(就是这究竟是哪个类的构造方法)
public Class<T> getDeclaringClass();
Class<?> userClass2 = Class.forName("com.study.reflect.User"); Constructor<?> constructor = userClass2.getConstructor(); Class<?> declaringClass = constructor.getDeclaringClass(); System.out.println(declaringClass.getName()); //最后输出:com.study.reflect.User
如果要直接获取类名(String),可以调用getName方法
public String getName();
查看源码,实际上调用的就是上面的方法:
/** * Returns the name of this constructor, as a string. This is * the binary name of the constructor's declaring class. */ public String getName() { return getDeclaringClass().getName(); }
测试一下:
System.out.println(constructor.getName()); //最后输出:com.study.reflect.User
3.获取此构造方法抛出的异常类型
Class<?>[] exceptionTypes = constructor.getExceptionTypes(); Type[] genericExceptionTypes = constructor.getGenericExceptionTypes(); //Type是Class实现的一个接口,个人觉得和上面方法主要是返回类型不同,连Class对象都是同一个
4.获取参数的信息
Constructor<?> constructor2 = userClass2.getConstructor(String.class,int.class); Type[] genericParameterTypes = constructor2.getGenericParameterTypes(); Class<?>[] parameterTypes = constructor2.getParameterTypes(); //和上面相比,只是返回类型不同,对象都是同一个。
Annotation[][] parameterAnnotations = constructor2.getParameterAnnotations(); //需要注意的是这个获取到的是一个二维数组。因为每个参数上都可能都多个注解。
5.获取该方法的修饰符
int modifiers = constructor2.getModifiers();
该方法返回的并不是一个枚举,而是一个整数。这也许和java的历史版本有关。至于整数和修饰符的对应关系,我们后面说。
6.获取泛型
TypeVariable<? extends Constructor<?>>[] typeParameters = constructor2.getTypeParameters();
7.其他
boolean synthetic = constructor2.isSynthetic(); //是否是复合构造方法 boolean varArgs = constructor2.isVarArgs(); //是否有可变数量的参数,就是有没有参数是数组类型
constructor.toGenericString();
constructor.toString(); //这两种toString返回的基本差不多
比如构造方法是:
public User(String name,int ... args){ this.age=args[0]; }
public User(String name,int[] args){ this.age=args[0]; }
由于可变参数的本质也是数组,所以一个类中不可能同时有上面2个方法
如上两种情况,其中第二个参数本质都是数组,但是第一种可以任意赋值多个,则isVarArgs() 的结果就是true。但是第二种,则会返回false。重点还是参数个数是否可变。
Constructor<?> constructor3 = userClass2.getConstructor(String.class,int[].class); //任意参数个数本质也是数组,也是使用int[].class
//如果是非public方法,要使用getDeclaredConstructor(Class<?>... parameterTypes) boolean varArgs = constructor3.isVarArgs(); //针对int ... args的这种返回true, 如果参数是int[] args 则会返回false
isSynthetic好像很复杂,先留个坑,后面再填。
8.创建对象,一般用的最多的方法。
newInstance(Object... initargs)
就是正常方法的调用传参,比如用上面的构造商法生成一个User
User user = (User)constructor2.newInstance("name", new int[]{1, 2, 3});
3.java.lang.Field
先说一下怎么获取Field对象。这个类和Constructor一样,没有public构造方法。一般通过Class的如下方法获取
Class.getFields()
Class.getField(String)
Class.getDeclaredFields()
Class.getDeclaredField(String)
上述没有Declared就是获取public权限的,有Declared就是可以获取任意权限。
下面针对功能简单说一下Field方法的分类
1.取值get,赋值set
Field里面有一大堆的get,set方法,主要是针对各种Field可能是不同的类型而定的方法。简单来说,get就是获取某个对象当前属性的value,所以get方法的参数一般都是Object,代表一个实例对象。set是为某个实例对象把当前这个Field重新设置一个value,所以参数一般都有2个,第一个代表实例对象,第二个代表value.
方法很多,不一一列举。
2.获取该属性上的注解,或者判断是否有某个注解
public Annotation[] getAnnotations(); public Annotation[] getDeclaredAnnotations();
3.获取修饰符
getModifiers()
4.获取或修改权限,允许赋值
所有的类都基于jdk1.6