Java反射——框架设计的灵魂

文章目录

  • 前言
  • 1.反射与框架
  • 2.反射优点
  • 3.获取Class对象的三种方式
  • 4.Class对象功能
    • 4.1 获取功能
      • 4.1.1 获取成员变量
      • 4.1.2 获取构造方法(构造器)
      • 4.1.3 获取成员方法
      • 4.1.4 获取全类名
    • 4.2 Field:成员变量
      • 4.2.1 设置值
      • 4.2.2 获取值
      • 4.2.3 忽略访问权限修饰符的安全检查
    • 4.3 Constructor:构造方法
      • 4.3.1 创建对象
    • 4.4 Method:方法对象
      • 4.4.1 执行方法
      • 4.4.2 获取方法名称
  • 5.案例



前言

  简单介绍JAVA的反射机制和配置文件properties的使用。



1.反射与框架

  • 框架:半成品软件,可以在框架的基础上进行软件开发,简化编码。
  • 反射:将类的各个组成成分封装为其他对象,这就是反射机制。


2.反射优点

  • 可以在程序运行中,操作这些对象。
  • 可以解耦,提高程序的可扩展性。

这里预先写一个Person类以供下面的操作。

//************Person类********************
package com.bese.domain;

public class Person {

    public String name;
    public int age;
    protected String gender;
    int weight ;
    private int height;

    public Person() {
    }

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

    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 String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

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

    //定义成员方法
    public void eat(){
        System.out.println("无参方法eat执行了...");
    }

    public void eat(String food){
        System.out.println("带参方法eat执行了:eat "+food);
    }
}



3.获取Class对象的三种方式

  1. Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象。
  • 多用于配置文件,将类名定义在配置文件中,读取文件加载类。
  1. 类名.class:通过类名的属性class获取Class对象。
  • 多用于参数的传递
  1. 对象.getClass():getClass()方法在Object类中定义,直接用对象来获取Class对象。
  • 多用于获取对象的字节码
//*****************获取Class对象的demo类************************
package com.bese.reflect;

import com.bese.domain.Person;

public class ReflectDemo1 {

    public static void main(String[] args) throws Exception{
        //1.Class.forName("全类名")
        Class cls1 = Class.forName("com.bese.domain.Person");
        System.out.println(cls1);

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

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

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

运行结果:
Java反射——框架设计的灵魂_第1张图片

注意:
  同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,三种方式获取的Class对象都是同一个。



4.Class对象功能

4.1 获取功能

4.1.1 获取成员变量

  • Field[] getFields():获取所有public修饰的成员变量
  • Field getField​(String name) :获取指定名称的public修饰的成员变量
  • Field[] getDeclaredFields​() :获取所有的成员变量,不考虑权限修饰符。
  • Field getDeclaredField​(String name) :获取指定名称的成员变量,不考虑权限修饰符
//**********************获取成员变量**************************
package com.bese.reflect;

import com.bese.domain.Person;
import java.lang.reflect.Field;

public class ReflectDemo2{
    /**
     * 获取成员变量
     */
    public static void main(String[] args) throws Exception{
        //1.获取Person的Class对象
        Class personClass = Person.class;

        //2.获取全部由Public修饰的成员变量:Field[] getFields()
        Field[] fileds = personClass.getFields();
        for (Field field : fileds){
            System.out.println(field);
        }
        System.out.println("------------分割线------------");

        //获取指定的由Public修饰的成员变量name的值:get​(Object obj)
        Field name = personClass.getField("name");
        Person p = new Person();
        Object value = name.get(p);
        System.out.println("name的值为:" + value);
        //设置name的值:set​(Object obj, Object value)
        name.set(p,"张三");
        System.out.println("name的值为:" + p.name);

        System.out.println("------------分割线------------");

        //Field[] getDeclaredFields​() :获取所有的成员变量,不考虑权限修饰符。
        Field[] declaredFields= personClass.getDeclaredFields();
        for (Field declaredField : declaredFields){
            System.out.println(declaredField);
        }

        //Field getDeclaredField​(String name)  Constructor getDeclaredConstructor​(Class... parameterTypes)
        //获取指定名称的成员变量,不考虑权限修饰符。
        Field height = personClass.getDeclaredField("height");
        //忽略访问权限修饰符:暴力反射setAccessible()
        height.setAccessible(true);
        height.set(p,123);
        Object value2 = height.get(p);
        System.out.println("height的值为:" + value2);
    }
}

运行结果:
Java反射——框架设计的灵魂_第2张图片
注意:

  • Field[] getFields()只能获取由Public修饰的成员变量,所以只有name和age被获取到了。
  • 设置私有属性时,要用暴力反射方式 height.setAccessible(true); ,将 setAccessible() 的值设为True,可以忽略权限修饰符。

4.1.2 获取构造方法(构造器)

  • Constructor[] getConstructors​() :返回由Public修饰的全参构造器
  • Constructor getConstructor​(Class… parameterTypes) :返回Public修饰的无参构造器
  • Constructor[] getDeclaredConstructors​() :返回全参构造器,忽略权限修饰符。
  • Constructor getDeclaredConstructor​(Class… parameterTypes) :返回无参构造器,忽略修饰符
//***************获取构造器,创建对象*******************************
package com.bese.reflect;

import com.bese.domain.Person;
import java.lang.reflect.Constructor;

public class ReflectDemo3 {
    /**
     * 获取构造方法
     */
    public static void main(String[] args) throws Exception {
        //1.获取Person的Class对象
        Class personClass = Person.class;

        //Constructor[] getConstructors​()
        //获取由Public修饰的全参构造方法,需要传入所有参数的类型Class对象
        Constructor constructor = personClass.getConstructor(String.class, int.class, String.class, int.class, int.class);
        System.out.println(constructor);
        //用构造器创建对象:newInstance()
        Object person = constructor.newInstance("张三",23,"man",56,175);
        System.out.println(person);

        System.out.println("------------分割线------------");

        //Constructor getConstructor​(Class… parameterTypes)
        //获取由Public默认的无参构造
        Constructor constructor1 = personClass.getConstructor();    //返回一个构造器
        System.out.println(constructor1);
        //用无参构造器创建对象:newInstance()
        Object person1 = constructor1.newInstance();
        System.out.println(person1);

        //简化后的空参构造器创建对象,不需要再获取无参构造器。
        Object o = personClass.newInstance();
        System.out.println(o);

        //如果要用私有构造器创建对象,用暴力反射的方式
        constructor1.setAccessible(true);

    }
}

运行结果:
Java反射——框架设计的灵魂_第3张图片
注意:
  获取构造器的目的是用来创建对象的,获取全参构造方法需要传入对应参数的类型对象,用全参构造器创建对象时,也需要传入对应的参数。


4.1.3 获取成员方法

  • Method[] getMethods​() :获取全部public修饰的成员方法
  • Method getMethod​(String name, Class… parameterTypes):获取指定名称且由Public修饰的方法
  • Method[] getDeclaredMethods​()
  • Method getDeclaredMethod​(String name, Class… parameterTypes)
//************************获取成员方法和方法名*****************************
package com.bese.reflect;

import com.bese.domain.Person;
import java.lang.reflect.Method;

public class ReflectDemo4 {
    /**
     * 获取成员方法
     */
    public static void main(String[] args) throws Exception {
        //1.获取Person的Class对象
        Class personClass = Person.class;
        Person p = new Person();

        //Method getMethod​(String name, Class... parameterTypes)
        //获取指定名称且不带参数的方法
        Method eat_method =  personClass.getMethod("eat");
        //执行方法:Object invoke​(Object obj, Object... args)
        eat_method.invoke(p);

        //获取指定名称且带参数列表的方法
        Method eat_method1 =  personClass.getMethod("eat",String.class);
        //调用方法
        eat_method1.invoke(p,"meal");

        System.out.println("--------------------分割线-----------------------");

        //获取全部public修饰的成员方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods){
            System.out.println(method);
        }

        System.out.println("--------------------分割线-----------------------");

        //只获取方法名称
        for (Method method : methods){
            String name = method.getName();
            System.out.println(name);
        }

        System.out.println("--------------------分割线-----------------------");

        //获取类名
        String className = personClass.getName();
        System.out.println("获取到的类的名称是:" + className);
    }
}


运行结果:
Java反射——框架设计的灵魂_第4张图片
Java反射——框架设计的灵魂_第5张图片
注意:
  可以看到获取到的方法全部是由Public修饰的,并且与Person类有关的方法都可以被获取到。


4.1.4 获取全类名

  • String getName​() :获取


4.2 Field:成员变量

4.2.1 设置值

  • void set​(Object obj, Object value)

4.2.2 获取值

  • get​(Object obj)

4.2.3 忽略访问权限修饰符的安全检查

  • setAccessible(true):暴力反射


4.3 Constructor:构造方法

4.3.1 创建对象

  • newInstance​(Object… initargs)
    如果使用无参构造方法创建对象,可以使用Class对象的方法newInstance​()


4.4 Method:方法对象

4.4.1 执行方法

  • Object invoke​(Object obj, Object… args)

4.4.2 获取方法名称

  • String getName()


5.案例

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

2.1 配置文件(properties)

# 放在src根目录下的配置文件pro.properties
className=com.bese.domain.Person
methodName=eat

//************************框架类*******************
package com.bese.reflect;

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

public class ReflectStruct {
    /*
      可以创建任意对象,执行任意方法
      前提:不能改变该类的任何代码
     */
    public static void main(String[] args) throws Exception{
        //1.加载配置文件
        //1.1创建Properties对象
        Properties pro = new Properties();
        //1.2加载配置文件,转换为一个集合
        //1.2.1获取class目录下的配置文件
        ClassLoader classLoader = ReflectStruct.class.getClassLoader();//类加载器getClassLoader()
        InputStream is = classLoader.getResourceAsStream("pro.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);
    }
}

注意:
  在配置文件中配置好要调用的类名和该类下的方法,再到框架类中加载配置文件,就可以使用配置文件中的类和它的方法了。



时间:2019年10月14日11:04:29


你可能感兴趣的:(Java)