Java中的反射机制(Reflect)

来源:互联网 发布:windows密钥 编辑:程序博客网 时间:2024/05/20 22:26

一、Class类的使用

1、在面向对象的世界里,万事万物皆对象。

不过在java语言中,有两类事物不是面向对象的。它们分别是 java 中的基本数据类型(例如:int a = 5)和 java 中的静态成员(包括静态成员变量、静态成员方法)。其中,虽然普通数据类型不是对象,但是与之对应的包装类却是面向对象的,例如:int - Integer,boolean - Boolean 等。

2、类是谁的对象呢?

类是对象,类是 java.lang.Class 类的实例对象。(There is a class named Class)

假如有一个类 Person,那么可以用 new Person()来表示 Person 类的一个实例对象。
其实在 Java 中,Person 类本身也是一个类的实例对象,这个类就是 Class (java.lang.Class)。
也就是说,在 Java 中任何一个类都是 Class 的实例对象,只不过这些实例对象只能由 JVM 创建,可以理解为就是编译之后产生的一个个 .class 文件。

java.lang.Class的部分源码如下:

public final    class Class<T> implements java.io.Serializable,                              java.lang.reflect.GenericDeclaration,                              java.lang.reflect.Type,                              java.lang.reflect.AnnotatedElement {    ···    /*     * Constructor. Only the Java Virtual Machine creates Class     * 私有的构造函数,只有Java虚拟机可以创建Class     * objects.     */    private Class() {}    ···}

Person 的实例对象可以用 new Person( )来表示,那么 Class 的实例对象应该如何表示呢?
以 Person 类为例,表示 Class 的实例对象一般有以下三种方式:

a、类名.class
Class c1 = Person.class;

实际在告诉我们任何一个类都有一个隐含的静态成员变量 class

b、该类的已知对象.getClass()
Person p = new Person();Class c2 = p.getClass();

小结:
  这里 p 代表 Person 类的一个实例对象, c1 、c2 则分别代表 Class 类的实例对象。
  官网描述为:c1、c2 表示了 Person 类的类类型(class type)。
  
  万事万物皆对象,类也是对象,是Class类的实例对象,这个对象我们称为该类的类类型。
  
  c1、c2都代表了 Person 类的类类型,c1 == c2 为true说明一个类只可能是 Class 类的一个实例对象

c、Class.forName(“类的完整包名路径”)
try {    Class<?> c3 = Class.forName("com.example.Person");    System.out.println(c3);} catch (ClassNotFoundException e) {    e.printStackTrace();}

我们完全可以通过类的类类型来创建该类的对象实例。

try {    Person person = c1.newInstance(); // 【注意】需要有无参数的构造方法    person.print();} catch (Exception e) {    e.printStackTrace();}

完整的代码:

public class MainClass {    public static void main(String[] args) {        // Person是一个类,它的实例对象如何表示?        Person p = new Person();    // p 就代表了Person类的一个实例对象        // 其实,Person类也是一个类的实例对象,这个类就是 Class (java.lang.Class)        // 在Java中,任何一个类都是Class的实例对象,只不过这些实例对象只能由JVM创建,也就是编译后产生的一个个 .class 文件        // 那么Person作为Class的一个实例对象,应该如何来表示呢?        // 第一种方式        Class<? extends Person> c1 = p.getClass();        System.out.println(c1);        System.out.println(c1.getSuperclass()); // 这里印证了Class的父类其实还是Object        // 第二种方式        Class<? extends Person> c2 = Person.class; // 任何一个类都有一个隐含的静态成员变量class        System.out.println(c2);        System.out.println(c1 == c2);        // 第三种方式        try {            Class<?> c3 = Class.forName("com.example.Person");            System.out.println(c3);        } catch (ClassNotFoundException e) {            e.printStackTrace();        }        // 通过类的类类型来创建该类的对象实例        try {            Person person = c1.newInstance(); // 【注意】需要有无参数的构造方法            person.print();        } catch (Exception e) {            e.printStackTrace();        }    }}class Person {    void print() {        System.out.print("person ----------------");    }}

二、基本数据类型

java中的基本数据类型(boolean、byte、char、short、int、long、float、double)以及void关键字等都存在类类型。

public class TypeClass {    public static void main(String[] args) {        Class c1 = int.class;        Class c2 = Integer.class;        Class c3 = double.class;        Class c4 = Double.class;        Class c5 = void.class;        Class c6 = Void.class;        System.out.println("int.class     = " + c1.getName());        System.out.println("Integer.class = " + c2.getName());        System.out.println("double.class  = " + c3.getName());        System.out.println("Double.class  = " + c4.getName());        System.out.println("void.class    = " + c5.getName());        System.out.println("Void.class    = " + c6.getName());    }}

执行后的打印日志:

int.class     = intInteger.class = java.lang.Integerdouble.class  = doubleDouble.class  = java.lang.Doublevoid.class    = voidVoid.class    = java.lang.Void

getName( )

获取类类型的名称

getSimpleName( )

获取不带包名的类类型名称

Class c = Integer.class;System.out.println("c.getName() = " + c.getName()); // c.getName() = java.lang.IntegerSystem.out.println("c.getSimpleName() = " + c.getSimpleName()); // c.getSimpleName() = Integer

三、动态加载类

Class.forName(“类的完整包名路径”)

不仅表示了类的类类型,还代表了动态加载类。
编译时刻加载类是静态加载类,运行时刻加载类是动态加载类。

静态加载示例:

public class Office {    public static void main(String[] args) {        start("Word");    }    /**     * new 创建对象是静态加载类,在编译时刻就需要加载所有可能使用到的类。     */    private static void start(String arg) {       if ("Word".equals(arg)) {            Word w = new Word();            w.start();        }        if ("Excel".equals(arg)) {            Excel e = new Excel();            e.start();        }    }}

上边示例代码其实只使用到了 Word 类,但是因为是静态加载,在编译时期就需要加载所有可能使用到的类。所以,当只提供了 Word 类的情况下,因为没有 Excel 类,在编译时期就会检测到 Excel 类不存在的异常,导致程序其实根本用不到 Excel 类的情况下,也会因为编译时期报错而导致异常。

动态加载示例:

public class OfficeBetter {    public static void main(String[] args) {        run("Word");        run("Excel");    }    /**     * 根据文件类型动态加载对应的类,并执行其中的 start() 方法     * @param name 代表文件的类型     */    private static void run(String name) {        try {            Class<?> c1 = Class.forName("com.example.office." + name);            // 这里因为动态加载,无法确定加载的类是Word还是Excel,所以抽出一个接口类 OfficeAble            // 让 Word 和 Excel 都实现 OfficeAble 并实现 start() 方法即可            OfficeAble o = (OfficeAble) c1.newInstance();            o.start();        } catch (Exception e) {            e.printStackTrace();        }    }}

这里即使我们只使用Word类,也不会出现静态加载所产生的问题。同时如果后期需要添加新的功能,比如加入一个 PPT 的类并要执行一定的逻辑,这时不需要改动 OfficeAble 中的代码逻辑,只需要创建出 PPT 类并实现 OfficeAble 接口,将相应的逻辑功能在 start( )方法中实现即可。这就是动态加载相对的一些优势。

四、获取方法信息

方法也是对象,是 java.lang.reflect.Method 的实例对象, Method 类封装了关于类方法的操作。

先通过上面三种获取类类型的任意一种方式获取到类类型,例如:

String s = "abc";Class c = s.getClass();

Class.getMethods()

Method[] methods = c.getMethods();  // 【注意】获取的是所有public的方法,包括从父类继承而来的

Class.getDeclaredMethods()

Method[] methods = c.getDeclaredMethods(); // 【注意】获取的是所有该类自己声明的方法,不问访问权限(不包含父类继承来的方法)

Method常用方法

method.getReturnType().getSimpleName(); // getReturnType() 得到方法返回值类型的类类型method.getName();   // getName() 得到方法的名称Class<?>[] paramTypes = method.getParameterTypes(); // getParameterTypes() 得到方法参数列表类型的类类型

五、获取成员变量信息

成员变量也是对象,是 java.lang.reflect.Field 的实例对象, Field 类封装了关于成员变量的操作。

先通过上面三种获取类类型的任意一种方式获取到类类型,例如:

String s = "abc";Class c = s.getClass();

Class.getFields()

Field[] fields = c.getFields();  // 【注意】获取的是所有public的成员变量信息,包括从父类继承而来的

Class.getDeclaredFields()

Field[] fields = c.getDeclaredFields(); // 【注意】获取的是所有自己声明的成员变量信息

Field常用方法

Field[] fields = c.getFields();for (int i = 0; i < fields.length; i++) {    // 可以直接打印成员变量的信息    // System.out.println(fields[i].toString());    Class<?> type = fields[i].getType(); // getType() 获得成员变量类型的类类型    System.out.println(type.getSimpleName() + "  " + fields[i].getName());}

六、获取构造函数信息

构造函数也是对象,是 java.lang.reflect.Constructor 的实例对象, Constructor 类封装了关于构造函数的操作。

先通过上面三种获取类类型的任意一种方式获取到类类型,例如:

String s = "abc";Class c = s.getClass();

Class.getConstructors()

Constructor[] constructors = c.getConstructors();  // 【注意】获取的是所有public的构造函数

Class.getDeclaredConstructors()

Constructor[] declaredConstructors = c.getDeclaredConstructors(); // 【注意】获取所有的构造函数(不问访问权限,构造函数都是自己声明的)

Constructor常用方法

Constructor[] declaredConstructors = c.getDeclaredConstructors();for (int i = 0; i < constructors.length; i++) {    Constructor constructor = constructors[i];    String name = constructor.getName(); // getName() 获取构造函数名称    Class[] paramTypes = constructor.getParameterTypes(); // getParameterTypes() 获取构造函数参数列表类型的类类型}

七、方法反射的基本操作

八、通过反射了解泛型

本人能力有限,如果此博文中有哪里讲得让人难以理解,欢迎留言交流,若有讲解错的地方欢迎指出,大家互相学期,共同进步!

原创粉丝点击