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等数据结构的源码。