反射就是把java类中的各种属性映射到另一个java类中,本以为我对反射还学得挺不错的,后来做着做着实验,发现杯具了。这个实验就是把一个类的所有东西打印出来,是的,所有,包括方法是如何具体实现的,然后我就拿前一篇写的枚举那个Animals2的类进行反射,里面阿猫阿狗里面的内容反射不出来,本想用递归的,结果你懂的,一个类本身拥有五个自己的实例对象,但是当我用暴力反射的时候,每个类有五个自己的实例对象,五个实例对象自己各自也含有五个实例对象…….一直不断循环,结果虚拟机不干了,抛出个异常,但是如果只打印一般的信息,比如把field,method,package,classname之类的按顺序打印出来就相对简单很多,唯一麻烦的就是排版问题,先把我写的代码弄上来,再进一步解释:
public class ReflectAnimal2 {
public static void main(String[] args) throws ClassNotFoundException {
Class clazz = Class.forName("it.cast.heima.Animals2");
printAll(clazz);
}
//打印出所有字段的值,不包括父类的
private static void printFileds(Class clazz) throws ClassNotFoundException {
Field[] fields = clazz.getDeclaredFields();
String modName;
String fieldName;
Class typeName;
for(Field field: fields) {
field.setAccessible(true);
modName = Modifier.toString(field.getModifiers());
fieldName = field.getName();
typeName = field.getType();
System.out.print(" " + modName);
System.out.print(" " + typeName.getName());
System.out.println(" " + field.getName()+"");
}
}
//打印出所有东西(包名,类名,字段,方法,但写的时候忘了把构造函数弄上去了)
public static void printAll(Class clazz) throws ClassNotFoundException {
printPackage(clazz);
printClassName(clazz);
printFileds(clazz);
printMethod(clazz);
}
//打印出所有方法
private static void printMethod(Class clazz) {
Method[] methods= clazz.getDeclaredMethods();
String modName;
String fieldName;
Class retName;
for(Method method: methods) {
method.setAccessible(true);
modName = Modifier.toString(method.getModifiers());
fieldName = method.getName();
retName = method.getReturnType();
Type[] types = method.getParameterTypes();
System.out.print(" " + modName);
System.out.print(" " + retName.getName());
System.out.print(" " + method.getName()+"(");
StringBuffer sb = new StringBuffer();
for(Type type: types) {
sb = sb.append( type + ",");
}
if(sb.length() != 0) {
System.out.println(sb.substring(0, sb.length()-1) + ")");
continue;
}
System.out.println(sb +")");
}
}
//打印出类名
private static void printClassName(Class clazz) {
System.out.print(Modifier.toString(clazz.getModifiers()));
System.out.println(" " + clazz.getName()+ "{");
}
public static void printPackage(Class clazz) {
System.out.println(clazz.getPackage().toString());
}
}
打印结果如下:
package it.cast.heima
public it.cast.heima.Animals2{
private int age
public java.lang.String name
protected [I a
public static final it.cast.heima.Animals2 Dog
public static final it.cast.heima.Animals2 CAT
public static final it.cast.heima.Animals2 MONKEY
public static final it.cast.heima.Animals2 PERSON
public void scream()
private java.lang.String say(class java.lang.String,int)
那个枚举类我自己增加了一些字段,还增加了带参数的有返回值的一个方法(增加难度)。另外我发现上面这段代码漏掉了构造方法= =不过差不多,对于反射,基本上只要了解如何得到一个类的field字段,基本上想要反射其他内容,仿造一下做法就得了。所以就只解释下如何获得一个类的所有字段吧。首先你要加载要反射的类进虚拟机,要不然白搭。加载方法有三种,Class.forName(), ClassName.class或者在一个实例对象上调用它的getClass()方法。好了,类的字节码得到了,于是你可以发现在Class类文档上有多个get方法,这些都是返回以下该类的包名啊,修饰符啊,字段啊,方法啊等等等等,为了得到这个类的字段,你可以用getFields()得到所有字段,并且返回一个Field数组,然后你遍历的输出来看一下,就发现你得到只是公有的字段,而且还包括父类的(上面这个例子父类是object,没字段)。所以你可以只用getDeclaredFields()方法获得所有的字段,包括public,private,protect和默认的(什么也没写那种)修饰符,但不包括父类的,这个文档也有说明。获得所有字段还不行,你还得有访问权限,调用setAccessible方法,默认情况下是不给你访问的,你非要访问的话。强暴它一下就能得到了,这个是方老师视频上说的,我一边看还一边笑= =真的太风趣了,这个就是所谓暴力反射。。。好了,反射其他内容就不再多做讲解了,操作大同小异。
反射出来当然还要调用,要不反射出来就没意义了,你要想调用它的方法,或者该方法的实例对象,然后invoke,传个对象,传参数进去就得了。静态方法可以写null表示没有对象,无参数的话也是填null;你也可以先new一个对象出来,怎么new?获得构造方法,调用newInstance方法就行了,如果那个对象有无参构造函数,可以直接clazz.newInstance,这是sun为了方便咱们这些有经验的开发人员才弄这么个方法出来的^ ^
隔天补充:反射内部类问题,其实内部类反射大同小异,只是内部类的名字有点怪异,格式一般为:外部类名$内部类名.class,所以加载进来的时候要把类名写好。另外非静态内部类构造函数,编译器默认会带有一个参数,参数类型为外部类的实例对象的引用。其实可以这么理解,要是没有外部类的实例对象,也就new不出内部类的实例对象出来,所以内部类的构造方法才需要传递一个实例对象的引用进去。至于静态类,参数类型为null。