java.lang.reflect

Posted 巅峰寂寞

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java.lang.reflect相关的知识,希望对你有一定的参考价值。

  要梳理这个包,就必须要整理一下反射了。为了方便描述,我们假定已经写好了一个普通类,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

 

以上是关于java.lang.reflect的主要内容,如果未能解决你的问题,请参考以下文章

什么可能导致 java.lang.reflect.InvocationTargetException?

java.lang.reflect.InvocationTargetException

Lint 基础结构错误原因:java.lang.reflect.InvocationTargetException - Android Studio 4.2.2

java.lang.reflect.InvocationTargetException

反射机制

java.lang.reflect操作对象属性(域)的值