Java 基础 50 反射

1.1反射机制的概述和字节码对象的获取方式

1.1.1反射介绍

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法

对于任意一个对象,都能够调用它的任意一个方法
这种动态获取的以及动态调用对象的方法的功能称为java语言的反射机制.

简单来说, 就可以把.class文件比做动物的尸体, 而反射技术就是对尸体的一种解剖.
通过反射技术, 我们可以拿到该字节码文件中所有的东西, 例如成员变量, 成员方法, 构造方法, 而且还包括私有

1.1.2 字节码文件获取的三种方式

  • 对象名.getCalss();

    • 次方法来自于Object 对象已经存在的情况下, 可以使用这种方式
  • 类名.class

    • 类名.class这是一个静态的属性, 只要知道类名, 就可以获取
  • Class.forName(“com.itheima_01.Student”);

    • 通过Class类中的静态方法, 指定字符串, 该字符串是类的全类名(包名+类名)
    • 此处将会抛出异常都系 ClassNotFoundException 防止传入错误的类名

1.1.3 案例代码

package com.itheima_01;

/*
 * 反射:
 *      在运行时,我们可以获取任意一个类的所有方法和属性
 *      在运行时,让我们调用任意一个对象的所有方法和属性
 * 
 * 反射的前提:
 *      要获取类的对象(Class对象)
 *
 */
public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        // 通过Object的getClass()方法获取,必须要有对象
        Student s = new Student();
        Class clazz = s.getClass();

        // 通过类名获取字节码对象
        Class clazz2 = Student.class;

        // static Class forName(String className)
        Class clazz3 = Class.forName("com.itheima_01.Student");

        System.out.println(clazz == clazz2);
        System.out.println(clazz == clazz3);
        System.out.println(clazz);

    }
}

1.1.4 问题: 字节码对象是用来描述什么的?

  • 用来描述.class文件的.

    • 面向对象阶段的时候讲过java中描述事物都是通过类的形式
      而字节码文件也可以看做为一种事物, 如何描述这种事物?
  • 那就看看这个事物是由什么组成的了

    • 成员变量
    • 成员方法
    • 构造方法

1.2 反射操作构造方法

1.2.1 通过获取的构造创建对象

  • 步骤:
    • 获得Class对象
    • 获得构造
    • 通过构造对象获得实例化对象
package com.itheima_01;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/*
 *  通过反射获取构造方法并使用
 *          Constructor[] getConstructors()  
 *          Constructor getConstructor(Class... parameterTypes) 
 *           T newInstance()   
 *
 *Constructor:
 *      T newInstance(Object... initargs)  
 */
public class ReflectDemo2 {
    public static void main(String[] args) throws ReflectiveOperationException {
        Class clazz = Class.forName("com.itheima_01.Student");
        
        //method(clazz);
        //Constructor getConstructor(Class... parameterTypes) 
        //method2(clazz);
        //method3(clazz);
        
        Object obj = clazz.newInstance();
        System.out.println(obj);
         
        
            
    }

    private static void method3(Class clazz)
            throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        Constructor c = clazz.getConstructor(String.class,int.class);//获取有参构造,参数1类型为String,参数2类型为int
        System.out.println(c);
        Object obj = c.newInstance("lisi",30);
        System.out.println(obj);
    }

    private static void method2(Class clazz)
            throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        Constructor c = clazz.getConstructor();//获取无参构造
        System.out.println(c);
        Object obj = c.newInstance();
        System.out.println(obj);
    }

    private static void method(Class clazz) {
        //Constructor[] getConstructors() :获取所有public修饰的构造方法 
        Constructor[] cs = clazz.getConstructors();
        for (int i = 0; i < cs.length; i++) {
            System.out.println(cs[i]);
        }
    }
    
}

1.2.2问题: 直接通过Class类中的newInstance()和获取getConstructor()有什么区别?

  • newInstance()
    • 只能通过空参的构造方法创建对象
  • getConstructor(Class… parameterTypes)
    • 方法接受一个可变参数, 可以根据传入的类型来匹配对应的构造方法

1.3 反射操作公共成员变量

1.3.1 反射public成员变量(字段)

通过反射运行public变量流程

  1. 通过反射获取该类的字节码对象
    Class clazz = Class.forName("com.heima.Person");
  2. 创建该类对象
    Object p = clazz.newInstance();
  3. 获取该类中需要操作的字段(成员变量)
    getField(String name) --> 方法传入字段的名称.
    注意: 此方法只能获取公共的字段
    Field f = clazz.getField("age");
  4. 通过字段对象中的方法修改属性值
    void set(Object obj, Object value) --> 参数1): 要修改那个对象中的字段, 参数2): 将字段修改为什么值.
    f.set(p, 23);

1.3.2 案例代码


package com.itheima_01;

import java.lang.reflect.Field;

/*
 *  通过反射获取成员变量并使用  
 *          Field[] getFields()  
 *          Field getField(String name)  
 *
 *           Field[] getDeclaredFields()  
 *           Field getDeclaredField(String name) 
 *
 * Field:
 *          Object get(Object obj)  
 *          void set(Object obj, Object value) 
 
 
 */
public class ReflectDemo3 {
    public static void main(String[] args) throws ReflectiveOperationException {
        //获取学生类的字节码对象
        Class clazz = Class.forName("com.itheima_01.Student");
        //获取学生类的对象
        Object stu = clazz.newInstance();
        
        
        //Field getField(String name)  :根据字段名称获取公共的字段对象
        Field f = clazz.getField("age");//获取成员变量对象
        //System.out.println(f);
        
        //void set(Object obj, Object value)
        f.set(stu,28);//通过成员变量对象,修改指定对为指定的值
        
        
        //Object get(Object obj)
        Object age = f.get(stu);//通过对象获取成员变量的值
        System.out.println(age);
        
        System.out.println(stu);
        
        
    }

    private static void method(Class clazz) {
        //Field[] getFields()  :获取公共的成员变量
        Field[] fs = clazz.getFields();
        for (int i = 0; i < fs.length; i++) {
            System.out.println(fs[i]);
        }
        System.out.println("----------");
        //getDeclaredFields()  :获取所有的成员变量
        Field[] fs2 = clazz.getDeclaredFields();
        for (int i = 0; i < fs2.length; i++) {
            System.out.println(fs2[i]);
        }
    }
}

1.3.3 方法总结

通过反射获取成员变量并使用
    Field[] getFields()--> 返回该类所有(公共)的字段
    Field getField(String name)--> 返回指定名称字段
    Field[] getDeclaredFields()--> 暴力反射获取所有字段(包括私有)
    Field getDeclaredField(String name) --> 暴力反射获取指定名称字段

                    ---------------马上讲-----------------

Field:
    Object get(Object obj)--> Field对象调用, 返回传入对象的具体字段
    void set(Object obj, Object value) -->Field对象调用
    参数1: 要修改的对象
    参数2: 将此对象的字段修改为什么值.

1.4 反射操作私有成员变量

1.4.1 反射private成员变量(字段)

  • 反射private属性执行流程
    • 获取学生类字节码对象
    • 获取学生对象
    • 通过getDeclaredField方法获取私有字段
    • 通过setAccessible让jvm不检查权限
    • 通过set方法设置对象为具体的值

1.4.2 案例代码

package com.itheima_01;

import java.lang.reflect.Field;

/*
 *  通过反射获取私有成员变量并使用  
 *           Field[] getDeclaredFields()  
 *           Field getDeclaredField(String name) 
 */
public class ReflectDemo4 {
    public static void main(String[] args) throws ReflectiveOperationException {
        //获取学生类的字节码对象
        Class clazz = Class.forName("com.itheima_01.Student");
        //获取学生对象
        Object stu = clazz.newInstance();
        //获取私有的字段对象
        Field f = clazz.getDeclaredField("name");
        f.setAccessible(true);//设置反射时取消Java的访问检查,暴力访问
        //System.out.println(f);
        
        f.set(stu, "lisi");
        
        Object name = f.get(stu);
        System.out.println(name);
    }

}

1.4.3 方法总结

Field[] getDeclaredFields()--> 暴力反射获取所有字段(包括私有)
Field getDeclaredField(String name) --> 暴力反射获取指定名称字段
void setAccessible(boolean flag) --> 让jvm不检查权限


1.5 通过反射获取成员方法并使用

1.5.1 反射获取普通成员方法

  • 反射public方法执行流程
    • 获取学生类字节码对象
    • 反射手段创建学生对象
    • 调用getMethod方法获取Method对象, 方法形参接受方法的名字
    • 调用Method方法中的invoke()将方法运行

1.5.2 案例代码


package com.itheima_01;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/*
 *  通过反射获取成员方法并使用  
 *      Method getMethod(String name, Class... parameterTypes)  
 *  Method:
 *      Object invoke(Object obj, Object... args)  
 *      
 */

public class ReflectDemo5 {
    public static void main(String[] args) throws ReflectiveOperationException {
        //获取学生类的字节码对象
        Class clazz = Class.forName("com.itheima_01.Student");
        //获取学生类的对象
        Object stu = clazz.newInstance();
        
        //获取无参有返回值的方法
        Method m = clazz.getMethod("getName");
        Object obj = m.invoke(stu);
        System.out.println(obj);
    }

    private static void method2(Class clazz, Object stu)
            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        //获取有参无返回值的方法
        Method m = clazz.getMethod("setName", String.class);
        m.invoke(stu, "lisi");
        System.out.println(stu);
    }

    private static void method(Class clazz, Object stu)
            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        //获取无参无返回值的方法
        Method m = clazz.getMethod("method");
        m.invoke(stu);
    }

}

1.5.3 方法总结

Class:
    Method getMethod(String name, Class... parameterTypes)  
            // 此方法由字节码对象调用
            // 参数1: 要反射的方法名称
            // 参数2: 此方法需要接受的参数类型(注意,传入的都是字节码)
Method:
    Object invoke(Object obj, Object... args)  
            // 方法由Method对象调用
            // 参数1: 要由那个对象调用方法
            // 参数2: 方法需要的具体实参(实际参数)

1.5.4 问题: 私有的成员方法怎么玩?

// 获取字节码对象
        Class clazz = Class.forName("com.heima.Student");
        // 创建学生对象
        Object stu = clazz.newInstance();
        // 暴力反射获取方法
        Method method = clazz.getDeclaredMethod("method");
        // 让jvm不检查权限
        method.setAccessible(true);
        // 执行方法
        method.invoke(stu);

你可能感兴趣的:(Java 基础 50 反射)