反射的作用
Java反射描述的是,在运行状态中:
1、对于任意一个类,都能够知道这个类的所有属性和方法
2、对于任意一个类,都能够调用它的任意一个属性和方法
之所以强调属性、方法,是因为属性、方法是开发者对于一个类最关注的两个部分。实际上通过反射,不仅仅可以获知类的属性、方法,还可以获知类的父类、接口、包等信息
至于反射的原理,不难,Java类加载机制一文中讲到了,一个类在加载的时候,会在内存中生成一个代表这个.class文件的java.lang.Class对象,.classs文件里面就包含了描述这个类的信息的一切内容。至于.class文件,是由Java编译器(注意是Java编译器,指的不仅仅是Javac)编译而来的,是编译原理的领域;.class文件结构,网上很多讲解了,比如类文件结构,这里就不讲了。
其实本文只是总结性的,并没有任何复杂或者难以理解的知识点,关注点只是希望用到的时候有个地方可以方便地查看而已。
本文用到的实体类
定义一下本文用到的实体类,尽量把这个实体类的内容定义得全一些,以期能说明问题:
package com.xrq.test19; import java.io.Serializable; import java.util.List; @SuppressWarnings("serial") public class Reflection implements Cloneable, Serializable { private String str; private double d; public boolean b; public static short s; public Reflection() { } public Reflection(String str) { this.str = str; } public Reflection(String str, double d, boolean b) { this.str = str; this.d = d; this.b = b; } private void privateMethod() { } public String publicMethod() { privateMethod(); return null; } public String publicMethod(int i) { return null; } public String publicMethod(int i, double d, List<String> l) { return "Reflection.publicMethod(int i, double d), i = " + i + ", d = " + d; } public static int returnOne() { return 1; } public String toString() { return "str = " + str + ", d = " + d + ", b = " + b; } }
Class和ClassLoader
和此java.lang.Class相关的Class和ClassLoader对象。测试代码为:
public static void main(String[] args) throws Exception { Class<?> c = Class.forName("com.xrq.test19.Reflection"); Reflection[] rs = new Reflection[2]; System.out.println("Class.getClass():" + c.getClass()); // 获取java.lang.Class的Class对象 System.out.println("Class.getClassLoader():" + c.getClassLoader()); // 获取类的加载器 System.out.println("Class.getSuperclass():" + c.getSuperclass()); // 获取父类Class对象 System.out.println("Class.getInterfaces():" + c.getInterfaces()[0] + ", " + c.getInterfaces()[1]); // 获取类的接口列表,注意返回的是一个数组 System.out.println("Class.getgetComponentType():" + rs.getClass().getComponentType()); // 获取该数组的Class对象 Reflection r = (Reflection)c.newInstance(); // 根据Class实例化出一个类实例来,默认调用无参构造方法 System.out.println("Class.newInstance():" + r); }
运行结果:
Class.getClass():class java.lang.Class Class.getClassLoader():sun.misc.Launcher$AppClassLoader@63c78e57 Class.getSuperclass():class java.lang.Object Class.getInterfaces():interface java.lang.Cloneable, interface java.io.Serializable Class.getgetComponentType():class com.xrq.test19.Reflection Class.newInstance():str = null, d = 0.0, b = false
Package
Package对象包含有关Java包的实现和规范的版本信息。测试代码为:
public static void main(String[] args) throws Exception { Class<?> c = Class.forName("com.xrq.test19.Reflection"); Package p = c.getPackage();
System.out.println("Package.toString():" + p.toString()); //toString() System.out.println("Package.getName():" + p.getName()); // 获取包名 System.out.println("Package.getImplementationTitle():" + p.getImplementationTitle()); // 获取包标题 System.out.println("Package.getImplementationVendor():" + p.getImplementationVendor()); // 获取提供该实现的组织、供应商或公司的名称 System.out.println("Package.getImplementationVersion():" + p.getImplementationVersion()); // 获取该实现的版本 System.out.println("Package.isSealed():" + p.isSealed()); // 获取包是否密封的
运行结果:
Package.toString():package com.xrq.test19 Package.getName():com.xrq.test19 Package.getImplementationTitle():null Package.getImplementationVendor():null Package.getImplementationVersion():null Package.isSealed():false
Field
提供有关类或接口的单个字段的信息,以及对它的动态访问权限。测试代码为:
public static void main(String[] args) throws Exception { Class<?> c = Class.forName("com.xrq.test19.Reflection"); Reflection r = new Reflection(); Field f0 = c.getField("b"); Field f1 = c.getDeclaredField("d"); Field[] fs0 = c.getFields(); Field[] fs1 = c.getDeclaredFields(); System.out.print("Class.getFields():"); // 获取类中所有public字段,顺序即public的Field定义的顺序 for (Field f : fs0) System.out.print(f + "\t"); System.out.println(); System.out.print("Class.getDeclaredFields():"); // 获取类中任意访问权限的字段,顺序即所有Field定义的顺序 for (Field f : fs1) System.out.print(f + "\t"); System.out.println(); System.out.println("Class.getField(String name):" + f0); // 根据name获取类中一个访问权限为public的字段 System.out.println("Class.getDeclaredField(String name):" + f1); // 根据name获取类中一个任意访问权限的字段 System.out.println(); System.out.println("Field.getName():" + f0.getName()); // 获取字段名 System.out.println("Field.getType():" + f0.getType()); // 获取类的类型 System.out.println("Field.getBoolean():" + f0.getBoolean(r)); // 获取某个实例对象该Field的值,什么类型的Field就是getXXX(Object obj) System.out.println("Field.getModifiers():" + f0.getModifiers()); // 以整数形式返回此Field对象的Java语言修饰符,如public、static、final等 System.out.println("Field.isAccessible():" + f0.isAccessible()); // 返回Field的访问权限,对private的Field赋值,必须要将accessible设置为true,如下 System.out.println(); f1.setAccessible(true); System.out.println("Before setB():" + r); f1.setDouble(r, 1.1); System.out.println("After setB():" + r); // 向对象的指定Field设定值 }
运行结果:
Class.getFields():public boolean com.xrq.test19.Reflection.b public static short com.xrq.test19.Reflection.s Class.getDeclaredFields():private java.lang.String com.xrq.test19.Reflection.str private double com.xrq.test19.Reflection.d public boolean com.xrq.test19.Reflection.b public static short com.xrq.test19.Reflection.s Class.getField(String name):public boolean com.xrq.test19.Reflection.b Class.getDeclaredField(String name):private double com.xrq.test19.Reflection.d Field.getName():b Field.getType():boolean Field.getBoolean():false Field.getModifiers():1 Field.isAccessible():false Before setB():str = null, d = 0.0, b = false After setB():str = null, d = 1.1, b = false
Constructor
提供关于类的单个构造方法的信息以及对它的访问权限。测试代码为:
public static void main(String[] args) throws Exception { Class<?> c = Class.forName("com.xrq.test19.Reflection"); Constructor<?> constructor = c.getConstructor(String.class); Constructor<?>[] constructors = c.getConstructors(); System.out.println("Class.getConstructor(Class<?>... parameterTypes):" + constructor); // 获取指定参数列表的构造函数 System.out.print("Class.getConstructors():"); // 获取所有的构造函数 for (Constructor<?> con : constructors) System.out.print(con + "\t"); System.out.println("\n"); System.out.println("Constructor.getName():" + constructor.getName()); // 获取构造函数名,没什么意义,肯定是和类同名 System.out.println("Constructor.getModifiers():" + constructor.getModifiers()); // 获取以整数形式返回的此Constructor对象的Java语言修饰符,如public、static、final等 System.out.println("Constructor.isAccessible():" + constructor.isAccessible()); // 获取该Constructor的访问权限 System.out.println("Constructor.getParameterTypes():" + constructor.getParameterTypes()[0]); // 获取Constructor的参数类型,是个数组 System.out.println("Constructor.isVarArgs():" + constructor.isVarArgs()); // 获取此Constructor中是否带了可变数量的参数,即例如"String... str"类型的参数 System.out.println(); Reflection r = (Reflection)constructor.newInstance("123"); // 根据指定的构造方法实例化出一个类的实例来,重要 System.out.println("Constructor.newInstance():" + r); }
运行结果:
Class.getConstructor(Class<?>... parameterTypes):public com.xrq.test19.Reflection(java.lang.String) Class.getConstructors():public com.xrq.test19.Reflection(java.lang.String) public com.xrq.test19.Reflection(java.lang.String,double,boolean) public com.xrq.test19.Reflection() Constructor.getName():com.xrq.test19.Reflection Constructor.getModifiers():1 Constructor.isAccessible():false Constructor.getParameterTypes():class java.lang.String Constructor.isVarArgs():false Constructor.newInstance():str = 123, d = 0.0, b = false
Method
提供关于类或接口上单独某个方法(以及如何访问该方法)的信息,所反映的方法可能是类方法或实例方法。测试代码为:
public static void main(String[] args) throws Exception { Reflection r = new Reflection(); Class<?> c = Class.forName("com.xrq.test19.Reflection"); Method md0 = c.getMethod("publicMethod", int.class, double.class, List.class); Method md1 = c.getDeclaredMethod("privateMethod", new Class[0]); Method[] ms0 = c.getMethods(); Method[] ms1 = c.getDeclaredMethods(); System.out.println("Method.getMethod():" + md0); // 根据方法名和参数列表获取指定的public方法 System.out.println("Method.getDeclaredMethod():" + md1); // 根据方法名和参数列表获取指定的任意访问权限的方法,但不包括继承的方法 System.out.print("Method.getMethods():"); // 获取此类包括其父类中所有的public方法 for (Method m : ms0) System.out.print(m + "\t"); System.out.println(); System.out.print("Method.getDeclaredMethods():"); // 返回此类中所有的方法(无访问权限限制),但不包括继承的方法 for (Method m : ms1) System.out.print(m + "\t"); System.out.println("\n"); System.out.println("Method.getName():" + md0.getName()); // 获取方法的名字 System.out.println("Method.isAccessible():" + md0.isAccessible()); // 获取方法的访问属性 System.out.println("Method.isVarArgs():" + md0.isVarArgs()); // 获取方法是否带有可变数量的参数 System.out.println("Method.getReturnType():" + md0.getReturnType()); // 获取方法的返回类型 System.out.println("Method.getParameterTypes():" + md0.getParameterTypes()[0] + ", " + md0.getParameterTypes()[1] + ", " + md0.getParameterTypes()[2]); // 获取方法的参数类型,数组形式,注意一下和下面的方法的区别 System.out.println("Method.getGenericParameterTypes():" + md0.getGenericParameterTypes()[0] + ", " + md0.getGenericParameterTypes()[1] + ", " + md0.getGenericParameterTypes()[2]); // 获取方法的参数化(带泛型)类型,数组形式 System.out.println(); System.out.println(md0.invoke(r, 1, 2.2, new ArrayList<String>())); // 反射调用方法,重要 }
运行结果:
Method.getMethod():public java.lang.String com.xrq.test19.Reflection.publicMethod(int,double,java.util.List) Method.getDeclaredMethod():private void com.xrq.test19.Reflection.privateMethod() Method.getMethods():public java.lang.String com.xrq.test19.Reflection.toString() public java.lang.String com.xrq.test19.Reflection.publicMethod(int) public java.lang.String com.xrq.test19.Reflection.publicMethod(int,double,java.util.List) public java.lang.String com.xrq.test19.Reflection.publicMethod() public static int com.xrq.test19.Reflection.returnOne() public final void java.lang.Object.wait() throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public boolean java.lang.Object.equals(java.lang.Object) public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll() Method.getDeclaredMethods():public java.lang.String com.xrq.test19.Reflection.toString() public java.lang.String com.xrq.test19.Reflection.publicMethod(int) public java.lang.String com.xrq.test19.Reflection.publicMethod(int,double,java.util.List) public java.lang.String com.xrq.test19.Reflection.publicMethod() private void com.xrq.test19.Reflection.privateMethod() public static int com.xrq.test19.Reflection.returnOne() Method.getName():publicMethod Method.isAccessible():false Method.isVarArgs():false Method.getReturnType():class java.lang.String Method.getParameterTypes():int, double, interface java.util.List Method.getGenericParameterTypes():int, double, java.util.List<java.lang.String> Reflection.publicMethod(int i, double d), i = 1, d = 2.2
Modifier枚举值列表
Field、Constructor、Method中都有getModifiers()方法,返回的是表示此对象的Java语言修饰符,详细看下每个修饰符对应的枚举值:
修饰符 | 枚举值 |
public | 1 |
private | 2 |
protected | 4 |
static | 8 |
final | 16 |
synchronized | 32 |
volatile | 64 |
transient | 128 |
native | 256 |
interface | 512 |
abstract | 1024 |
strict | 2048 |
也就是说如果一个方法是"public static final synchronized"的,那么这个方法的getModifiers()返回的应该是1 + 8 + 16 + 32 = 57,有兴趣的可以自己试验一下。
那如果反过来,我有一个值是X,如何通过X知道它是哪种访问权限的呢?个人的方法是(写一个完整的版本,现实中可以用if...else简化算法):
1、X先和00000001,也就是1做"&"运算,X&1,非0就是public;
2、X再和00000010,也就是2做"&"运算,X&2,非0就是private;
3、X最后和00000100,也就是4做"&"运算,X&4,非0就是protected
其它几位也都是一样的判断方式。