Technorati 标记:  java, reflection, class<T>

    承接着第一篇 如何获取class对象 , 这一节将继续谈到如何获取class对象的信息。class对象信息大致可分为两类:(1)class对象的信息;(2)class对象的成员信息。class对象的信息主要修饰修饰符,泛型参数,所实现的接口,继承的路径与及注解信息。class对象的成员信息主要有成员变量field,函数方法与构造器。在进入正文之前,如果对类的信息了解的不多,可以参考java修饰符概述.

一、获取class对象信息

    获取class对象信息,主要是使用了一系列对应的函数的方法,在这里先做一个简单的介绍:

方法 描述
getCanonicalName() 获取class的类名
getModifiers() 获取class的修饰符
getTypeParameters() 获取class的泛型参数
getGenericInterfaces() 获取class实现的接口
getSuperclass() 获取class的父类,多次调用即可获得继承的路径
getAnnotations() 获取class的注解

    附:1、以上方法都是class对象的函数方法。方法名后带有“s“的,表示获取一个或多个的意思,都是以数组的形式返回。如”getTypeParameters()可能返回Type[]数组,其他的类似。注意的是”getmodifiers()“返回的是一个int。2、getAnnotations()并不能获取类的所有注解,只有实现了java.lang.reflect.AnnotatedElement的注解才可以被获得。如在预订的三个注解里@Override,@Deprecate,@SuppressWarning,只有@Deprecate可被获得。

    下面给一个Demo,演示如何遍历class对象的信息:

public class ClassDeclarationSpy {
	public static void main(String[] args) {
		try {
			Class<?> c = Class.forName("java.util.Date");
			out.println("类名:" + c.getCanonicalName());
			out.println("修饰符:" + Modifier.toString(c.getModifiers()));

			out.print("泛型参数: ");
			TypeVariable<?>[] tv = c.getTypeParameters();
			if (tv.length != 0) {
				for (TypeVariable<?> t : tv)
					out.print(t.getName() + " ");
				out.println();
			} else {
				out.println("无泛型参数");
			}

			out.print("实现的接口:");
			Type[] intfs = c.getGenericInterfaces();
			if (intfs.length != 0) {
				for (Type intf : intfs)
					out.println(" " + intf.toString());
			} else {
				out.println("无实现的接口");
			}

			out.print("继承路径:");
			List<Class<?>> l = new ArrayList<>();
			assembleAncestor(c, l);
			if (l.size() != 0) {
				for (Class<?> cl : l)
					out.println(cl.getCanonicalName());
			} else {
				out.println("无超类");
			}

			out.print("注解:");
			Annotation[] ann = c.getAnnotations();
			if (ann.length != 0) {
				for (Annotation a : ann)
					out.print(a.toString() + " ");
				out.format("%n");
				out.println();
			} else {
				out.println("无注解");
			}

		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

	private static void assembleAncestor(Class<?> c, List<Class<?>> l) {
		Class<?> ancestor = c.getSuperclass();
		if (ancestor != null) {
			l.add(ancestor);
			assembleAncestor(ancestor, l);
		}
	}
}


    其输出如下:

image

    类似的,可以获取java.util.List的信息,其输出如下:

image

     尝试去获取一个已经过时的class,如java.io.StringBufferInputStream

image

二、获取class对象的成员信息

    class对象的成员信息主要有成员变量,函数方法与构造器。与获取class对象信息类似,java提供了各种方法来获取成员信息——这些方法大概可以分两类,一类是直接获取所有的成员(变量,函数或者构造器)的方法,另外一类是搜索指定成员的方法。下面给出这些方法的概览:

1、获得与Class对象对应类的字段

Class API

继承的??

私有的??

getDeclaredField()

no

yes

getField()

yes

no

getDeclaredFields()

no

yes

getFields()

yes

no

    解释如下,“继承的?”代表了此方法是否可以获取从超类继承过来的filed值,“私有的”代表了此方法是否可以获取私有的field;如果方法含有“Declared”表示获取class对象自身含有的field值,如果没有,表示可以获取超类的field;如果方法名尾部含有“s”字样,表示此方法获取的是所有可以获取的field值返回一个Field<?>[]数组,反之,则是获取指定的field值,返回Field<T>。如“getDeclaredField()”表示获取当前class对象自身含有的field,此方法可以获取private修饰的field值,”getfields()”表示获取class对象所有public的field(包含从超累继承过来的),不可以获取private修饰的field。

2、获得与Class对象对应类的方法

Class API

继承的方法?

私有方法?

getDeclaredMethod()

no

yes

getMethod()

yes

no

getDeclaredMethods()

no

yes

getMethods()

yes

no

3、获得与Class对象对应类的构造方法

Class API

继承的?

私有的?

getDeclaredConstructor()

——

yes

getConstructor()

——

no

getDeclaredConstructors()

——

yes

getConstructors()

——

no

    因为构造器不能从超类继承过来,因此继承对构造器来说没有任何意义,其他的用法与获取field值得方法类似。

   Demo:建立两个类Student,Bachelor,如下

package com.other;

public class Student {
	private int id;
	private String sex;
	public String name;
	
	public Student() {
	}
	public Student(int id, String sex, String name) {
		super();
		this.id = id;
		this.sex = sex;
		this.name = name;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
 
package com.other;

public class Bachelor extends Student{
	private String major;
	
	public Bachelor() {
	}
	public Bachelor(String major) {
		super();
		this.major = major;
	}
}


    使用ClassHelp输出class对象的成员信息

public class ClassHelp {
	
	public static void main(String[] args) throws Exception {
		
			Student stu = new Bachelor("computer");
			Class<?> clazz = stu.getClass();
			//输出class的类名
			System.out.println("class类名:" + clazz.getName());
			//输出class所在的包
			System.out.println("class包:"+ clazz.getPackage());
			//获取指定的field
			System.out.println(clazz.getField("name"));
//			printMembers(clazz.getFields(), "Fields");
//			printMembers(clazz.getDeclaredConstructors(), "Constructors");
//			printMembers(clazz.getMethods(), "methods");
	}

	public static void printMembers(Member[] mbrs, String s) {
		//Field,Mehtod,constructor都是Member的实现
		out.println(s);
		for (Member mbr : mbrs) {
			if (mbr instanceof Field)
				out.println(((Field) mbr).toGenericString());
			else if (mbr instanceof Constructor)
				out.println( ((Constructor<?>) mbr).toGenericString());
			else if (mbr instanceof Method)
				out.println( ((Method) mbr).toGenericString());
		}
		if (mbrs.length == 0)
			out.println("不存在" +  s);
		out.println();
	}
}


    ClassHelp代码尽管简单,也演示了如何访问class对象成员的信息,其输出如下:

image

    另外三行注释代码的输出如下:

image

    对于Field、Constructors的输出应该没有什么问题,可Method的输出就多了很多“意外”的信息,其实这些都继承过来的方法——在java里,任意一个对象都是java,lang.Object的直接或间接的子类,所以getMethods()也相对应的输出里java。lang.Object的九种方法。

   方法toGenericString()返回描述此成员(Filed。Method,Constructor)的泛型描述,如果不存在,则调用toString()返回成员(Filed。Method,Constructor)字符串。