java之反射Reflect

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息,以及动态调用对象方法的功能称为java语言的反射机制。1.Class类的使用(1)java中普通的数据类型类和静态的成员不属于对象。(2)类也是对象,是java.lang.Class类的实例对象。(3)Class类本身是无法new自身的...
摘要由CSDN通过智能技术生成

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息,以及动态调用对象方法的功能称为java语言的反射机制。

1.Class类的使用

(1)java中普通的数据类型类和静态的成员不属于对象。

(2)类也是对象,是java.lang.Class类的实例对象。

(3)Class类本身是无法new自身的。因为可以从源码看到:

         Class类本身的构造方法是一个私有的构造方法,只有java虚拟机可以来调用。

(4)Class类的使用代码: 

package function.reflect;

/**
 * Class类的使用
 *
 * @author kimtian
 */
public class ClassUsed {
    public static void main(String[] args) {
        //Student 对象的实例
        Student student1 = new Student();
        //Student这个类也是一个实例对象,是Class类的实例对象
        //任意一个类都是Class的实例对象,这个实例对象有三种表示方式

        //第一种表示方式-->任何一个类都有一个隐含的静态成员变量class
        Class c1 = Student.class;
        System.out.println(c1);

        //第二种表达方式  已经知道该类的对象,通过getClass方法调用
        Class c2 = student1.getClass();
        //第三种表达方式
        Class c3 = null;
        try {
            c3 = Class.forName("function.reflect.Student");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println(c2 == c3);
        /**
         * c1,c2表示了Student类的类类型(ClassType)
         * 万事万物皆对象
         * 类也是对象,是Class类的实例对象
         * 这个对象我们称为该类的类类型
         */
        // 不管c1 or c2 都代表了foo类的类类型,一个类只可能是Class类的一个实例对象
        System.out.println(c1 == c2);

        //我们完全可以通过类的类类型创建该类的实例对象。-->通过c1 or c2 or c3创建Student的实例
        Student student2 = null;
        try {
            //需要有无参数的构造方法,否则会报错
            student2 = (Student) c1.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

class Student {
}

2.动态加载类

(1)从上面可以看到   Class.forName("类的全称")    可以用来创建类的实例。

         c1.newInstance();不仅表示了类的类类型,还代表了动态加载类;

         编译时刻加载类是静态加载类、运行时刻加载类是动态加载类,我们在做一些通过功能方法的时候最好使用动态加载类。

(2)静态加载类的实现:

         由于使用的静态加载类,如果我们没有Office类,那么在编译Office类的时候就会出错。     

         如果有一个类似于Word、Excel这样的类出错,全部其他的功能都使用不了。

package function.reflect;

/**
 * 如果有一个出问题,全部其他的功能也都使用不了
 * 每次增加新功能,要修改基础代码
 *
 * @author kimtian
 */
public class Office {
    public static void main(String[] args) {
        if ("Word".equals(args[0])) {
            //new创建对象是静态加载类,在编译时刻就需要加载所有可能使用到的类。
            //只想在运行哪个时候加载哪个,就要使用动态加载类
            Word word = new Word();
            word.start();
        }
        if ("Excel".equals(args[0])) {
            Excel excel = new Excel();
            excel.start();
        }
    }
}

(3)动态加载类的实现:

          使用动态加载类,如果要使用Excel,但是没有Excel类,则只在运行的时候才会报错。但不会影响其他的,如Word的使用。

          我们给定一个OfficeAble的通用接口,使Word、Excel等都实现该接口,可扩展性好,新增类似其他的功能时,只需要增加新的代码实现OfficeAble通用接口。不用重新修改编译这个功能基础类。

package function.reflect;

/**
 * 如果需要加其他OfficeAble的具体实现,也不需要重新修改编译这个类
 *
 * @author kimtian
 */
public class OffiiceBetter {
    public static void main(String[] args) {
        try {
            //动态加载类,在运行时刻加载
            Class c = Class.forName(args[0]);

            //通过类类型,创建该类对象
            OfficeAble oa = (OfficeAble) c.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

3.获取类的信息

(1)获取类的方法的信息

对类进行操作,通过class类获得类的所有方法信息。

  • getMethods获取的是所有的public的函数,包括父类继承而来的;
  • getDeclaredMethods获取的是所有该类自己声明的方法,不问访问权限(父类继承来的就没有了);
package function.reflect;

import java.lang.reflect.Method;

/**
 * Class类的基本操作
 * 通过Class类获取类的所有方法信息
 *
 * @author kimtian
 */
public class ClassOperating {
    /**
     * 打印类的信息,包括类的成员函数和成员变量
     *
     * @param object 对象
     */
    public static void printClassMessage(Object object) {
        //要获取类的信息  首先要获取类的类类型
        //传递是哪个子类的类类型,c就是该子类的类类型
        Class c = object.getClass();
        //获取类的名称
        System.out.println("类的名称是" + c.getName());
        /**
         * 获取所有的方法,万事万物皆对象,方法也是对象
         * 一个成员方法就是一个Method对象
         * getMethods获取的是所有的public的函数,包括父类继承而来的
         * getDeclaredMethods获取的是所有该类自己声明的方法,不问访问权限(父类继承来的就没有了)
         *  
         */
        Method[] ms = c.getMethods();
        Method[] ms1 = c.getDeclaredMethods();
        //得到方法的名称
        for (int i = 0; i < ms.length; i++) {
            //得到方法的返回值类型 --得到的是返回值类型的类类型
            Class returnType = ms[i].getReturnType();
            //得到返回值名字
            System.out.print(returnType.getName() + " ");
            //得到方法的名称
            System.out.print(ms[i].getName() + "(");
            //获取参数的类型-->得到的是参数列表的类型的类类型
            Class[] paramTypes = ms[i].getParameterTypes();
            for (int j = 0; j < paramTypes.length; j++) {
                //为了美观,最后一个返回值不加标点符号
                if (j == paramTypes.length - 1) {
                    System.out.print(paramTypes[j].getSimpleName());
                } else {
                    System.out.print(paramTypes[j].getSimpleName() + ",");
                }
            }
            System.out.print(")");
            System.out.println();
        }

    }

    public static void main(String[] args) {
        String s = "hello world";
        //得到String类的所有类型
        ClassOperating.printClassMessage(s);
        //得到Integer类的所有类型
        Integer i = 1;
        ClassOperating.printClassMessage(i);
    }
}

(2)获取成员变量信息

对类进行操作,通过class类获得类的所有成员变量的信息。

  • getFields()方法获取的是所有的public的成员变量的信息
  • getDeclaredFields()获取的是所有该类自己声明的成员变量的信息
package function.reflect;

import java.lang.reflect.Field;

/**
 * Class类的基本操作
 * 通过Class类获取类的所有成员变量
 * @author kimtian
 */
public class ClassOperateTwo {
    /**
     * 打印成员变量的信息
     *
     * @param object 对象
     */
    public static void printFieldMessage(Object object) {
        Class c = object.getClass();
        /**
         * 成员变量也是对象
         * java.lang.reflect.Field中封装了关于成员变量的操作
         * getFields()方法获取的是所有的public的成员变量的信息
         * getDeclaredFields()获取的是所有该类自己声明的成员变量的信息
         */
        Field[] fs = c.getFields();
        Field[] fs1 = c.getDeclaredFields();
        for (Field field : fs1) {
            //得到成员变量的类型的类类型
            Class filedType = field.getType();
            //得到成员变量类型的名称
            String typeName = filedType.getName();
            //得到成员变量的名称
            String fieldName = field.getName();
            System.out.println(typeName + " " + fieldName);
        }
    }

    /**
     * 测试
     * @param args
     */
    public static void main(String[] args) {
        String s = "hello world";
        //得到String类的所有局部变量以及类型
        ClassOperateTwo.printFieldMessage(s);
        //得到Integer类的所有局部变量以及类型
        Integer i = 1;
        ClassOperateTwo.printFieldMessage(i);
    }
}

(3)获取类的构造方法的信息

package function.reflect;

import java.lang.reflect.Constructor;

/**
 * Class类的基本操作
 * 通过Class类获取类的构造函数的信息
 *
 * @author kimtian
 */
public class ClassOperateThree {
    /**
     * 打印构造函数的信息
     *
     * @param object 对象
     */
    public static void printContructMessage(Object object) {
        //获取类类型
        Class c = object.getClass();
        /**
         * 构造函数也是对象
         * java.lang.reflect.Constructor中封装了构造函数的信息
         * Field类封装了关于成员变量的操作
         * getConstructors()方法获取的是所有的public的构造函数的信息
         * getDeclaredConstructors()获取的是所有的构造函数的信息,构造方法必须是自己声明的
         */
        Constructor[] cs = c.getConstructors();
        Constructor[] cs1 = c.getDeclaredConstructors();
        for (Constructor constructor : cs) {
            //得到构造方法的名称
            System.out.print(constructor.getName() + "(");
            //获取构造函数的参数列表-->得到的是参数列表的类类型
            Class[] paramTypes = constructor.getParameterTypes();
            for (int j = 0; j < paramTypes.length; j++) {
                //为了美观,最后一个返回值不加标点符号
                if (j == paramTypes.length - 1) {
                    System.out.print(paramTypes[j].getName());
                } else {
                    System.out.print(paramTypes[j].getName() + ",");
                }
            }
            System.out.println(")");

        }
    }

    public static void main(String[] args) {
        printContructMessage("hello");
        printContructMessage(new Integer("1"));
    }
}

(4)类的其他信息的获取

    还可以获取类实现的接口,类的访问权限,类的父类等信息。

    可以查看帮助文档和源码确认还可以获取哪些信息。

package function.reflect;

import java.lang.reflect.Modifier;

/**
 * Class类的基本操作
 * 通过Class类获取类的其他信息
 *
 * @author kimtian
 */
public class ClassOperateFour {
    /**
     * 打印构造函数的信息
     *
     * @param object 对象
     */
    public static void printOtherMessage(Object object) {
        //获取类类型
        Class c = object.getClass();
        /**
         * 得到类实现的接口
         */
        Class[] interfaces = c.getInterfaces();
        for (Class class1 : interfaces) {
            System.out.println(class1.getName());
        }
        /**
         * 得到类的访问权限,返回的是一个int类型
         */
        int a = c.getModifiers();
        /**
         * 由于int类型我们还是不好确认访问权限是什么,所以使用Modifier.toString将其转换
         */
        String string = Modifier.toString(a);
        System.out.println(string);
        /**
         * 得到包名称
         */
        Package pk = c.getPackage();
        System.out.println(pk);
        /**
         * 得到父类
         */
        Class class2 = c.getSuperclass();
        System.out.println(class2);
        /**
         * 还有很多信息可以获取,可自行查看源码和文档
         */

    }

    public static void main(String[] args) {
        printOtherMessage("hello");
        printOtherMessage(new Integer("1"));
    }

}

4.方法反射的基本操作

语法:  method.invoke(对象,参数列表)

package function.reflect;


import java.lang.reflect.Method;

/**
 * 方法反射的基本操作
 *
 * @author kimtian
 */
public class ReflectMethod {
    public static void main(String[] args) {
        /**
         * 获取print(int,int)方法
         * 要获取一个方法就是获取类的信息,获取类的信息,首先要获取类的类类型
         */
        Ademo a1 = new Ademo();
        //获取类的类类型
        Class c = a1.getClass();
        /**
         * 获取方法 名称和参数列表决定
         * getMethod获取的是public方法
         * getDeclaredMethods获取的是自己声明的方法
         */
        try {
            //要处理异常,万一没有这样的方法会引发异常
            Method m = c.getMethod("print", new Class[]{int.class, int.class});
            //基本使用该方法的方式
            a1.print(10, 50);
            /**
             * 方法的反射操作是用m对象来进行方法调用,和上面a1.print调用的效果完全相同
             * 方法如果没有返回值返回null,有返回值返回具体的返回值
             */
            Object o = m.invoke(a1, new Object[]{15, 25});
            System.out.println("====================================");
            //也可以写成这样
            Method m1 = c.getMethod("print", String.class, String.class);
            Object o1 = m1.invoke(a1, "AAbb", "CCdd");
            System.out.println("====================================");
            /**
             * 如果没有参数,这样使用
             */
            Method m2 = c.getMethod("print", new Class[]{});
            Method m3 = c.getMethod("print");
            Object o2 = m3.invoke(a1, new Object[]{});
            Object o3 = m2.invoke(a1);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

class Ademo {
    public void print() {
        System.out.println("nihao");
    }

    public void print(int a, int b) {
        System.out.println(a + b);
    }

    public void print(String a, String b) {
        System.out.println(a.toUpperCase() + "," + b.toLowerCase());
    }
}

5.集合泛型的本质

  可以知道集合的泛型是为了防止错误输入,只在编译阶段有效。

  而反射的操作都是编译后操作,在运行时操作。可以通过反射绕开集合的泛型编译校验,插入数据。

package function.reflect;

import java.lang.reflect.Method;
import java.util.ArrayList;

/**
 * 通过Class,Method来认识泛型的本质
 *
 * @author kimtian
 */
public class ReflectGeneric {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        //泛型为String的例子
        ArrayList<String> stringList = new ArrayList<String>();
        stringList.add("hello");
        System.out.println("集合的前期大小为:" + stringList.size());
        //获取集合1的类类型
        Class c1 = list.getClass();
        //获取集合2的类类型
        Class c2 = stringList.getClass();
        /**
         * 反射的操作都是编译之后的操作,运行时操作
         * c1==c2 结果返回true说明编译之后集合是去泛型化的
         * Java中集合的泛型是防止错误输入的,只在编译阶段有效,绕过编辑就无效了
         * 验证:我们可以通过方法的反射来操作,绕过编译
         */
        System.out.println(c1 == c2);
        /**
         * 验证:我们可以通过方法的反射来操作,绕过编译
         */
        try {
            Method m = c2.getMethod("add", Object.class);
            //绕过编译操作就绕开了泛型
            m.invoke(stringList, 10);
            System.out.println("集合的后期大小为:" + stringList.size());
            System.out.println(stringList);
            /**
             * 不能使用for循环去遍历,因为类型不同会报错
             */
//            for (String s:stringList){
//                System.out.println(s);
//            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值