JAVA基础-反射

2013年7月22日08:36:13 发表评论 8 views

一、反射的介绍

JAVA反射机制是在运行状态中,能够获取任意一个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法。这种动态获取的以及动态调用对象的方法的功能称为java语言的反射机制。JAVA编译时是先获取到类,然后才是类里边的属性和方法,而反射则和编译相反,他是先获取类里边的对象和方法然后在告诉他是哪个类里的。简单来说, 就可以把.class文件比做动物的尸体, 而反射技术就是对尸体的一种解剖。通过反射技术, 我们可以拿到该字节码文件中所有的东西, 例如成员变量, 成员方法, 构造方法, 而且还包括私有。想要反射首先要获取到程序的“尸体”也就是.class文件。

二、字节码文件的获取

获取字节码对象有3种方式:

1、类名.class - 这是一个静态的属性, 只要知道类名, 就可以获取

2、对象名.getClass() - Object类里的getClass()方法,对象已经存在的情况下, 可以使用这种方式

3、Claire.forName("类的全类名(包名+类名)") - 通过Class类里的静态方法forName来获取节码对象

举例:

public static void main(String[] args) throws ClassNotFoundException {
    // 通过Object的getClass()方法获取,必须要有对象
    Student s = new Student();
    Class clazz = s.getClass();
    // 通过类名获取字节码对象
    Class clazz2 = Student.class;
    // 通过Class类里的静态方法forName来获取节码对象
    Class clazz3 = Class.forName("com.fanshe.Student");
    System.out.println(clazz == clazz2);
    System.out.println(clazz == clazz3);
    System.out.println(clazz);
}

字节码文件其实就是描述.class文件的对象。

三、对构造方法的操作

*通过反射获取公有构造方法的两种方式:

1、返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法,可以获取无参构造也可以根据传入的类型来匹配对应的构造方法:getConstructor(Class<?>... parameterTypes)

2、返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法:

getConstructors()

3、创建此 Class 对象所表示的类的一个新实例:

newInstance()

*暴力获取(可以获取全部权限的):

1、返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法:

getDeclaredConstructor(Class<?>... parameterTypes)

2、返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法:

getDeclaredConstructors()

举例:

public static void main(String[] args) throws ReflectiveOperationException {
    // Class.forName()获取字节码对象
    Class<?> forName = Class.forName("com.fanshe.Student");
    // 获取所有公共构造方法
    Constructor<?>[] constructors = forName.getConstructors();
    // 遍历
    for (Constructor<?> constructor : constructors) {
        // 打印结果
        System.out.println(constructor);
    }
    System.out.println("--------------------------------------");
    // 暴力获取,可以获取所有的构造方法(包括私有的)
    Constructor<?> c1 = forName.getDeclaredConstructor();
    c1.setAccessible(true);
    System.out.println(c1);
    //获取有参构造
    Constructor<?> c2 = forName.getConstructor(String.class, int.class);
    System.out.println(c2);
}

四、对成员变量和方法的操作

*公有成员变量获取方法:

1、返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段:

getField(String name) - 参数为要返回的变量名

2、返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段:

getFields() - 参数为要返回的变量名

*任意成员变量获取方法:

1、返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段:

getDeclaredField(String name) - 参数为要返回的变量名

2、返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段:

getDeclaredFields() - 参数为要返回的变量名

*获取公有的方法:

1、返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法:

getMethod(String name, Class<?>... parameterTypes) - name 方法名、parameterTypes 参数列表

2、返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法:

getMethods()

*暴力获取方法:

1、 返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法:

getDeclaredMethod(String name, Class<?>... parameterTypes) - name 方法名、parameterTypes参数列表

2、返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法:

getDeclaredMethods()

需要注意的是想要获取私有的变量或者方法时应使用AccessibleObject类里的setAccessible(boolean flag)方法 - 参数值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。

Constructor, Field, Method都是AccessibleObject的子类所以可以直接使用父类的setAccessible(boolean flag)方法。

**通过反射获得变量的流程:

1. 通过反射获取该类的字节码对象

2. 创建该类对象

3. 获取该类中需要操作的字段(成员变量)

4. 通过字段对象中的方法修改属性值

**通过反射执行方法的流程

1. 通过反射获取该类的字节码对象

2. 创建该类对象

3. 调用getMethod方法获取Method对象, 方法形参接受方法的名字

4. 调用Method方法中的invoke()将方法运行

举例:

*被反射的学生01类

public class Student01 {
    public String name;
    private int age;
    public Student01() {
        super();
        // TODO Auto-generated constructor stub
    }
    public Student01(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    public void name() {
        System.out.println("测试");
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    private void mane1() {
       System.out.println("这是个萌萌哒私有的");
    }
    @Override
    public String toString() {
        return "Student01 [name=" + name + ", age=" + age + "]";
    }
}

*对学生01类进行反射

public static void main(String[] args) throws ReflectiveOperationException {
    // 获取字节码对象
    Class<?> clazz = Class.forName("com.fanshe.Student01");
    // 创建该类的对象
    Object stu = clazz.newInstance();
    // System.out.println(stu);
    // 获取学生类的name变量
    Field f1 = clazz.getField("name");
    /*
    * set将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
    * 为stu对象里的name变量赋值
    */
    f1.set(stu, "李四");
    // get()返回指定对象上此 Field 表示的字段的值。
    Object name = f1.get(stu);
    // 暴力获取age字段
    Field f2 = clazz.getDeclaredField("age");
    System.out.println(f2);
    // 让jvm不检查权限
    f2.setAccessible(true);
    // 为其赋值
    f2.set(stu, 24);
    // 获取stu对象的f2字段的值
    Object age = f2.get(stu);
    System.out.println(name);
    System.out.println(age);
    // 公有无参无返回值,name()
    Method method = clazz.getMethod("name");
    // 使用Method类的invoke方法执行name方法
    method.invoke(stu);
    // 公有代参无返回值,参数为String类型
    Method m2 = clazz.getMethod("setName", String.class);
    // 执行stu对象的setName方法,传入参数
    m2.invoke(stu, "李晨宇");
    // 公有无参有返回值
    Method m3 = clazz.getMethod("getName");
    // 返回值为invoke
    Object invoke = m3.invoke(stu);
    System.out.println(invoke);
    // 私有
    Method m4 = clazz.getDeclaredMethod("mane1");
    // 让jvm不检查权限
    m4.setAccessible(true);
    // 执行stu对象的mane1方法
    m4.invoke(stu);
}

需要注意的是,反射无参构造时被反射的类一定要有无参构造方法,默认生成的也算。

五、反射的应用

我们在开发的时候,由于要考虑到代码的重用性,就会用反射来处理一些问题。而JAVA的一些常用jar包和主流框架的配置都用到了反射的原理,学习反射有助于我们对源码的阅读和理解。BeanUtils工具类(Apache开发的便于操作JavaBeen的工具类)就用到了反射的方法。

*BeanUtils的部分实现:

public class MyBeanUtils {
    //因为是工具类,不需要实例化。所以私有构造方法
    private MyBeanUtils() {
        super();
        // TODO Auto-generated constructor stub
    }
    /*
    * 给对象中的属性赋值 传入类的对象类型,和要修改的属性的值不确定所以用Object类      型,属性名用String类型
    */
    public static void setProrerty(Object object, String name, Object values) throws ReflectiveOperationException, SecurityException {
        // 获取传入对象的字节码文件
        Class clazz = object.getClass();
        // 根据传入的属性获取Field对象,因为不确定属性的权限,用的暴力反射
        Field field = clazz.getDeclaredField(name);
        // 让jvm不检查权限
        field.setAccessible(true);
        // 为object对象里的name属性赋值
        field.set(object, values);
    }
    // 获取对象中的属性
    public static String getProrerty(Object object, String name) throws ReflectiveOperationException, SecurityException {
        // 获取传入对象的字节码文件
       Class clazz = object.getClass();
       // 根据传入的属性获取Field对象,因为不确定属性的权限,用的暴力反射
       Field field = clazz.getDeclaredField(name);
        // 让jvm不检查权限
       field.setAccessible(true);
       // 获取name属性的值
       Object object2 = field.get(object);
       // System.out.println(object);
       // 将值返回
        return object2.toString();
    }
    // 给对象中的属性赋值(通过Map的方式),Map里key存的是属性名,value存的是要赋的值
    public static void populat(Object object, Map map) throws ReflectiveOperationException, SecurityException {
        // 获取传入对象的字节码文件
       Class clazz = object.getClass();
       // 返回此集合中的key集合
        Set keySet = map.keySet();
        // 遍历key
        for (Object object2 : keySet) {
            // 获得value值
            Object value = map.get(object2);
            try {
                // 根据传入的key(属性)获取Field对象,因为不确定属性的权限,用的暴力反射
                Field field = clazz.getDeclaredField(object2.toString());
               // 让jvm不检查权限
                field.setAccessible(true);
                // 赋值
                field.set(object, value);
             } catch (NoSuchFieldException e) {
                 // 出现异常,给出友好型提示
                 System.out.println("Mdzz,属性都记不住");
             }
        }
    }
}

*测试工具类:

public static void main(String[] args) throws RuntimeException, ReflectiveOperationException {
    // 创建学生01对象
    Student01 s1 = new Student01();
    // 使用MyBeanUtils工具类为学生01对象赋值
    MyBeanUtils.setProrerty(s1, "name", "啦啦");
    MyBeanUtils.setProrerty(s1, "age", 15);
    // 使用MyBeanUtils工具类为学生01对象取值
    String name = MyBeanUtils.getProrerty(s1, "name");
    String age = MyBeanUtils.getProrerty(s1, "age");
    // 打印出来
    System.out.println(name);
    System.out.println(age);
    System.out.println("------------------------------------ ");
    // 创建HashMap作为数据源
    HashMap<String, Object> hashMap = new HashMap<>();
    // 为HashMap赋值
    hashMap.put("qqqqq", "大大的"); // 属性不存在会给出友好型提示
    hashMap.put("name", "大大的");
    hashMap.put("age", 110);
    // 使用MyBeanUtils工具类为学生01对象赋值
    MyBeanUtils.populat(s1, hashMap);
    System.out.println(s1);
}
  • yiisaa
  • 这是我的微信扫一扫
  • weinxin
  • zhengweiqiangcom
  • 我的微信公众号扫一扫
  • weinxin

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: