java学习脚印:Class类与反射机制

来源:互联网 发布:java线程挂起原因 编辑:程序博客网 时间:2024/05/16 21:13

java学习脚印:Class类与反射机制


权利声明: 为了又好又快的学习,本文部分内容节选自网络,已经注明转载出处。


1.反射的引入——为什么使用反射及反射的使用场所

能够分析类能力的程序被成为反射(reflective)。反射机制能够:

  • 在运行中分析类的能力
  • 在运行中查看对象,例如,编写一个toString方法供所有类使用。
  • 实现数组的操作代码
  • 利用Method对象,实现类似C++中函数指针功能。

     节选自[1]。

总之反射机制是一种功能强大复杂的机制。例如,我们在使用eclipse编辑器开发java应用时,系统自动提示哪个类具有哪些方法,某个方法具有哪些参数,这都是通过反射机制获取的类信息。


2.反射机制的原理


2.1 反射的基础——java.lang.Class类

java中的反射主要依赖于java.lang.Class类的诸多方法。下面是来自官网的关于Class类的API解释:

Class Class<T>

Instances of the class Class represent classes and interfaces in a running Java application. An enum is a kind of class and an annotation is a kind of interface. Every array also belongs to a class that is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions. The primitive Java types (boolean,byte,char,short,int,long, float, and double), and the keywordvoid are also represented asClass objects.


主要表达3点意思:

1) Class类实际上也是一个java类,不要把它与关键字class混淆。这个类是众多类或者接口的抽象,因为他们都包含名字,方法等公共属性(如可通过Class类的getName方法获取Class所代表的实体的名字),因此可以抽象出一个Class类来表达关于类或者接口的信息。java虚拟机正是利用Class类来保存每个对象所属的那个类的信息,以便利用运行时信息选择相应的方法执行。

2) Class类实例代表是java应用中的类或者接口的信息,包括枚举类型(类类型),注释(接口类型);数组类型也有对应的Class对象,并且同种元素类型和维度的数组共享同一个Class对象。

3) 像int,long等java基础类型以及关键字void也有相应的Class对象。


2.2  如何获取Class 对象


一共有三种方法可以获取某个类的Class对象,注意由于Class引入了泛型,因此需要加上类型参数,这里我们使用?通配符简化处理。

1) 通过对象的getClass方法获取

MyClass object;

 Class<?> cl = object.getClass();

2) 通过类名字符串获取

String className = "java.lang.String"

Class<?> cl = Class.forName(className)

其中 className是完整的类名,如果类在一个包里,则包的名字也作为类名的一部分。

3) 通过类型名来获取

Class<?> cl = int.class;

Class<?> cl = Date.class;

Class类中有诸多方法,例如获取Class所代表的类,接口等实体的名字,构造函数,方法,参数等的方法。

下面做一个简单示例,这个实例打印出指定类的所有公有方法。

代码清单2-1 ReflectionDemo1.java

package com.learningjava;import java.lang.reflect.Method;/** * simple use of reflection  * @author wangdq */public class ReflectionDemo1 {public static void main(String[] args) throws ClassNotFoundException {Class<?> cl = Class.forName("java.util.Date");Method[] methods = cl.getMethods();//get entity nameSystem.out.println("Class name: "+cl.getName());//enum methodsfor(Method method : methods){    System.out.println("method = " + method.getName());}}}

输出:

Class name: java.util.Date
method = parse
method = after
method = before
method = getDate
method = UTC
method = getDay
method = getHours
method = getMinutes
...(略去更多行)



2.3 反射的几个部分

通过反射机制,可以实现一下部分的功能:

  • 获取关于Class所代表的类,接口或基础类型的实体名字;
  • 获取类或者方法或者域的修饰符,例如public,private等
  • 获取包信息
  • 获取实现接口的信息
  • 获取构造器,其他各种方法以及方法的参数
  • 获取注释(Annotation)的信息
  • 获取泛型,数组等的信息
  • 获取动态代理,类的加载等信息

下面重点讲解一下获取构造器、域及方法的实现,关于注释以及动态代理将另文详述。


只要有了java.lang.Class类 的对象,就可以通过其中的方法来获取到该类中的构造方法、域和方法。

获取这三部分信息有两个版本的API:

  • 对应的方法分别是getConstructor、getField和getMethod。这一版本的特点是获取这个类或者超类的公有域或者方法(getFields和getMethods),以及所有公有构造器(getConstructors)。
  • 相应的getDeclaredXXX版本,这一版本的特点就是获取这个类的全部域(getDeclaredFields),获取这个类或者接口的全部方法但不包括从超类继承而来的方法(getDeclaredMethods),或所有构造器(getDeclaredConstructors)。

Constructor、Field和Method这三个类分别表示类中的构造方法、域和方法。这些类中的方法可以获取到所对应结构的元数据(节选自[2])。本部分代码片段节选自http://tutorials.jenkov.com[3]。


1) 获取公有构造器

通过java.lang.reflect.Constructor类,我们利用反射获取类的构造器,从而可以动态创建对象。

Class类的getConstructor方法有两个版本:

Constructor<T>getConstructor(Class<?>... parameterTypes)

Returns a Constructor object that reflects the specified public constructor of the class represented by thisClass object.
Constructor<?>[]getConstructors()
Returns an array containing Constructor objects reflecting all the public constructors of the class represented by thisClass object.

通过指定参数类型,第一个版本获取指定参数的公有构造器,例如指定一个String类型的参数获取指定构造器:

Class aClass = ...//obtain class objectConstructor constructor =        aClass.getConstructor(new Class[]{String.class});

第二个版本获取公有构造器数组:

Class aClass = ...//obtain class objectConstructor[] constructors = aClass.getConstructors();

获取构造器的参数类型:

Constructor constructor = ... // obtain constructor - see aboveClass[] parameterTypes = constructor.getParameterTypes();

使用构造器对象创建对象:

//get constructor that takes a String as argumentConstructor constructor = MyObject.class.getConstructor(String.class);MyObject myObject = (MyObject)        constructor.newInstance("constructor-arg1");


2) 获取公有

通过java.lang.reflect.Field类,我们可以获取类的实例域,并且可以通过get/set方法访问和修改他们。

Class类的getField方法有两个版本:

FieldgetField(String name)

Returns a Field object that reflects the specified public member field of the class or interface represented by thisClass object.
Field[]getFields()
Returns an array containing Field objects reflecting all the accessible public fields of the class or interface represented by thisClass object.

带参数的第一个版本的通过指定域名,获取指定公有的域:

Class  aClass = MyObject.classField field = aClass.getField("someField");


第二个版本获取域的数组:

Class aClass = ...//obtain class objectField[] methods = aClass.getFields();


域名及类型可获取如下:

Field field = ... //obtain field objectString fieldName = field.getName();
Object fieldType = field.getType();


获取和设置域的值:

Class  aClass = MyObject.classField field = aClass.getField("someField");MyObject objectInstance = new MyObject();Object value = field.get(objectInstance);field.set(objetInstance, value);

3)获取公有方法的信息

通过java.lang.reflect.Method类,利用反射可以获取类的方法,并且在运行时动态触发这些方法。

Class类的getMethods有两个版本,如下:

MethodgetMethod(String name, Class<?>... parameterTypes)

Returns a Method object that reflects the specified public member method of the class or interface represented by thisClass object.
Method[]getMethods()
Returns an array containing Method objects reflecting all the publicmember methods of the class or interface represented by thisClass object, including those declared by the class or interface and those inherited from superclasses and superinterfaces.

带参数的版本获取指定名称和参数类型的公有方法,例如获取名称为dosomething,带一个String参数的方法可以如下书写:

Class  aClass = ...//obtain class objectMethod method =    aClass.getMethod("doSomething", new Class[]{String.class});

不带参数的版本获取公有方法数组:

Class aClass = ...//obtain class objectMethod[] methods = aClass.getMethods();

注意无参数的方法调用时需要传递null作为类型说明:

Class  aClass = ...//obtain class objectMethod method =    aClass.getMethod("doSomething", null);


获取方法的参数类型和返回类型:

Method method = ... // obtain method - see aboveClass[] parameterTypes = method.getParameterTypes();
Class returnType = method.getReturnType();

利用Method对象调用方法:

//get method that takes a String as argumentMethod method = MyObject.class.getMethod("doSomething", String.class);Object returnValue = method.invoke(null, "parameter-value1");


Method invoke方法签名为:

Object invoke(Object obj, Object... args)


这里obj参数代表是你想要在其上调用方法的对象,args代表方法调用需要的参数。如果是静态方法,使用null作为参数,如上代码;但是如果不是静态方法,则需要传递一个对象实例如MyObject。

4)获取私有方法

通过使用Class.getDeclaredField(String name) 或者Class.getDeclaredFields()方法来获取私有的域。下面代码片段获取了一个类的私有域:

public class PrivateObject {  //私有域  private String privateString = null;  public PrivateObject(String privateString) {    this.privateString = privateString;  }}PrivateObject privateObject = new PrivateObject("The Private Value");//获取私有域对象Field privateStringField = PrivateObject.class.            getDeclaredField("privateString");privateStringField.setAccessible(true);//获取私有域的值String fieldValue = (String) privateStringField.get(privateObject);System.out.println("fieldValue = " + fieldValue);


注意:通常java虚拟机不允许访问类的私有域和方法,但是通过调用Method.setAcessible(true)方法将打破这一局限,使用反射来访问私有域或者方法,即使这些域是private、protected或者是包的范围限制。当然利用通常代码则不允许这样操作。


利用Class.getDeclaredMethod(String name, Class[] parameterTypes)或者 Class.getDeclaredMethods()方法可以获取私有方法,


下面代码示例的获取私有方法:


public class PrivateObject {  private String privateString = null;  public PrivateObject(String privateString) {    this.privateString = privateString;  }  //私有方法  private String getPrivateString(){    return this.privateString;  }}PrivateObject privateObject = new PrivateObject("The Private Value");//获取私有方法对象Method privateStringMethod = PrivateObject.class.        getDeclaredMethod("getPrivateString", null);privateStringMethod.setAccessible(true);//调用私有方法String returnValue = (String)        privateStringMethod.invoke(privateObject, null);System.out.println("returnValue = " + returnValue);

3.反射的综合示例

下面通过上面介绍的方法,综合运用反射,来获取一个指定类的详细信息,包括域、构造器、和方法。

代码清单3-1 ClassInfoAnalyzer.java和ReflectionDemo2.java

1) 反射获取信息类 ClassInfoAnalyzer.java

package com.learningjava;import java.io.OutputStream;import java.io.PrintWriter;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.lang.reflect.Modifier;/** * List details about a given class * @author wangdq * 2014-2-25 */public class ClassInfoAnalyzer {/** * construct the analyzer * @param cl the given Class object * @param out the outputstream to write info into */ClassInfoAnalyzer(Class<?> cl,OutputStream out) {this.cl = cl;this.writer = new PrintWriter(out);}//write details of a given Class objectpublic void writeDetails() {writer.append("\n");writeHeadInfo();writer.append("\n{");writeBodyInfo();writer.append("\n}\n");writer.flush();writer.close();}//write head info private void writeHeadInfo() {String modiferStr = Modifier.toString(cl.getModifiers());String name = cl.getName();writer.append(modiferStr+" "+name);Class<?> superClass = cl.getSuperclass();//获取超类if(!superClass.equals(java.lang.Object.class)) {writer.append(" extends "+superClass.getName());}//获取接口Class<?>[] interfaces = cl.getInterfaces();if(interfaces.length >0) {writer.append(" implements ");for(int ix = 0;ix <interfaces.length;ix++) {writer.append(interfaces[ix].getName());if(ix+1 != interfaces.length) {writer.append(", ");}}}}//write the body infoprivate void writeBodyInfo() {writer.append("\nConstructors: \n\n\t");writeAllConsturctorsInfo(cl);writer.append("\nMethods: \n\n\t");writeAllMethodsInfo(cl);writer.append("\nFields: \n\n\t");writeAllFieldsInfo(cl);}//write all info about its constructorsprivate void writeAllConsturctorsInfo(Class<?> cl) {Constructor<?>[] consArray = cl.getDeclaredConstructors();for(Constructor<?> cons : consArray) {writeConstructorInfo(cons);writer.append("\n\t");}}//write all info about its fieldsprivate void writeAllFieldsInfo(Class<?> cl) {Field[] fields = cl.getDeclaredFields();for(Field field:fields) {writeFieldInfo(field);writer.append("\n\t");}}//write all info about its methodsprivate void writeAllMethodsInfo(Class<?> cl) {Method[] methods = cl.getDeclaredMethods();for(Method method:methods) {writeMethodInfo(method);writer.append("\n\t");}}//write a constructor's whole infoprivate void writeConstructorInfo(Constructor<?> cons) {String modiferStr = Modifier.toString(cons.getModifiers());String name = cons.getName();writer.append(modiferStr+" "+name);writeParametersInfo(cons.getParameterTypes());}//write a field infoprivate void writeFieldInfo(Field field) {String modiferStr = Modifier.toString(field.getModifiers());Class<?> type = field.getType();String typeStr = null;if(type.isArray()) {typeStr = type.getComponentType().getName()+"[]";}else {typeStr = type.getName();}String name = field.getName();writer.append(modiferStr+" "+typeStr+" "+name);}//write a method's whole infoprivate void writeMethodInfo(Method method) {String modiferStr = Modifier.toString(method.getModifiers());String name = method.getName();String returnType = method.getReturnType().getName();writer.append(modiferStr+" "+returnType+" "+name);writeParametersInfo(method.getParameterTypes());}//write a method 's parameters infoprivate void writeParametersInfo(Class<?>[] parameters) {writer.append("(");for(int ix = 0;ix <parameters.length;ix++) {if(parameters[ix].isArray()) {writer.append(parameters[ix].getComponentType().getName()+"[]");}else {writer.append(parameters[ix].getName());}if(ix+1 != parameters.length) {writer.append(", ");}}writer.append(");");}private Class<?> cl;private PrintWriter writer;}

2) 驱动测试类 ReflectionDemo2.java

package com.learningjava;import java.util.Scanner;/** * using reflection to list a class info * you can write the info into other OuputSream as you like *  * @author wangdq * 2014-2-25 */public class ReflectionDemo2 {public static void main(String[] args) {System.out.println("input a valid class name "+ "with full name(eg:java.lang.String)");Scanner scanner = new Scanner(System.in);String className = scanner.nextLine();scanner.close();try {ClassInfoAnalyzer analyzer = new ClassInfoAnalyzer(Class.forName(className),System.out);analyzer.writeDetails();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}


运行结果:

input a valid class name with full name(eg:java.lang.String)java.lang.Stringpublic final java.lang.String implements java.io.Serializable, java.lang.Comparable, java.lang.CharSequence{Constructors: public java.lang.String(byte[]);public java.lang.String(byte[], int, int);public java.lang.String(byte[], java.nio.charset.Charset);public java.lang.String(byte[], java.lang.String);public java.lang.String(byte[], int, int, java.nio.charset.Charset); java.lang.String(int, int, char[]); java.lang.String(char[], boolean);public java.lang.String(java.lang.StringBuilder);public java.lang.String(java.lang.StringBuffer);public java.lang.String(int[], int, int);public java.lang.String(char[], int, int);public java.lang.String(char[]);public java.lang.String(java.lang.String);public java.lang.String();public java.lang.String(byte[], int, int, java.lang.String);public java.lang.String(byte[], int);public java.lang.String(byte[], int, int, int);Methods: public boolean equals(java.lang.Object);public java.lang.String toString();public int hashCode();public volatile int compareTo(java.lang.Object);public int compareTo(java.lang.String);public int indexOf(java.lang.String, int);public int indexOf(int);public int indexOf(int, int);static int indexOf(char[], int, int, char[], int, int, int);public int indexOf(java.lang.String);public static java.lang.String valueOf(float);public static java.lang.String valueOf(double);public static java.lang.String valueOf(boolean);public static java.lang.String valueOf(char[], int, int);public static java.lang.String valueOf(char[]);public static java.lang.String valueOf(java.lang.Object);public static java.lang.String valueOf(char);public static java.lang.String valueOf(int);public static java.lang.String valueOf(long);public char charAt(int);private static void checkBounds(byte[], int, int);public int codePointAt(int);public int codePointBefore(int);public int codePointCount(int, int);public int compareToIgnoreCase(java.lang.String);public java.lang.String concat(java.lang.String);public boolean contains(java.lang.CharSequence);public boolean contentEquals(java.lang.StringBuffer);public boolean contentEquals(java.lang.CharSequence);public static java.lang.String copyValueOf(char[], int, int);public static java.lang.String copyValueOf(char[]);public boolean endsWith(java.lang.String);public boolean equalsIgnoreCase(java.lang.String);public static transient java.lang.String format(java.util.Locale, java.lang.String, java.lang.Object[]);public static transient java.lang.String format(java.lang.String, java.lang.Object[]);public [B getBytes();public [B getBytes(java.lang.String);public void getBytes(int, int, byte[], int);public [B getBytes(java.nio.charset.Charset); void getChars(char[], int);public void getChars(int, int, char[], int); int hash32();private int indexOfSupplementary(int, int);public native java.lang.String intern();public boolean isEmpty();public int lastIndexOf(int);public int lastIndexOf(int, int);public int lastIndexOf(java.lang.String, int);public int lastIndexOf(java.lang.String);static int lastIndexOf(char[], int, int, char[], int, int, int);private int lastIndexOfSupplementary(int, int);public int length();public boolean matches(java.lang.String);public int offsetByCodePoints(int, int);public boolean regionMatches(int, java.lang.String, int, int);public boolean regionMatches(boolean, int, java.lang.String, int, int);public java.lang.String replace(char, char);public java.lang.String replace(java.lang.CharSequence, java.lang.CharSequence);public java.lang.String replaceAll(java.lang.String, java.lang.String);public java.lang.String replaceFirst(java.lang.String, java.lang.String);public [Ljava.lang.String; split(java.lang.String);public [Ljava.lang.String; split(java.lang.String, int);public boolean startsWith(java.lang.String);public boolean startsWith(java.lang.String, int);public java.lang.CharSequence subSequence(int, int);public java.lang.String substring(int, int);public java.lang.String substring(int);public [C toCharArray();public java.lang.String toLowerCase();public java.lang.String toLowerCase(java.util.Locale);public java.lang.String toUpperCase(java.util.Locale);public java.lang.String toUpperCase();public java.lang.String trim();Fields: private final char[] valueprivate int hashprivate static final long serialVersionUIDprivate static final java.io.ObjectStreamField[] serialPersistentFieldspublic static final java.util.Comparator CASE_INSENSITIVE_ORDERprivate static final int HASHING_SEEDprivate transient int hash32}


通过本例,请仔细体会反射方法的使用。


4.参考资料

[1]: 《java核心技术: 卷一》第八版 机械工业出版社

[2]:  Java深度历险(七)——Java反射与动态代理 

       http://www.infoq.com/cn/articles/cf-java-reflection-dynamic-proxy

[3]:  Java Reflection Tutorial  http://tutorials.jenkov.com/java-reflection/index.html

0 0
原创粉丝点击