JAVA反射机制

1.概述

  JAVA反射机制是运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
  反射就是将Java类中的各个成分映射成一个个的Java对象。例如,一个类拥有成员变量、方法、构造方法等信息,可以在获取该类字节码文件(.class)对象的基础上,利用反射技术对该类进行解剖,把类的各个组成部分(成员变量、方法、构造方法等)映射成一个个对象。
  因此,反射的本质就是在获取到一个类的Class类对象后(字节码文件对象),反向获取该类的对象的各种各种信息,包括成员变量、方法、构造方法等。

2.java代码在计算机中经历的三个阶段

在这里插入图片描述

3.获取字节码文件对象的三种方式

(1)Class.forName(“全类名”)
  此方式对应源代码阶段。将字节码文件加载进内存,返回Class对象。多用于配置文件,将类名定义在配置文件中,读取文件,加载类。
(2)类名.class
  此方式对应类对象阶段。通过类名的属性class来获取。多用于参数的传递。
(3)对象.getClass()
  此方式对应运行时阶段。getClass()方法在Object类中定义,所有对象均有此方法。

package cn.ecarg.reflect;
import cn.ecarg.domain.Person;
public class ReflectDemo1 {
    public static void main(String[] args) throws Exception {
        //1.Class.forName("全类名")
        Class cls1 = Class.forName("cn.ecarg.domain.Person");
        System.out.println(cls1);

        //2.类名.class
        Class cls2 = Person.class;
        System.out.println(cls2);

        //3.对象.getClass()
        Person person = new Person();
        Class cls3 = person.getClass();
        System.out.println(cls3);

        //用 == 比较三个对象:
        System.out.println(cls1 == cls2);//true
        System.out.println(cls1 == cls3);//true
    }
}

输出:

在这里插入图片描述
总结:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次。无论通过哪种方式获取的class对象都是同一个。

4.获取各对象并调用

4.1 定义Person类

package cn.ecarg.domain;

public class Person {
    private String name;
    private int age;

    public String a;
    private String b;
    public String c;
    private String d;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }

    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;
    }

    public void eat(){
        System.out.println("eat...");
    }

    public void eat(String food){
        System.out.println("eat" + food);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }
}

4.2 获取成员变量并调用

4.2.1 获取成员变量方法

(1)获取所有public修饰的成员变量
  Field[ ] getFields()
(2)获取指定名称的public修饰的成员变量
  Field[ ] getField(String name)
(3) 获取所有成员变量,不考虑权限修饰符
  Field[ ] getDeclaredFields()
(4)获取指定名称的成员变量
  Field[ ] getclaredField(String name)

4.2.2 调用方法

(1)设置成员变量值
 &esmp;void set(Object obj,Object value);
(2)获取成员变量值
  Object get(Object obj);
(3)忽略访问权限修饰符的安全检查(暴力反射)
  setAccssible(true);

4.2.3 实例测试

package cn.ecarg.reflect;

import cn.ecarg.domain.Person;
import java.lang.reflect.Field;

public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;

        //1.获取成员变量们
        //1.1 Field[] getFields()
        System.out.println("***************获取所有public修饰的成员变量*************");
        Field[] fields = personClass.getFields();
        for(Field field : fields){
            System.out.println(field);
        }

        //1.2 Field[] getField(String name)
        System.out.println("***************获取指定名称的public修饰的成员变量***************");
        Field a = personClass.getField("a");
        System.out.println(a);

        //获取成员变量a的值
        System.out.println("***************获取成员变量a的值***************");
        Person person = new Person();
        Object value = a.get(person);
        System.out.println(value);//null
        //设置成员变量a的值
        System.out.println("***************设置成员变量a的值***************");
        a.set(person,"张三");
        System.out.println(person);


        //1.3 Field[] getDeclaredFields()
        System.out.println("***************获取所有的成员变量,不考虑访问权限修饰符***************");
        Field[] fields1 = personClass.getDeclaredFields();
        for(Field field : fields1){
            System.out.println(field);
        }

        //1.4 Field[] getDeclaredField(String name)
        System.out.println("***************获取指定名称的成员变量,并使用暴力反射,从而不考虑访问权限修饰符***************");
        Field d = personClass.getDeclaredField("d");
        //忽略访问权限修饰符的安全检查
        d.setAccessible(true);//暴力反射
        Object value2 = d.get(person);
        System.out.println(value2);//null
    }
}

输出:
在这里插入图片描述

4.3 获取构造函数并调用

4.3.1 获取构造函数方法

(1)获取所有“公有”的构造方法
  Constructor[ ] getConstructors()
(2)获取单个的“公有的”构造方法
  Constructor getConstructor(Class… parameterTypes)
  Class… parameterTypes:构造函数形参的Class对象类型
(3) 获取所有的构造方法,不考虑权限修饰符
  Constructor[ ] getDeclaredConstructors()
(4)获取"某个构造方法"可以是私有的,或受保护、默认、公有
  Constructor getDeclaredConstructor(Class… parameterTypes)
  Class… parameterTypes:构造函数形参的Class对象类型

4.3.2 调用方法

(1)创建对象
  T newInstance(Object… initargs);
  如果使用空参数构造方法创建对象,操作可以简化为Class对象的newInstance方法。

4.3.3 实例测试

package cn.ecarg.reflect;

import cn.ecarg.domain.Person;
import java.lang.reflect.Constructor;

public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;

        //1.获取构造方法们
        //1.1 Constructor<T> getConstructor(Class<?>...parameterTypes)
        System.out.println("************获取指定的构造方法************");
        Constructor constructor = personClass.getConstructor(String.class,int.class);
        System.out.println(constructor);

        //创建对象1
        System.out.println("************使用Constructor对象创建Object对象************");
        Object person = constructor.newInstance("张三",23);
        System.out.println(person);

        //获取默认构造方法
        System.out.println("************获取默认构造方法************");
        Constructor constructor1 = personClass.getConstructor();
        System.out.println(constructor1);

        //创建对象2
        System.out.println("************使用Constructor对象创建Object对象************");
        Object person1 = constructor1.newInstance();
        System.out.println(person1);

        //简化 创建对象2 的操作,使用Class对象中的newInstance()
        System.out.println("************空构造函数,直接利用Class对象的newInstance()创建对象2************");
        Object person3 = personClass.newInstance();
        System.out.println(person3);
    }
}

输出:
在这里插入图片描述

4.4 获取成员方法并调用

4.4.1 获取成员方法方法

(1)获取所有“公有”的成员方法,包括父类的方法,也包括Object类的方法
  Method[ ] getMethods()
(2)获取单个的“公有”成员方法
  Method getMethod(String name,Class<?>… parameterTypes)
  name:方法名Class…parameterTypes:形参的Class类型对象
(3) 获取所有的成员方法,不考虑权限修饰符
  Method[ ] getDeclaredMethods()
(4)获取"某个成员方法"可以是私有的,或受保护、默认、公有
  Method getDeclaredMethod(String name,Class…parameterTypes)
  name:方法名;Class…parameterTypes:形参的Class类型对象

4.4.2 调用方法

(1)执行方法
  Object invoke(Object obj,Object… args)
  obj:要调用方法的对象;args:调用方法时所传递的实参
(2)获取方法名
  String getName();

4.4.3 实例测试

package cn.ecarg.reflect;

import cn.ecarg.domain.Person;
import java.lang.reflect.Method;

public class ReflectDemo4 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class personClass = Person.class;

        //1.获取成员方法
        //1.1 Method getMethod(String name,Class<?>...parameterTypes)
        System.out.println("**********获取无参的成员方法**********");
        Method eat_method = personClass.getMethod("eat");//获取无参方法
        Person person = new Person();
        //执行方法
        System.out.println("**********执行方法**********");
        eat_method.invoke(person);

        System.out.println();

        System.out.println("**********获取有参的成员方法**********");
        Method eat_method2 = personClass.getMethod("eat",String.class);
        //执行方法2
        System.out.println("**********执行方法**********\"");
        eat_method2.invoke(person,"饭");

        System.out.println();

        //1.2 Method[] getMethods()
        System.out.println(" 获取所有public修饰的方法(除了定义的Person类的方法,还有直接继承的父类Object中的public修饰的方法)");
        Method[] methods = personClass.getMethods();
        for(Method method : methods){
            System.out.println("方法全限定名:"+ method);
            String string = method.getName();
            System.out.println("方法名:" + string);
        }
    }
}

输出:在这里插入图片描述

5. 反射案例(框架实现)

(1)需求
  写一个“框架”,在不改变该类任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法。
(2)实现方法
  ① 配置文件
  ② 反射
(3)步骤
  ① 将需要创建的对象的全类名和需要执行的方法定义配置文件中;
  ② 在程序中加载读取配置文件;
  ③ 使用反射技术来加载文件进内存;
  ④ 创建对象;
  ⑤ 执行方法。
(4)具体实现
Person.java

package cn.ecarg.domain;

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }

    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;
    }

    public void eat(){
        System.out.println("eat...");
    }

    public void eat(String food){
        System.out.println("eat" + food);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age
                '}';
    }
}

Student.java

package cn.ecarg.domain;

public class Student {

    public void sleep(){
        System.out.println("正在执行Student类中的sleep方法");
    }
}

ReflectTest.java

package cn.ecarg.reflect;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 框架类
 * 前提:不能改变类的任何代码,可以创建任意类的对象,执行任意方法。
 */
public class ReflectTest {

    public static void main(String[] args) throws Exception{
        //1.加载配置文件
        //1.1 创建Properties对象
        Properties pro = new Properties();
        //1.2 加载配置文件,转化为一个集合
        //1.2.1 获取class目录下的配置文件(.properties)
        ClassLoader classLoader = ReflectTest.class.getClassLoader();//获取ReflectTest字节码文件对应的类加载器(由类加载器将ReflectTest.class加载进内存)
        //返回pro.properties文件的字节流
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        //加载properties配置文件
        pro.load(is);

        //2.获取配置文件中定义的数据
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");

        //3.加载该类进内存
        Class cls = Class.forName(className);

        //4.创建对象
        Object obj = cls.newInstance();

        //5.获取方法对象
        Method method = cls.getMethod(methodName);

        //6.执行方法
        method.invoke(obj);
    }
}

pro.properties

className=cn.ecarg.domain.Student
methodName=sleep

输出:
**加粗样式**

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值