Java基础系列之(一) - Reflection

  前段时间与一个新手谈论Java基础的时候提到反射,然后对反射的一些问题在这里基本介绍下。在介绍之前,这里了解几个反射的几个概念。

Class - class是对一个类进行描述的,你可以认为它是一个类的模型。

Constructor - constructor是一个Class的构造函数,一个Class可以允许重载多个构造函数。

Field - Filed是用来描述Class的成员变量的。

Method - method是描述一个Class的方法的。

Annotation - Annotation是描述Class的注解的,注解可以作用于Class,Method,Filed,Argument之上。

Generic - Generic指的是泛型,限制参数,返回类型的描述。

当然其他的还有参数,访问域等一些概念。

 

1、Class(类)

  有多种方式如下:

  Class clz = myobject.class;

  动态加载也可以:

  Class clz = Class.forName("类的全名");

 

 (1) 下面描述的是可以通过Class对象获取构造函数、方法、成员变量、注解等,这里注意的是,这里获取得都是访问范围是public。

        Class clz = Reflection1Test.class;
        
        //获取所有public的构造函数
        Constructor[] constructors = clz.getConstructors();
        //获取参数是String类型的public构造函数
        Constructor constructor = clz.getConstructor(new Class[]{String.class});
        
        //获取所有public方法
        Method[] methods = clz.getMethods();
        //获取方法名称是"test",参数类型分别是List和String,而且是public的方法
        Method method = clz.getMethod("test", new Class[]{java.util.List.class,String.class});
        
        //获取指定的枚举类型的枚举
        Annotation annotation = clz.getAnnotation(Reflection1Test.class);
        //获取该类的所有枚举
        Annotation[] annotations = clz.getAnnotations();

 

(2) 类的修饰范围

  

Class clz = Reflection1Test.class;
int modifiers = clz .getModifiers();

这里返回的是一个int值,int里面存储的信息包含public,private,protected,final,abstract,interface,native,static,synchronized,Transient,Volatile。这里稍稍提下存储原理,基本原理采用的是二进制的占位思想,之前我的一篇文章bitmap里面也是用的是二进制占位的算法。public,private,protected,final,abstract,interface,native,static,synchronized,Transient,Volatile每个定义一个二进制的位置。例如:

    public static final int PUBLIC           = 0x00000001;
public static final int PRIVATE = 0x00000002; public static final int PROTECTED = 0x00000004; public static final int STATIC = 0x00000008; public static final int FINAL = 0x00000010; public static final int SYNCHRONIZED = 0x00000020; public static final int VOLATILE = 0x00000040; public static final int TRANSIENT = 0x00000080; public static final int NATIVE = 0x00000100; public static final int INTERFACE = 0x00000200; public static final int ABSTRACT = 0x00000400; public static final int STRICT = 0x00000800;

这样的话,假如一个Class既是public,又是final,这个时候她的modifier值就是public+final。

当然你想判断它是否是public还是final,JDK提供一个modifier类专门处理,如下:

Modifier.isAbstract(int modifiers);
Modifier.isFinal(int modifiers);
Modifier.isInterface(int modifiers);
Modifier.isNative(int modifiers);
Modifier.isPrivate(int modifiers);
Modifier.isProtected(int modifiers);
Modifier.isPublic(int modifiers);
Modifier.isStatic(int modifiers);
Modifier.isStrict(int modifiers);
Modifier.isSynchronized(int modifiers);
Modifier.isTransient(int modifiers);
Modifier.isVolatile(int modifiers);

 

2、Constructor(构造函数)

  (1) 获取构造函数

    上面已经提到过了。怎么获取构造函数:
         

//获取所有public的构造函数
Constructor[] constructors = clz.getConstructors();

  上面返回的是一个public的构造函数数组。

 

  如果你像返回指定的构造函数,需要提供构造函数的参数类型。

 

Constructor constructor = clz.getConstructor(new Class[]{String.class});

 返回的也是public的构造函数,如果没有找到指定的构造函数,就会抛NoSuchMethodException。

 

(2) 构造函数的参数类型

  

Class[] cType = constructor.getParameterTypes();

 

(3) 构造函数实例化

 我们都知道构造函数是一个对象实例化的入口,或者说是new的时候要首先调用的特殊方法。那么Constructor肯定能够实例化一个对象的。

try {
            MyObject obj = (MyObject)constructor.newInstance(new Object[]{"123",new ArrayList<String>()});
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

constructor.newInstance()需要提供一个可变的参数列表,提供的参数必须和声明的构造函数的值和类型都一一对应。

 

3、Field(变量)

(1)获取Field对象

 

        //获取所有public成员变量
        Field[] fields = clz.getFields();
        //获取名称是name的public的变量
        Field field = clz.getField("name");

这里也一样,返回的都是public修饰符的变量,如果找不到指定匹配的变量名称,会抛NoSuchFieldException异常。

 

(2)获取Field的名称和类型

        //获取变量的名称
        String name = field.getName();
        
        //获取变量的类型
        Class fieldType = field.getType();

 

(3)get/set Field的值

        MyObject obj = new MyObject();
        field = clz.getField("name");
        //获取obj对象的name变量的值
        Object object = field.get(obj);
        //设置obj对象的变量name的值为123
        field.set(obj, "123");

这里通过变量的对象来获取它的值,并且能修改它的值。当然这里依然遵照的是public的原则。

 

(4)访问私有变量

 上面已经说过了,上面访问都是public的变量,那么,私有变量怎么访问呢?如下:

        //获取所有声明的变量
        Field[] declaredFields = clz.getDeclaredFields();
        Field declaredField = clz.getDeclaredField("privateString");
        //私有变量必须先将访问权限设置为true
        declaredField.setAccessible(true);
        Object o = declaredField.get(test2);

declaredField.setAccessible(true)代码是关闭字段的访问检查。

4、Method(方法)

(1)获取Method对象

        //获取所有public方法
        Method[] methods = clz.getMethods();
        //获取方法名称是"test",参数类型分别是List和String,而且是public的方法
        Method method = clz.getMethod("test", new Class[]{java.util.List.class,String.class});

如果没有找到指定匹配的Method,会抛NoSuchMethodException异常。

 

(2)获取Method的参数

        //获取Method的参数类型
        Class[] parameterTypes = method.getParameterTypes();

因为一个方法的参数可以是多个,所以返回的是一个数组。

 

(3)获取Method的返回值

        
        //返回Method返回值的类型
        Class returnType = method.getReturnType();

这里有人就问,如果没有返回值呢,如果是void的呢,其实void也可以看成一个返回值,空返回值的。所以void的方法返回的直接是void。

 

(4)Method调用

定义一个test方法,有两个参数,如下:

    public void test(List s,String s1){
        System.out.println("调用了Test");
    }
    Reflection1Test test = new Reflection1Test("");
        //调用Reflection1Test对象的test方法
        Object returnValue = method.invoke(test, new Object[]{new ArrayList(),"123"});

这里有一个小细节,如果test是null的话,调用的是方法必须是static。

 

(5)访问私有方法

        Reflection1Test test3 = new Reflection1Test("");
        //获取所有声明的方法
        Method[] declaredMethods = clz.getDeclaredMethods();
        Method declaredMethod = clz.getDeclaredMethod("test", new Class[]{java.util.List.class,String.class});
        declaredMethod.setAccessible(true);
        Object returnValue2 = declaredMethod.invoke(test3, new Object[]{new ArrayList(),"123"});

与Field一样setAccessible(true)关闭反射的权限检查。

 

5、Annotation(注解)

注解的简单理解就是在类、方法、变量上打一个标签,这个标签为了具体说明它的实际意义,对类、方法、变量没有实际应用意义。

(1)定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
@Documented
public @interface AnnotationTest {
    String value() default "";
}

注解定义是在interface前面带一个@符号,这就说明是一个注解,一般有几个定义规则。

@Retention - 定义注解存在的周期,RetentionPolicy有三个值,Source,Class,RUNTIME。
  RetentionPolicy.Source 表示只是在源码中存在,编译的时候就被丢弃了。
  RetentionPolicy.Class 表示编译的时候注入到Class中,但是VM加载的时候会丢弃。
  RetentionPolicy.RUNTIME 表示注解会被VM加载进去,在运行期能够调用。


@Target - 定义注解的作用范围,一般作用于类、方法、变量、构造函数。
  • ElementType.ANNOTATION_TYPE
  • ElementType.CONSTRUCTOR
  • ElementType.FIELD
  • ElementType.LOCAL_VARIABLE
  • ElementType.METHOD
  • ElementType.PACKAGE
  • ElementType.PARAMETER
  • ElementType.TYPE

其中,ANNOTATION_TYPE是针对注解用的,在注解定义上被应用的

 

  @Inherited - 能够被子类复用。

@Inherited
public @interface MyAnnotation {

}
@MyAnnotation
public class MySuperClass { ... }

public class MySubClass extends MySuperClass { ... }

 

@Documented - 能够在文档显示的。
@Documented
public @interface MyAnnotation {

}
@MyAnnotation
public class MySuperClass { ... }

(2)使用注解

1、类注解
@AnnotationTest(value="chs")
public class Reflection1Test {...}

下面是访问类的注解的例子:
        Class clz = Reflection1Test.class;
        
        //获取类的所有注解
        Annotation[] annotations = clz.getAnnotations();
        //获取注解类型是AnnotationTest
        Annotation annotation = clz.getAnnotation(AnnotationTest.class);
        if(annotation instanceof AnnotationTest){
            AnnotationTest annotationTest = (AnnotationTest)annotation;
            System.out.println(annotationTest.value());
        }

2、方法注解
    @AnnotationTest(value="chs2")
    public Reflection1Test(String s){
        
    }

下面是访问方法注解的例子:

        Method method = clz.getMethod("test", new Class[]{java.util.List.class,String.class});
        
        Annotation[] annotations2 = method.getAnnotations();

        Annotation annotation2 = method.getAnnotation(AnnotationTest.class);
        if(annotation instanceof AnnotationTest){
            AnnotationTest annotationTest = (AnnotationTest)annotation;
            System.out.println(annotationTest.value());
        }

 


3、变量注解
    @AnnotationTest(value="chs2")
    private String privateString;

下面是变量访问注解的例子:

        Field field = clz.getField("name");
        
        Annotation[] annotations3 = field.getAnnotations();

        Annotation annotation3 = field.getAnnotation(AnnotationTest.class);
        if(annotation instanceof AnnotationTest){
            AnnotationTest annotationTest = (AnnotationTest)annotation;
            System.out.println(annotationTest.value());
        }

 

4、参数注解

    public void test(List s,@AnnotationTest String s1){
        System.out.println("调用了Test");
    }

下面是参数访问注解的例子:

        Annotation[][] Annotations5 = method.getParameterAnnotations();
        Class[] parameterTypes = method.getParameterTypes();
        
        int i=0;
        for(Annotation[] annotations21 : Annotations5){
          Class parameterType = parameterTypes[i++];
          for(Annotation annotation12 : annotations21){
            if(annotation12 instanceof AnnotationTest){
                AnnotationTest myAnnotation = (AnnotationTest) annotation12;
                System.out.println("param: " + parameterType.getName());
                System.out.println("value: " + myAnnotation.value());
            }
          }

 

5、Generic(泛型)

泛型的应用非常广,一般使用的时候,是在定义一个参数化(可变的)的类或者接口,说白了,你根本不知道运行的时候应该具体哪一种类型,但是你知道是一个可变参数。你不能再运行的时候知道被参数化的类的类型是什么,当你具体使用这个类的时候,你可以显示指定它。

总之是,定义的时候不知道具体类型,使用的时候需要指定具体的类型。

 

 

(1)返回值是一个泛型类型

    public List<String> getList(){
        return null;
    }

下面是访问返回值的泛型类型的例子:

Method method = Reflection1Test.class.getMethod("getList", null);

Type return = method.getGenericReturnType();

if(return instanceof ParameterizedType){
    ParameterizedType type = (ParameterizedType) return ;
    Type[] arguments = type.getActualTypeArguments();
    for(Type argument : arguments ){
        Class argClz = (Class) argument ;
        System.out.println("argClz = " + argClz );
    }
}

 

(2)方法的参数是一个泛型类型

    
    public void setList(List<String> list){
    }

下面是一个方法参数的泛型类型访问的例子:

Method method = Reflection1Test.class.getMethod("setList", List.class);

Type[] genericParameterTypes = method.getGenericParameterTypes();

for(Type genericParameterType : genericParameterTypes){
    if(genericParameterType instanceof ParameterizedType){
        ParameterizedType pType = (ParameterizedType) genericParameterType;
        Type[] argTypes = pType.getActualTypeArguments();
        for(Type parameterType : argTypes){
            Class parameterArgClz= (Class) parameterType;
            System.out.println("parameterArgClass = " + parameterArgClz);
        }
    }
}

 

(3)变量的类型是一个泛型类型

    public List<String> list;

下面是一个变量的泛型类型访问的例子:

Field field = Reflection1Test.class.getField("list");

Type genericType = field.getGenericType();

if(genericType instanceof ParameterizedType){
    ParameterizedType pType = (ParameterizedType) genericType;
    Type[] argTypes = pType.getActualTypeArguments();
    for(Type argType : argTypes){
        Class argClz = (Class) argType;
        System.out.println("Class = " + argClz);
    }
}

 

至此,java反射基本介绍如此,下一张准备说下ArrayList、LinkedList、HashMap等数据结构的源码。 
 
 

 

posted @ 2014-12-03 18:07  武黄帝  阅读(468)  评论(0编辑  收藏  举报