本文将叙述如何运行时查看类信息,其中包括变量,方法,以及通过反射修改变量,执行方法等
包括如何反射匿名内部类及如何执行其方法,但是笔者强烈不建议这么做,这里只是演示反射而已
下面是一个测试类
- packagereflect;
- publicclassOuter{
- static{
- System.out.println("Testing..");
- }
- TestInterfaceti=newTestInterface(){
- publicStringtest(){
- return"thisisti";
- }
- };
- publicOuter(Stringname){
- System.out.println("Outer");
- }
- publicStringtoString(){
- System.out.println("OutertoString");
- returnnewTestInterface(){
- publicStringtest(){
- return"thisisatest!";
- }
- }.test();
- }
- @SuppressWarnings("unused")
- privatevoidprivateMethod(){
- System.out.println("privateMethod");
- }
- protectedvoidprotectedMethod(){
- System.out.println("protectedMethod");
- }
- voidpackageMethod(){
- System.out.println("packageMethod");
- }
- publicstaticvoidstaticMethod(){
- System.out.println("staticMethod");
- }
- publicinterfaceTestInterface{
- publicStringtest();
- }
- publicstaticclassStaticInner{
- privatestaticfinalStringTAG="StaticInnerTAG";
- publicStaticInner(){
- System.out.println("StaticInner");
- }
- publicStringtoString(){
- System.out.println("StaticInnertoString");
- returnTAG;
- }
- }
- privateclassInner{
- Stringname;
- publicInner(){
- System.out.println("Inner");
- }
- publicInner(Stringname){
- System.out.println("reflect.Outer.Inner.Inner(Stringname)");
- this.name=name;
- }
- }
- }
package reflect;
public class Outer {
static{
System.out.println("Testing..");
}
TestInterface ti = new TestInterface() {
public String test() {
return "this is ti";
}
};
public Outer(String name){
System.out.println("Outer");
}
public String toString(){
System.out.println("Outer toString");
return new TestInterface() {
public String test() {
return "this is a test!";
}
}.test();
}
@SuppressWarnings("unused")
private void privateMethod(){
System.out.println("privateMethod");
}
protected void protectedMethod(){
System.out.println("protectedMethod");
}
void packageMethod(){
System.out.println("packageMethod");
}
public static void staticMethod(){
System.out.println("staticMethod");
}
public interface TestInterface {
public String test();
}
public static class StaticInner {
private static final String TAG = "StaticInnerTAG";
public StaticInner(){
System.out.println("StaticInner");
}
public String toString(){
System.out.println("StaticInner toString");
return TAG;
}
}
private class Inner{
String name;
public Inner(){
System.out.println("Inner");
}
public Inner(String name){
System.out.println("reflect.Outer.Inner.Inner(String name)");
this.name = name;
}
}
}
其中包括了普通内部类,静态内部类,内部匿名类,接口等
其中外部类Outer没有默认构造器,String reflect.Outer.Inner.getName()是private的……
外部类
Constructor,Method,Class.forName,newInstance
下面来看如何对上述的类进行反射
首先先看外部类Outer
- publicstaticvoidmain(String[]args){
- try{
- Class<?>outer=Class.forName("reflect.Outer");
- outer=Outer.class;
- Constructor<?>[]constructors=outer.getConstructors();
- for(Constructor<?>constructor:constructors){
- Class<?>[]types=constructor.getParameterTypes();
- System.out.println(constructor.getName()+"("+arrayToString(types)+")");
- }
- Constructor<?>outerConstructor=outer.getConstructor(String.class);
- OuterouterInstance=(Outer)outerConstructor.newInstance("aouter");
- System.out.println(outerInstance);
- }catch(ClassNotFoundExceptione){
- e.printStackTrace();
- }catch(SecurityExceptione){
- e.printStackTrace();
- }catch(NoSuchMethodExceptione){
- e.printStackTrace();
- }catch(IllegalArgumentExceptione){
- e.printStackTrace();
- }catch(InstantiationExceptione){
- e.printStackTrace();
- }catch(IllegalAccessExceptione){
- e.printStackTrace();
- }catch(InvocationTargetExceptione){
- e.printStackTrace();
- }
- }
- publicstaticStringarrayToString(Class<?>[]classes){
- StringBuilderstringBuilder=newStringBuilder();
- for(Class<?>type:classes){
- stringBuilder.append(type.getCanonicalName());
- stringBuilder.append(",");
- }
- if(classes.length>0){
- stringBuilder.deleteCharAt(stringBuilder.length()-1);
- }
- returnstringBuilder.toString();
- }
public static void main(String[] args) {
try {
Class<?> outer = Class.forName("reflect.Outer");
outer = Outer.class;
Constructor<?>[] constructors = outer.getConstructors();
for (Constructor<?> constructor : constructors) {
Class<?>[] types = constructor.getParameterTypes();
System.out.println(constructor.getName() + "(" + arrayToString(types) + ")");
}
Constructor<?> outerConstructor = outer.getConstructor(String.class);
Outer outerInstance = (Outer) outerConstructor.newInstance("a outer");
System.out.println(outerInstance);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public static String arrayToString(Class<?>[] classes){
StringBuilder stringBuilder = new StringBuilder();
for (Class<?> type : classes) {
stringBuilder.append(type.getCanonicalName());
stringBuilder.append(",");
}
if(classes.length > 0){
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
}
return stringBuilder.toString();
}
打印结果
- Testing..
- reflect.Outer(java.lang.String)
- Outer
- OutertoString
- thisisatest!
Testing..
reflect.Outer(java.lang.String)
Outer
Outer toString
this is a test!
下面简单分析下
- Class<?>outer=Class.forName("reflect.Outer");
- //outer=Outer.class;
Class<?> outer = Class.forName("reflect.Outer");
//outer = Outer.class;
我们对类reflect.Outer进行反射,这里使用Outer.class也可以获得Class对象
之前说过使用类字面量不会初始化该类,而Class.forName则会初始化,当使用Outer.class时候log如下
- reflect.Outer(java.lang.String)
- Testing..
- Outer
- OutertoString
- thisisatest!
reflect.Outer(java.lang.String)
Testing..
Outer
Outer toString
this is a test!
顺序改变了,打印的时候Testing..实际是下面这句触发的
- OuterouterInstance=(Outer)outerConstructor.newInstance("aouter");
Outer outerInstance = (Outer) outerConstructor.newInstance("a outer");
这时候加载Outer这个类,然后进行初始化
接下来我们查看了Outer的所有构造器Constructor,并打印出所需要的参数(与普通方法区别开来)
构造器只有一个,参数是String
如何执行这个带有String作为参数的构造器来返回一个Outer实例
- Constructor<?>outerConstructor=outer.getConstructor(String.class);
- OuterouterInstance=(Outer)outerConstructor.newInstance("aouter");
- System.out.println(outerInstance);
Constructor<?> outerConstructor = outer.getConstructor(String.class);
Outer outerInstance = (Outer) outerConstructor.newInstance("a outer");
System.out.println(outerInstance);
我们先取得我们需要的Constructor,然后,Constructor提供了newInstance方法,这样就可以获得Outer实例
最后打印实例,调用了toString函数
如果你尝试调用
- outer.newInstance();
outer.newInstance();
则会看到如下异常
java.lang.InstantiationException: reflect.Outer
at java.lang.Class.newInstance0(Class.java:357)
at java.lang.Class.newInstance(Class.java:325)
at reflect.Test.main(Test.java:30)
这是因为Outer没有提供默认构造器
在获得Constructor时其实是有下面两个选择的,至于区别,我们放在Method里说
- outer.getConstructors();
- outer.getDeclaredConstructors()
outer.getConstructors();
outer.getDeclaredConstructors()
现在看看如何获得类的方法,顺便看看Declared的作用
不如直接打印出来,这样比较直观
- Method[]methods=outer.getMethods();
- for(Methodmethod:methods){
- Class<?>[]types=method.getParameterTypes();
- System.out.println(method.getReturnType().getName()+""+outer.getName()+"."+method.getName()+"("+arrayToString(types)+")");
- }
- System.out.println("--------------------------------------");
- methods=outer.getDeclaredMethods();
- for(Methodmethod:methods){
- Class<?>[]types=method.getParameterTypes();
- System.out.println(method.getReturnType().getName()+""+outer.getName()+"."+method.getName()+"("+arrayToString(types)+")");
- }Class<?>outer=Outer.class;
Method[] methods = outer.getMethods();
for (Method method : methods) {
Class<?>[] types = method.getParameterTypes();
System.out.println(method.getReturnType().getName() + " " + outer.getName() + "." + method.getName() + "(" + arrayToString(types) + ")");
}
System.out.println("--------------------------------------");
methods = outer.getDeclaredMethods();
for (Method method : methods) {
Class<?>[] types = method.getParameterTypes();
System.out.println(method.getReturnType().getName() + " " + outer.getName() + "." + method.getName() + "(" + arrayToString(types) + ")");
}Class<?> outer = Outer.class;
结果如下
- voidreflect.Outer.staticMethod()
- java.lang.Stringreflect.Outer.toString()
- voidreflect.Outer.wait(long)
- voidreflect.Outer.wait()
- voidreflect.Outer.wait(long,int)
- booleanreflect.Outer.equals(java.lang.Object)
- intreflect.Outer.hashCode()
- java.lang.Classreflect.Outer.getClass()
- voidreflect.Outer.notify()
- voidreflect.Outer.notifyAll()
- --------------------------------------
- voidreflect.Outer.privateMethod()
- voidreflect.Outer.protectedMethod()
- voidreflect.Outer.packageMethod()
- voidreflect.Outer.staticMethod()
- java.lang.Stringreflect.Outer.toString()
void reflect.Outer.staticMethod()
java.lang.String reflect.Outer.toString()
void reflect.Outer.wait(long)
void reflect.Outer.wait()
void reflect.Outer.wait(long,int)
boolean reflect.Outer.equals(java.lang.Object)
int reflect.Outer.hashCode()
java.lang.Class reflect.Outer.getClass()
void reflect.Outer.notify()
void reflect.Outer.notifyAll()
--------------------------------------
void reflect.Outer.privateMethod()
void reflect.Outer.protectedMethod()
void reflect.Outer.packageMethod()
void reflect.Outer.staticMethod()
java.lang.String reflect.Outer.toString()
调用getMethods时,Outer的方法只打印出了toString,其余结果均为父类Object的公有方法
调用getDeclaredMethods时,则只打印出在Outer中声明定义的方法
他们均打印出了公共的静态方法
所有带Declared和不带Declared的成对的方法都和上面的类似
如何利用反射执行一个方法,下面以void reflect.Outer.privateMethod()为例
- Methodmethod=outer.getDeclaredMethod("privateMethod");
- method.setAccessible(true);
- method.invoke(outerInstance,(Object[])null);
- method.setAccessible(false);
Method method = outer.getDeclaredMethod("privateMethod");
method.setAccessible(true);
method.invoke(outerInstance, (Object[])null);
method.setAccessible(false);
首先privateMethod是私有方法,所以为了能得到对应的Method对象,我们需要调用getDeclaredMethod
为了能访问私有方法,我们需要进行setAccessible(true)的设置
然后需要有个对象来执行,我们选择刚才的outerInstance
这时控制台输出
- privateMethod
privateMethod
干完活后,恢复其设置setAccessible(false)
下面是静态方法的执行
- Methodmethod=outer.getDeclaredMethod("staticMethod");
- method.setAccessible(true);
- method.invoke(Outer.class,(Object[])null);
- method.setAccessible(false);
Method method = outer.getDeclaredMethod("staticMethod");
method.setAccessible(true);
method.invoke(Outer.class, (Object[])null);
method.setAccessible(false);
ps:使用method.invoke(outerInstance, (Object[])null);也能执行,但是不该这样
内部类
isInterface,Field,isAccessible,setAccessible,getModifiers,Modifier.toString()
下面看看内部类的反射
- Class<?>[]classes=outer.getDeclaredClasses();
- for(Class<?>clazz:classes){
- if(clazz.isInterface()){
- System.out.println("interface:"+clazz.getName());
- }else{
- System.out.println("class:"+clazz.getName());
- }
- }
Class<?>[] classes = outer.getDeclaredClasses();
for (Class<?> clazz : classes) {
if(clazz.isInterface()){
System.out.println("interface : " + clazz.getName());
}else{
System.out.println("class : " + clazz.getName());
}
}
输出
- class:reflect.Outer$Inner
- class:reflect.Outer$StaticInner
- interface:reflect.Outer$TestInterface
class : reflect.Outer$Inner
class : reflect.Outer$StaticInner
interface : reflect.Outer$TestInterface
看来反射提供的api功能还是很全的
下面是如何反射静态内部类
- try{
- Class<StaticInner>staticInner=StaticInner.class;
- StaticInnerstaticInnerInstance=staticInner.newInstance();
- }catch(SecurityExceptione){
- e.printStackTrace();
- }catch(IllegalArgumentExceptione){
- e.printStackTrace();
- }catch(InstantiationExceptione){
- e.printStackTrace();
- }catch(IllegalAccessExceptione){
- e.printStackTrace();
- }
try {
Class<StaticInner> staticInner = StaticInner.class;
StaticInner staticInnerInstance = staticInner.newInstance();
}catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
也可以使用Class.forName方式,如果StaticInner是不可见的,那么上面的代码则无法编译,下面看看Class.forName
- try{
- Class<StaticInner>staticInner=(Class<StaticInner>)Class.forName("reflect.Outer.StaticInner");
- StaticInnerstaticInnerInstance=staticInner.newInstance();
- }catch(SecurityExceptione){
- e.printStackTrace();
- }catch(IllegalArgumentExceptione){
- e.printStackTrace();
- }catch(InstantiationExceptione){
- e.printStackTrace();
- }catch(IllegalAccessExceptione){
- e.printStackTrace();
- }catch(ClassNotFoundExceptione){
- e.printStackTrace();
- }
try {
Class<StaticInner> staticInner = (Class<StaticInner>) Class.forName("reflect.Outer.StaticInner");
StaticInner staticInnerInstance = staticInner.newInstance();
}catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
如果你和上面的写法一样,那么很不幸,运行时会得到如下错误
java.lang.ClassNotFoundException: reflect.Outer.StaticInner
at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:186)
at reflect.Test.main(Test.java:11)
我们去bin目录下可以发现这样的文件Outer$StaticInner.class
反射代码只需做如下改动
- Class<StaticInner>staticInner=(Class<StaticInner>)Class.forName("reflect.Outer$StaticInner");
Class<StaticInner> staticInner = (Class<StaticInner>) Class.forName("reflect.Outer$StaticInner");
.变成了$
这里newInstance之所以能执行,是因为StaticInner有默认构造器
下面看看StaticInner中生命了哪些变量
- Class<StaticInner>staticInner=StaticInner.class;
- Field[]fields=staticInner.getDeclaredFields();
- for(Fieldfield:fields){
- Class<?>type=field.getType();
- field.setAccessible(true);
- System.out.println(Modifier.toString(field.getModifiers())+""+type.getName()+""+field.getName());
- }
- StaticInnerStaticInnerInstance=staticInner.newInstance();
- Fieldfield=staticInner.getDeclaredField("TAG");
- booleanaccessible=field.isAccessible();
- if(!accessible){
- field.setAccessible(true);
- }
- System.out.println(field.get(StaticInnerInstance));
- field.set(StaticInnerInstance,"TAG");
- System.out.println(field.get(StaticInnerInstance));
- if(!accessible){
- field.setAccessible(false);
- }
Class<StaticInner> staticInner = StaticInner.class;
Field[] fields = staticInner.getDeclaredFields();
for (Field field : fields) {
Class<?> type = field.getType();
field.setAccessible(true);
System.out.println(Modifier.toString(field.getModifiers()) + " " + type.getName() + " " + field.getName());
}
StaticInner StaticInnerInstance = staticInner.newInstance();
Field field = staticInner.getDeclaredField("TAG");
boolean accessible = field.isAccessible();
if(!accessible){
field.setAccessible(true);
}
System.out.println(field.get(StaticInnerInstance));
field.set(StaticInnerInstance, "TAG");
System.out.println(field.get(StaticInnerInstance));
if(!accessible){
field.setAccessible(false);
}
结果如下
- privatejava.lang.StringTAG
- StaticInner
- StaticInnerTAG
- TAG
private java.lang.String TAG
StaticInner
StaticInnerTAG
TAG
StaticInner只声明了一个变量,名字为TAG,类型为String
即使是private变量,设置setAccessible(true)之后也能改变其值
普通内部类的反射和上面类似
最后说下匿名内部类
匿名内部类的作用是仅仅使用一次,所以也没有必要进行反射,实际上也很难进行反射
例子中有两个匿名内部类
- TestInterfaceti=newTestInterface(){
- publicStringtest(){
- return"thisisti";
- }
- };
- publicStringtoString(){
- System.out.println("OutertoString");
- returnnewTestInterface(){
- publicStringtest(){
- return"thisisatest!";
- }
- }.test();
- }
TestInterface ti = new TestInterface() {
public String test() {
return "this is ti";
}
};
public String toString(){
System.out.println("Outer toString");
return new TestInterface() {
public String test() {
return "this is a test!";
}
}.test();
}
在bin目录下发现有Outer$1.class和Outer$2.class
getDeclaredClasses发现也没有匿名内部类的信息
- try{
- Class<Outer>outer=Outer.class;
- Class<?>[]classes=outer.getDeclaredClasses();
- for(Class<?>clazz:classes){
- System.out.println(clazz.getName());
- }
- }catch(SecurityExceptione){
- e.printStackTrace();
- }catch(IllegalArgumentExceptione){
- e.printStackTrace();
- }
try {
Class<Outer> outer = Outer.class;
Class<?>[] classes = outer.getDeclaredClasses();
for (Class<?> clazz : classes) {
System.out.println(clazz.getName());
}
}catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
结果
- reflect.Outer$Inner
- reflect.Outer$StaticInner
- reflect.Outer$TestInterface
reflect.Outer$Inner
reflect.Outer$StaticInner
reflect.Outer$TestInterface
只有这三个,包括一个接口
如果非要对匿名内部类进行反射,也不是不可能
下面演示如何反射一个匿名内部类,仅仅是演示,没有任何意义,也不推荐这么做
我们以上面的toString方法里面的内部类做例子
- try{
- Class<?>clazz=Class.forName("reflect.Outer$2");
- Constructor<?>[]constructors=clazz.getDeclaredConstructors();
- constructors[0].setAccessible(true);
- Class<?>[]types=constructors[0].getParameterTypes();
- for(Class<?>type:types){
- System.out.println("ParameterTypes:"+type.getName());
- }
- TestInterfacetestInterface=(TestInterface)constructors[0].newInstance(newOuter(""));
- Method[]methods=clazz.getDeclaredMethods();
- for(Methodmethod:methods){
- Class<?>[]parameterTypes=method.getParameterTypes();
- System.out.println(method.getName()+"("+arrayToString(parameterTypes)+")");
- }
- Methodmethod=clazz.getDeclaredMethod("test");
- Stringstring=(String)method.invoke(testInterface);
- System.out.println(string);
- }catch(SecurityExceptione){
- e.printStackTrace();
- }catch(IllegalArgumentExceptione){
- e.printStackTrace();
- }catch(ClassNotFoundExceptione){
- e.printStackTrace();
- }catch(IllegalAccessExceptione){
- e.printStackTrace();
- }catch(InvocationTargetExceptione){
- e.printStackTrace();
- }catch(InstantiationExceptione){
- e.printStackTrace();
- }catch(NoSuchMethodExceptione){
- e.printStackTrace();
- }
try {
Class<?> clazz = Class.forName("reflect.Outer$2");
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
constructors[0].setAccessible(true);
Class<?>[] types = constructors[0].getParameterTypes();
for (Class<?> type : types) {
System.out.println("Parameter Types:" + type.getName());
}
TestInterface testInterface = (TestInterface) constructors[0].newInstance(new Outer(""));
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
Class<?>[] parameterTypes = method.getParameterTypes();
System.out.println(method.getName() + "(" + arrayToString(parameterTypes) + ")");
}
Method method = clazz.getDeclaredMethod("test");
String string = (String)method.invoke(testInterface);
System.out.println(string);
}catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
结果如下
- ParameterTypes:reflect.Outer
- Testing..
- Outer
- test()
- thisisatest!
Parameter Types:reflect.Outer
Testing..
Outer
test()
this is a test!
下面看看代码都做了什么,依次说明
首先获取Outer$2的Class
然后获取其构造器,查看所需要的参数,我们发现内部匿名类也需要一个外部类的支持
然后我们newInstance生成实例,声明为一个接口,由于要传入外部类,这里我们直接new一个,所以会初始化外部类,打印第2,3句
接下来查看内部类的方法,找到test(),这是第4句打印的结果
最后用实例化好的内部匿名类执行这个方法,所以打印出了最后一句
执行第一个匿名内部类的方法代码简单些,同样不推荐这么做
- try{
- Class<Outer>clazz=Outer.class;
- Outero=newOuter("");
- //System.out.println(o.ti.test());
- Fieldfield=clazz.getDeclaredField("ti");
- TestInterfacetestInterface=(TestInterface)field.get(o);
- System.out.println(testInterface.test());
- }catch(SecurityExceptione){
- e.printStackTrace();
- }catch(IllegalArgumentExceptione){
- e.printStackTrace();
- }catch(NoSuchFieldExceptione){
- //TODOAuto-generatedcatchblock
- e.printStackTrace();
- }catch(IllegalAccessExceptione){
- e.printStackTrace();
- }
try {
Class<Outer> clazz = Outer.class;
Outer o = new Outer("");
//System.out.println(o.ti.test());
Field field = clazz.getDeclaredField("ti");
TestInterface testInterface = (TestInterface) field.get(o);
System.out.println(testInterface.test());
}catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
为了演示反射,这里没有使用System.out.println(o.ti.test());
至此,反射演示结束
由于写本篇blog的时候多次修改代码,所以上面可能有些地方不太一致,如果发现,敬请指正