• 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

  • 相关阅读:
    php中文乱码处理方法
    Zend 官方框架增加 Swoole 协程支持 !
    矩阵行列式的向量表示
    ArduinoYun教程之ArduinoYun硬件介绍
    MIT 操作系统实验 MIT JOS lab1
    java File_encoding属性
    Java入门 第一季第五章 编程练习解析
    android 三种定位方式
    6.30
    OpenJudge百炼习题解答(C++)--题3142:球弹跳高度的计算
  • 原文地址:https://www.cnblogs.com/andong2015/p/10627243.html
Copyright © 2020-2023  润新知