深入学习Java反射之道-class

来源:互联网 发布:程序员教程 pdf 编辑:程序博客网 时间:2024/04/30 07:32

深入学习Java反射之道-Class

反射常被用于在程序运行时检查或修改其执行行为。

为什么要使用反射

反射可以在下面几点帮助我们

  • 扩展功能,扩展已有类的功能
  • 类浏览器和可视化开发,类浏览器需要枚举类的成员,广泛应用于可视化开发环境
  • 调试和测试工具,调试器需要能够检查类的私有成员,测试工具会使用反射调用API。

反射的弊端

反射很强大,但是不要滥用,如果可以避免使用,尽量避免。当使用反射时,下面的问题需要注意。

  • 性能开销,反射会动态地处理对象,导致JVM的优化不能进行,因此,要避免在性能敏感地地方使用反射。
  • 安全限制,反射可能会与安全管理相互冲突。
  • 内部资源暴露,反射可以访问private方法,这可能会带来意料之外的副作用。

反射与类

java中的对象不是基本值类型就是引用类型,而所有的引用类型都继承自java.lang.Object

Java虚拟机为每个类生成一个不可变的java.lang.Class的类实例,该类实例提供了对象属性的运行期检查,包括了对象的成员和类型信息。java.lang.Class是所有反射操作的入口。

如何获取类实例

java.lang.Class是所有反射操作的入口。需要调用java.lang.Class的对应方法,以获取类实例。

Object.getClass()

如果可以获取到一个对象的实例,最简单的获取类实例的方法是调用该对象的getClass()方法。注意,这只是用于引用类型(getClass()方法继承自Object)。下面是几个例子:

  • 获取String的类实例
Class c = "foo".getClass();
  • 获取Enum的类实例
enum E{A,B}Class c= A.getClass();
  • 获取集合的类实例
Set<String> s = new HashSet<String>();Class c = s.getClass();
  • 获取数组的类实例
byte[] bytes = new byte[1024];Class c = bytes.getClass();

.class 语法

如果我们可以获得类型但是无法获取对应的对象,那么可以使用.class语法来获得类实例。这同时解决了基本值类型的类实例获取方法。

  • 获取基本值类型的类实例
boolean b;Class c = boolean.class;  // correct
  • 获取引用类型的类实例
Class c = java.io.PrintStream.class;
  • 获取数组类型的类实例
Class c = int[][][].class;

Class.forName()

如果可以获得一个类的fully-qualified name(包名+类名),那么可以通过static方法Class.forName()来获得对应的类实例。注意,基本值类型不能使用这个方法。

  • String
Class c = Class.forName("java.lang.String");
数组类型的Class.forName()
  • double一维数组
Class cDoubleArray = Class.forName("[D");

对应double[].class.getName()

  • String 二维数组
Class cStringArray = Class.forName("[[Ljava.lang.String;");

对应String[][].class.getName()

基本值类型通过包装类获取类实例

每个基本值类型和void都有自己对应的包装类(java.lang包中),因此可以通过包装类的TYPE属性来获取对应的封装类的类实例。

Class c = Double.TYPE;Class c = Void.TYPE;

Class的其它方法

Class.getSuperclass()

返回当前类实例的父类。

Class.getClasses()

返回Class<?>[],包含了当前类实例的所有public成员类,接口,枚举(包括继承的成员)的类实例。

Class<?>[] c = Character.class.getDeclaredClasses();/*** 数组c中会包含Character类中的3个public成员类* static class    Character.Subset* static class    Character.UnicodeBlock* static class    Character.UnicodeScript*/
Class.getDeclaredClasses()

返回Class<?>[],包含了当前类实例的所有(public, protected, friendlly, private )成员类,接口,枚举(包括继承的成员)的类实例。

Class<?>[] c = Character.class.getDeclaredClasses();/*** 数组c中会包含Character类中的3个public成员类和一个私有类* Character.Subset* Character.UnicodeBlock* Character.UnicodeScript* (private)Character.CharacterCache.*/
getDeclaringClass()
  • java.lang.Class.getDeclaringClass()
  • java.lang.reflect.Field.getDeclaringClass()
  • java.lang.reflect.Method.getDeclaringClass()
  • java.lang.reflect.Constructor.getDeclaringClass()

获取Class,Field,Method,Constructor声明所在类,注意,匿名内部类没有声明所在类。

Field f = System.class.getField("out");Class c = f.getDeclaringClass();//  c -> System.public class MyClass {    static Object o = new Object() {        public void m() {}     };    static Class<c> = o.getClass().getEnclosingClass();}//  o -> null.
Class.getEnclosingClass()

返回匿名内部类的封装类。

public class MyClass {    static Object o = new Object() {         public void m() {}     };    static Class<c> = o.getClass().getEnclosingClass();    // o -> MyClass}

类的标识符和类型检查

一个类有一个或多个修饰符,这些修饰符会影响运行期行为。

  • 访问权限修饰符
    • public
    • protected
    • friendly(默认包权限,也被称作default)
    • private
  • 重载修饰符
    • abstract
  • 静态修饰符
    • static
  • 不可修改修饰符
    • final
  • 强制严格浮点操作修饰符
    • strictfp
  • 注解

java.lang.reflect.Modifer包含了所有可能的修饰符的声明,也包含了解析Class.getModifiers()返回值的方法。

获取修饰符的例子

import java.lang.annotation.Annotation;import java.lang.reflect.Modifier;import java.lang.reflect.Type;import java.lang.reflect.TypeVariable;import java.util.ArrayList;import java.util.List;import static java.lang.System.out;/** * 引用自  * http://docs.oracle.com/javase/tutorial/reflect/class/classModifiers.html */public class ClassDeclarator {    public static void main(String... args) {        try {            Class<?> c = Class.forName(args[0]);            out.format("Class:%n  %s%n%n", c.getCanonicalName());            out.format("Modifiers:%n  %s%n%n",Modifier.toString(c.getModifiers()));            out.format("Type Parameters:%n");            TypeVariable[] tv = c.getTypeParameters();            if (tv.length != 0) {                out.format("  ");                for (TypeVariable t : tv)                    out.format("%s ", t.getName());                out.format("%n%n");            } else {                out.format("  -- No Type Parameters --%n%n");            }            out.format("Implemented Interfaces:%n");            Type[] intfs = c.getGenericInterfaces();            if (intfs.length != 0) {                for (Type intf : intfs)                    out.format("  %s%n", intf.toString());                out.format("%n");            } else {                out.format("  -- No Implemented Interfaces --%n%n");            }            out.format("Inheritance Path:%n");            List<Class> l = new ArrayList<Class>();            printAncestor(c, l);            if (l.size() != 0) {                for (Class<?> cl : l)                    out.format("  %s%n", cl.getCanonicalName());                out.format("%n");            } else {                out.format("  -- No Super Classes --%n%n");            }            out.format("Annotations:%n");            Annotation[] ann = c.getAnnotations();            if (ann.length != 0) {                for (Annotation a : ann)                    out.format("  %s%n", a.toString());                out.format("%n");            } else {                out.format("  -- No Annotations --%n%n");            }        } catch (ClassNotFoundException x) {            x.printStackTrace();        }    }    private static void printAncestor(Class<?> c, List<Class> l) {        Class<?> ancestor = c.getSuperclass();        if (ancestor != null) {            l.add(ancestor);            printAncestor(ancestor, l);        }    }}

探索类的成员

java.lang.Class中获取fields,methods和constructors的方法分为两种,一种是获取全部成员,将结果放入List;一种是获取名称对应的成员。
另外,根据成员的声明位置,获取成员的方法也分为两种,一种是直接获取类中所有声明的成员,一种是获取类的public成员(包括继承的成员)。

field & method & constructor

field
Class API List of members ? Inherited members ? Private members ? getDeclaredField() no no yes getField() no yes no getDeclaredFields() yes no yes getFields() yes no no
method
Class API List of members ? Inherited members ? Private members ? getDeclaredMethod() no no yes getMethod() no yes no getDeclaredMethods() yes no yes getMethods() yes no no
constructor
Class API List of members ? Private members ? getDeclaredConstructor() no yes getConstructor() no no getDeclaredContructors() yes yes getContructors() yes no

获取类的成员的代码示例

import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.lang.reflect.Member;import static java.lang.System.out;/** * 引用自 * http://docs.oracle.com/javase/tutorial/reflect/class/classMembers.html */enum ClassMember { CONSTRUCTOR, FIELD, METHOD, CLASS, ALL }public class ClassSpy {    public static void main(String... args) {    try {        Class<?> c = Class.forName(args[0]);        out.format("Class:%n  %s%n%n", c.getCanonicalName());        Package p = c.getPackage();        out.format("Package:%n  %s%n%n",               (p != null ? p.getName() : "-- No Package --"));        for (int i = 1; i < args.length; i++) {        switch (ClassMember.valueOf(args[i])) {        case CONSTRUCTOR:            printMembers(c.getConstructors(), "Constructor");            break;        case FIELD:            printMembers(c.getFields(), "Fields");            break;        case METHOD:            printMembers(c.getMethods(), "Methods");            break;        case CLASS:            printClasses(c);            break;        case ALL:            printMembers(c.getConstructors(), "Constuctors");            printMembers(c.getFields(), "Fields");            printMembers(c.getMethods(), "Methods");            printClasses(c);            break;        default:            assert false;        }        }    } catch (ClassNotFoundException x) {        x.printStackTrace();    }    }    private static void printMembers(Member[] mbrs, String s) {    out.format("%s:%n", s);    for (Member mbr : mbrs) {        if (mbr instanceof Field)        out.format("  %s%n", ((Field)mbr).toGenericString());        else if (mbr instanceof Constructor)        out.format("  %s%n", ((Constructor)mbr).toGenericString());        else if (mbr instanceof Method)        out.format("  %s%n", ((Method)mbr).toGenericString());    }    if (mbrs.length == 0)        out.format("  -- No %s --%n", s);    out.format("%n");    }    private static void printClasses(Class<?> c) {    out.format("Classes:%n");    Class<?>[] clss = c.getClasses();    for (Class<?> cls : clss)        out.format("  %s%n", cls.getCanonicalName());    if (clss.length == 0)        out.format("  -- No member interfaces, classes, or enums --%n");    out.format("%n");    }}