java反射必知必会

来源:互联网 发布:广东电信设计院知乎 编辑:程序博客网 时间:2024/05/19 07:42
        java反射在学习工作中时常使用,自己也利用反射做了一些工具(比如《利用反射打造自定义注解,自动校验或处理数据》),但一直对反射缺乏较为系统的了解。以下内容是慕课网教程《反射——Java高级开发必须懂的》的学习笔记,相关代码见github工程github.com/zxiaofan/JDK-Study,该项目主要用于学习JDK相关源码以及基础知识。当然,想要更为系统地直接地了解反射,还需要去研究源码,这也是以后必须要做的事情。

1、Class类的使用
1.1、面向对象世界里,万事外物皆对象。
起初,“面向对象”是专指在程序设计中采用封装、继承、多态等设计方法。
类是对象,类是java.lang.Class类的实例对象。
There is a class named Class:有一个类,其名字是Class, 其实例是每一个对象。

1.2、类的实例对象如何表示
// 私有构造方法,仅java虚拟机能创建Class的实例对象
    private Class(ClassLoader loader) {
        classLoader = loader;
    }
任何一个类都是Class的实例对象,这个实例对象有3种表示方式:

public class Class_Basic1 {     @SuppressWarnings("rawtypes")    public static void main(String[] args) {        // Fruit实例对象表示方式        Fruit fruit = new Fruit();        // 任何一个类都是Class的实例对象(成为该类的类类型),这个实例对象有3种表示方式:        // ① 任何一个类都有一个隐含的静态成员变量        Class c1 = Fruit.class;        // ② 类对象的getClass方法        Class c2 = fruit.getClass();        // c1/c2:Fruit类的类类型(class type)        System.out.println(c1 == c2); // true        // ③ forName        Class c3 = null;        try {            c3 = Class.forName("java1.lang.reflect.Fruit");        } catch (ClassNotFoundException e) {            e.printStackTrace();        }        System.out.println(c2 == c3); // true        // 通过类的类类型(c1、c2、c3)创建类的创建类的对象实例。        try {            Fruit fruit2 = (Fruit) c1.newInstance(); // 需要有无参的构造方法            fruit2.getColour();        } catch (Exception e) {            e.printStackTrace();        }    }} class Fruit {    void getColour() {        System.out.println("Hi colour");    }}



2、java动态加载类
Class.forName("类全称"):表示了类的类类型;代表了动态加载类。
静态加载类:编译时加载的类
如:new创建对象
动态加载类:运行时加载的类
假设有一个方法
getColour(String className){
Class c=Class.forName("指定水果");
IFruit fruit2 = (IFruit) c1.newInstance();
fruit2.getColour();
}
如果我们想获得不同水果的颜色(或者说根据不同的类调用不同的执行方法getColour),只需传入指定水果类(需实现IFruit接口,重写getColour方法)的类全称即可。


3、java获取方法信息
/** * 通过反射获取Class相关信息. * * 获取类信息,需先获取类的类类型 Class c = obj.getClass(); * * @author xiaofan */public class ClassInfoUtil extends TestCase {    /**     * 测试打印方法信息.     *     */    public void testPrintClassMethodInfo() {        Class c1 = int.class;        Class c2 = String.class;        Class c3 = void.class;        System.out.println(c1.getName()); // 类的全称(int)        System.out.println(c2.getName()); // java.lang.String        System.out.println(c2.getSimpleName()); // 不包含包名的类名称(String)        System.out.println(c3.getName()); // void        // 基本数据类型、void关键字,都存在类类型        String str = "zxiaofan.com";        System.out.println("=====打印  " + str.getClass().getName() + "  的类信息=====");        printClassMethodInfo(str);    }    /**     * 测试打印成员变量信息.     *     */    public void testFieldInfo() {        printFieldInfo(new Integer("1"));    }    /**     * 测试打印构造函数信息.     *     */    public void testConstructorInfo() {        printConstructorInfo(new Integer("1"));    }    /**     * 打印任意类的信息(类的成员函数、成员变量).     *     */    @SuppressWarnings("rawtypes")    public static void printClassMethodInfo(Object obj) {        // 获取类信息,需先获取类的类类型        Class c = obj.getClass();        // 获取类名称        System.out.println("类全称是:" + c.getName());        // Method类,方法对象,一个成员对象就是一个Method对象;        // getMethods()获取所有public的函数,包括父类继承而来的        // c.getDeclaredMethods()获取所有该类自己声明的方法(所有访问类型)        Method[] ms = c.getMethods(); // c.getDeclaredMethods()        System.out.println("类方法如下:");        for (int i = 0; i < ms.length; i++) {            Method method = ms[i];            // 方法返回值类型的类类型            Class returnType = method.getReturnType();            System.out.print("__" + returnType.getName() + " ");            // 方法名称            System.out.print(method.getName() + "(");            // 获取参数类型(参数列表的类型的类类型)            Class[] paramTypes = method.getParameterTypes();            for (Class class1 : paramTypes) {                System.out.print(class1.getName() + ",");            }            System.out.println(")");        }    }     /**     * 打印任意类的成员变量信息.     *     * 成员变量也是对象,java.lang.Field类封装了成员变量相关操作。     *     * getFields():所有public的成员变量;     *     * getDeclaredFields():该类自己声明的(即所有)成员变量信息。     *     */    @SuppressWarnings("rawtypes")    public static void printFieldInfo(Object obj) {        // 获取类信息,需先获取类的类类型        Class c = obj.getClass();        // Field[] fields=c.getFields();        Field[] fields = c.getDeclaredFields();        for (Field field : fields) {            // 成员变量的类型的类类型            Class fieldType = field.getType();            String typeName = fieldType.getName();            // 成员变量的名称            String fieldName = field.getName();            System.out.println(typeName + " " + fieldName);        }    }     /**     * 打印任意类的构造函数信息.     *     * 构造函数也是对象。java.lang.Constructor封装了构造函数信息     *     * getConstructors():所有public的构造函数     *     * getDeclaredConstructors():所有构造函数     *     * getEnclosingConstructor():类A构造函数定义了内部类InnerA,则通过InnerA的Class对象调用getEnclosingConstructor可获取类A的构造函数(不是构造函数列表)     *     */    @SuppressWarnings("rawtypes")    public static void printConstructorInfo(Object obj) {        // 获取类信息,需先获取类的类类型        Class c = obj.getClass();        // Constructor[] constructors = c.getConstructors();        // Constructor enclosingConstructor = c.getEnclosingConstructor();        Constructor[] constructors = c.getDeclaredConstructors();        for (Constructor constructor : constructors) {            System.out.print(constructor.getName() + " (");            Class[] paramTypes = constructor.getParameterTypes();            for (Class class1 : paramTypes) {                System.out.print(class1.getName() + ",");            }            System.out.println(")");        }    }}



4、通过反射调用指定方法
方法名和参数列表唯一决定某个方法(method.invoke(对象,参数列表))
public class ClassMethod extends TestCase {    /**     * 使用Method.invoke调用方法.     *     */    public void testInvokemethod() {        A a = new A();        Class c = a.getClass(); // 获取类的方法《--类信息《--类类型        // 方法:由名称和参数列表决定        // getMethod获取public方法        //        try {            // Method method = c.getDeclaredMethod("print", new Class[]{int.class, int.class});            Method method_int = c.getDeclaredMethod("print", int.class, int.class); // 等价于new Class[]{int.class, int.class}            // 方法method没有返回值则返回null,否则需要强转            Object invokeReturn = method_int.invoke(a, new Object[]{10, 20}); // 等价于 a.print(10,20);            System.out.println("强转:" + (int) invokeReturn);            System.out.println("==========");            Method method_String = c.getDeclaredMethod("print", String.class, String.class);            method_String.invoke(a, "hi", "zxiaofan"); // 即a.print("hi","zxiaofan")            System.out.println("==========");            // Method method_NoParam = c.getDeclaredMethod("print", new Class[]{});            Method method_NoParam = c.getDeclaredMethod("print"); // 等价于("print", new Class[]{})            method_NoParam.invoke(a, new Object[]{});            method_NoParam.invoke(a); // 等价于invoke(a, new Object[]{})        } catch (Exception e) {            e.printStackTrace();        }    }     /**     * 反射与泛型.     *     * 通过反射插入集合的非相同类型的数据,只能直接处理Object或对Object强转到实际类型。     */    @SuppressWarnings("rawtypes")    public void testGeneric() {        List list0 = new ArrayList<>();        List<String> list = new ArrayList<>();        list.add("hi");        // list.add(123); // 编译报错        Class c0 = list0.getClass();        Class c = list.getClass();        assertEquals(c0, c); // true        // c0==c1说明:编译之后集合的泛型是去泛型化的;java中的泛型是防止错误输入的,只在编译阶段有效,编译后无效        // 通过方法的反射绕过编译        Method method;        try {            method = c.getMethod("add", Object.class);            method.invoke(list, 456); // 绕过编译操作就绕过了泛型        } catch (Exception e) {            e.printStackTrace();        }        for (Object obj : list) { // 这样遍历是可以的            System.out.println(obj);        }        System.out.println("======");        for (String str : list) {            System.out.println(str); // 不能这样遍历,Iterator迭代也不行            // str为int时抛异常:java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String        }     }} class A {    public void print() { // 无参无返回值        System.out.println("hi zxiaofan.com");    }     public String print(String a, String b) { // 参数String返回String        String c = a + b;        System.out.println(c);        return c;    }     public int print(int a, int b) { // 参数int返回int        int c = a + b;        System.out.println(c);        return c;    }}



0 0