您的位置:首页 > 职场人生

黑马程序员-JAVA-反射的一些特殊例子

2015-04-14 09:46 627 查看
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

上次已经稍稍了解过了反射的基本用法,那么在一些特殊的情况下,应该怎么取得对象的反射,又有何不同之处呢。

下面分别做一些简单的小例子来测试一下:

其中一些方法已经抽象提取,文章最后附其代码,这里是反射中用到的类以及接口,注解如下:

interface ITest {
final int val=87;

void todo();

int getnum(String val);

default int getsum(String val) {
return this.getnum(val) + 0xff;
}
static double getPI(){
return Math.PI;
}
}

class Outer{
@Anno(comm="Inner Class")
private class Inner implements ITest{
final int val=987;
@Override
public void todo(){
return;
}
@Override
public int getnum(String val){
return val.length();
}
}
@Anno(comm="Static Inner Class")
private static class StaticInner{
final int sval=877;

public void todo(){
return;
}

int getnum(String val){
return val.length();
}
}

@Annos({@Anno(comm="void func"),@Anno(comm="with AnonymousInnerClass")})
private void fun(){
new ITest(){

@Override
public void todo() {
return;
}

@Override
public int getnum(String val) {
return 0;
}
};
return;
}
@Repeatable(value = Annos.class)
@Retention(RetentionPolicy.RUNTIME)
@interface Anno{
String comm() default "rA";
}

@Retention(RetentionPolicy.RUNTIME)
static @interface Annos {
Anno[] value();
}
}


接口的反射

接口是不能取得实例的,那么getClass()这一途径就无效了。

private static void testInterface() {
Class<?> ci = ITest.class;
sp(ci);
showAnnotations(ci);
sp();

testConstructor(ci);
showFields(ci);
testMethods(ci);
}


输出如下:

interface com.itheima.ITest

Constructors Reflect Test:
.newInstance() Test:
unable to init newInstance
no Constructors

Field Reflect Test:
public static final int com.itheima.ITest.val
Value:87

Method Reflect Test:
public abstract void com.itheima.ITest.todo()
unable to invoke instance method by null Object
public default int com.itheima.ITest.getsum(java.lang.String)
unable to invoke instance method by null Object
public abstract int com.itheima.ITest.getnum(java.lang.String)
unable to invoke instance method by null Object
public static double com.itheima.ITest.getPI()
result:3.141592653589793


可以看出,除非能够明确获得一个实现接口的实例,否则无法使用任何实例字段和方法,静态的没有问题。还有通过反射,可以证明:接口是没有构造方法的,当然也无法通过.newInstance()产生实例。

注解的反射

注解的简单反射例子如下:

Class<?> ci = Outer.class;
sp(ci);
showAnnotations(ci);
sp();
Class<?>[] inners=ci.getDeclaredClasses();
for (Class<?> cls : inners) {
sp(cls);
showAnnotations(cls);
sp();
}

Method[] methods = ci.getDeclaredMethods();

if (methods.length == 0) {
sp("no Methods");
}
for (Method method : methods) {
sp(method.toGenericString());
showAnnotations(method);
}
sp();


输出如下:

class com.itheima.Outer

interface com.itheima.Outer$Anno
Annotation[@java.lang.annotation.Repeatable(value=interface com.itheima.Outer$An
nos):@java.lang.annotation.Retention(value=RUNTIME)]

interface com.itheima.Outer$Annos
Annotation[@java.lang.annotation.Retention(value=RUNTIME)]

class com.itheima.Outer$Inner
Annotation[@com.itheima.Outer$Anno(comm=Inner Class)]

class com.itheima.Outer$StaticInner
Annotation[@com.itheima.Outer$Anno(comm=Static Inner Class)]

private void com.itheima.Outer.fun()
Annotation[@com.itheima.Outer$Annos(value=[@com.itheima.Outer$Anno(comm=void fun
c), @com.itheima.Outer$Anno(comm=with AnonymousInnerClass)])]


可以看到注解在反射输出中与接口别无二致,但是可以通过isAnnotation()方法来判断是否注解以及使用isAnnotationPresent来判断当前元素是否存在注解。

内部类和静态内部类的反射

其实刚才的例子已经使用了内部类了,这里改写一下代码输出更详细的信息

代码如下:

private static void testInnerClass() {
Class<?> ci = Outer.class;
sp(ci);
sp();
Class<?>[] inners=ci.getDeclaredClasses();
for (Class<?> cls : inners) {
sp(cls);
showAnnotations(cls);
sp();
testConstructor(ci);
showFields(ci);
testMethods(ci);
}
}


输出结果:

class com.itheima.Outer

interface com.itheima.Outer$Anno
Annotation[@java.lang.annotation.Repeatable(value=interface com.itheima.Outer$An
nos):@java.lang.annotation.Retention(value=RUNTIME)]

Constructors Reflect Test:
.newInstance() Test:
get new instance:com.itheima.Outer@4554617c
com.itheima.Outer()
constructed new instance:com.itheima.Outer@74a14482

Field Reflect Test:

Method Reflect Test:
private void com.itheima.Outer.fun()
Annotation[@com.itheima.Outer$Annos(value=[@com.itheima.Outer$Anno(comm=void fun
c), @com.itheima.Outer$Anno(comm=with AnonymousInnerClass)])]

interface com.itheima.Outer$Annos
Annotation[@java.lang.annotation.Retention(value=RUNTIME)]

Constructors Reflect Test:
.newInstance() Test:
get new instance:com.itheima.Outer@135fbaa4
com.itheima.Outer()
constructed new instance:com.itheima.Outer@45ee12a7

Field Reflect Test:

Method Reflect Test:
private void com.itheima.Outer.fun()
Annotation[@com.itheima.Outer$Annos(value=[@com.itheima.Outer$Anno(comm=void fun
c), @com.itheima.Outer$Anno(comm=with AnonymousInnerClass)])]

class com.itheima.Outer$Inner
Annotation[@com.itheima.Outer$Anno(comm=Inner Class)]

Constructors Reflect Test:
.newInstance() Test:
get new instance:com.itheima.Outer@330bedb4
com.itheima.Outer()
constructed new instance:com.itheima.Outer@2503dbd3

Field Reflect Test:

Method Reflect Test:
private void com.itheima.Outer.fun()
Annotation[@com.itheima.Outer$Annos(value=[@com.itheima.Outer$Anno(comm=void fun
c), @com.itheima.Outer$Anno(comm=with AnonymousInnerClass)])]

class com.itheima.Outer$StaticInner
Annotation[@com.itheima.Outer$Anno(comm=Static Inner Class)]

Constructors Reflect Test:
.newInstance() Test:
get new instance:com.itheima.Outer@4b67cf4d
com.itheima.Outer()
constructed new instance:com.itheima.Outer@7ea987ac

Field Reflect Test:

Method Reflect Test:
private void com.itheima.Outer.fun()
Annotation[@com.itheima.Outer$Annos(value=[@com.itheima.Outer$Anno(comm=void fun
c), @com.itheima.Outer$Anno(comm=with AnonymousInnerClass)])]


无论是内部类还是静态内部类,或者内部定义注解,都能成功取得他们的信息并且成功创建实例来访问字段和方法。但是有一个问题,匿名内部类的信息不在getDeclaredClasses之中。

匿名内部类的反射

如何反射匿名内部类,为此特地搜索了一下,果断发现有前辈利用编译器命名规则来去匿名内部类的方法。毫不犹疑,试试看:

private static void testAnonymousInnerClass(Class outer) {
String outerName=outer.getCanonicalName();
Class<?> ci = null;
//尝试10次,如果需要可以尝试更多
for (int i = 0; i < 10; i++) {
ci=null;
try {
ci=Class.forName(outerName+"$"+i);
} catch (ClassNotFoundException e) {
//fail
continue;
}
if(ci!=null&&ci.isAnonymousClass()){
sp(ci);
showAnnotations(ci);
sp();
testConstructor(ci);
showFields(ci);
testMethods(ci);
}
}
}


传入Outer.class的测试结果:

class com.itheima.Outer$1

Constructors Reflect Test:
.newInstance() Test:
unable to init newInstance
com.itheima.Outer$1(com.itheima.Outer)
constructed new instance:com.itheima.Outer$1@2a139a55

com.itheima.Outer$1
Field Reflect Test:
final com.itheima.Outer com.itheima.Outer$1.this$0
Value:com.itheima.Outer@6d06d69c

com.itheima.Outer$1
Method Reflect Test:
public void com.itheima.Outer$1.todo()
public int com.itheima.Outer$1.getnum(java.lang.String)
result:0


前辈的方法果然有效,匿名内部类也无所遁形了。

这里是刚才调用到的几个遍历方法,字段,注解,构造方法的代码,以及一个小小的输出控制台代码

public static void showAnnotations(AnnotatedElement e) {
Annotation[] annotations = e.getAnnotations();
if (annotations.length > 0) {
sp(annotations);
}
}

public static void showFields(Class<?> ci) {
// get an instance for Fields
Object instance = tryGetInstance(ci);
Field[] flds=ci.getDeclaredFields();
sp("Field Reflect Test:");
for (Field field : flds) {
Object result=null;
sp(field.toGenericString())
d6dc
;
if (!field.isAccessible()) {
field.setAccessible(true);
}
try {
result=field.get(instance);
System.out.print("Value:");
sp(result);
} catch (IllegalArgumentException | IllegalAccessException e) {
sp("unable to get value");
}
}
sp();
}

/**
* 测试ci类的所有方法
* 参数使用@defaultValue
* 实例对象使用@tryGetInstance 为null时不尝试实例对象方法
* 输出到控制台
* @param ci 要测试的类
*/
public static void testMethods(Class<?> ci) {
// get an instance for Methods
Object instance = tryGetInstance(ci);
// get methods
Method[] methods = ci.getDeclaredMethods();

// test static Methods
sp("Method Reflect Test:");
if (methods.length == 0) {
sp("no Methods");
}
for (Method method : methods) {
sp(method.toGenericString());
showAnnotations(method);
//          Class<?>[] exceptionTypes = method.getExceptionTypes();
//          if (exceptionTypes.length > 0) {
//              sp(exceptionTypes);
//          }
if (isStatic(method)) {
tryInvoke(method, null);
} else if (instance != null) {
tryInvoke(method, instance);
} else {
sp("unable to invoke instance method by null Object");
}
}
sp();
}

/**
* 测试这个类的构造方法 包括使用Class.newInstance() 尝试反射调用无参数构造方法
* 尝试反射调用所有构造方法,其参数值使用@defaultValue(Class)
*
* @param ci
*            测试的类
*/
public static void testConstructor(Class<?> ci) {
sp("Constructors Reflect Test:");
// Class .newInstance
sp(".newInstance() Test:");
try {
Object o = ci.newInstance();
sp("get new instance:"+o);
} catch (InstantiationException | IllegalAccessException e) {
sp("unable to init newInstance");
}

// Constructors Reflect:
Constructor<?>[] cons = ci.getDeclaredConstructors();
if (cons.length == 0) {
sp("no Constructors");
}
for (Constructor<?> constructor : cons) {
sp(constructor.toGenericString());
Annotation[] annotations = constructor.getAnnotations();
if (annotations.length > 0) {
sp(annotations);
}
//          Class<?>[] exceptionTypes = constructor.getExceptionTypes();
//          if (exceptionTypes.length > 0) {
//              sp(exceptionTypes);
//          }
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
Parameter[] paras = constructor.getParameters();
Object[] initparas = new Object[paras.length];
for (int i = 0; i < paras.length; i++) {
initparas[i] = defaultValue(paras[i].getType());
}
try {
sp("constructed new instance:"
+ constructor.newInstance(initparas));
} catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
sp("unable to construct with default parameters");
}
}
sp();
}

public static boolean isStatic(Method method) {
return (method.getModifiers() & Modifier.STATIC) > 0;
}

/**
* 尝试用对象instance来invoke方法method
* 方法的参数列表使用@defaultValue生成
* 输出到控制台
* @param method 引发的方法
* @param instance 传入的object 保证其含有method方法
*/
public static void tryInvoke(Method method, Object instance) {
if (!method.isAccessible()) {
method.setAccessible(true);
}
Parameter[] paras = method.getParameters();
Object[] initparas = new Object[paras.length];
for (int i = 0; i < paras.length; i++) {
initparas[i] = defaultValue(paras[i].getType());
}
Object result = null;
try {
result = (method.invoke(instance, initparas));
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
sp("unable to invoke with default parameters");
}
if (result != null) {
System.out.print("result:");
sp(result);
}
}

/**
* 尝试构造一个此类的实例
* 首先尝试.newInstance()
* 然后按构造方法声明顺序尝试
* 参数将使用@defaultValue
* 一旦成功构造一个实例立即返回
* 失败返回null
* @param ci 需要构造的类
* @return ci的实例
*/
public static Object tryGetInstance(Class<?> ci) {
Object instance = null;
try {
instance = ci.newInstance();
if (ci.isInstance(instance)) {
return instance;
}
} catch (InstantiationException | IllegalAccessException e) {
}

Constructor<?>[] cons = ci.getDeclaredConstructors();
if (cons.length == 0) {
return null;
}
for (Constructor<?> constructor : cons) {
sp(constructor.getName());
Annotation[] annotations = constructor.getAnnotations();
if (annotations.length > 0) {
sp(annotations);
}
Class<?>[] exceptionTypes = constructor.getExceptionTypes();
if (exceptionTypes.length > 0) {
sp(exceptionTypes);
}
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
Parameter[] paras = constructor.getParameters();
Object[] initparas = new Object[paras.length];
for (int i = 0; i < paras.length; i++) {
initparas[i] = defaultValue(paras[i].getType());
}
try {
instance = constructor.newInstance(initparas);
if (ci.isInstance(instance)) {
return instance;
}
} catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
// ignore
continue;
}
}
return instance;
}

/**
* 取类的默认值
* 数组返回空数组
* 引用类型全部尝试.newInstance(),异常则返回null
* 基本类型中,整形返回0,浮点返回0.0,布尔返回false,void返回null
*
* @param cls
*            类型
* @return 默认值
*/
public static Object defaultValue(Class<?> cls) {
if (cls.isPrimitive()) {
switch (cls.getName()) {
case "byte":
case "char":
case "short":
case "int":
case "long":
return (byte) 0;
case "float":
case "double":
return 0.0f;
case "boolean":
return false;
case "void":
return null;
}
} else if (cls.isArray()) {
return Array.newInstance(cls.getComponentType(), 0);
} else {
try {
return cls.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
return null;
}
}
return null;
}

/**
* 简单的在控制台输出 对于非数组类型,输出.toString()
* 对于数组类型,输出:类型[内容]
* 不递归
* @param o
*            被打印的对象
*/
public static void sp(Object o) {
if (o == null) {
System.out.println("null");
}
if (o.getClass().isArray()) {
StringBuilder sb = new StringBuilder();
int len = Array.getLength(o);
sb.append(o.getClass().getComponentType().getSimpleName());
if (len == 0) {
sb.append("[]");
System.out.println(sb.toString());
return;
}
sb.append('[');
for (int i = 0; i < len; i++) {
if (i != 0) {
sb.append(':');
}
sb.append(Array.get(o, i));
}
sb.append(']');
System.out.println(sb.toString());
} else {
System.out.println(o);
}
}

public static void sp() {
sp("");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java