Java反射

1.反射机制是什么?

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

2.反射机制能做什么?

  • 获得类的定义信息
    • 包名、类名
    • 成员变量定义信息
    • 构造方法定义信息
    • 方法定义信息
  • 在运行时创建对象
  • 在运行时调用成员

3.反射应用场景

  • 框架中对象的构建(例如mybatis中的resultType,resultMap,spring中的bean)

  • 框架中方法的调用(例如对象set方法,get方法,spring mvc 控制层方法,..)

    总之:反射不能预知未来,但可驾驭未来,通过反射可以更好构建一些编程框架,
    以实现通用性编程,从而达到简化代码编写。

4.反射机制API

4.1 获取Class对象(字节码对象):可理解为反射应用的入口

  • 类名.class
  • Class.forName("包名.类名")
  • 实例对象.getClass()
数组对象是通过 实例对象.getClass().getComponentType()
说明:返回数组中元素的Class对象,如果不是Class对象那么返回null
      非数组类型不能通过**getComponentType()**方法获得元素的Class对象类型

Class<?> java.lang.Class.getComponentType()

4.2 获取包名类名(c为Class对象实例)

  • c.getPackage.getName() 包名
  • c.getName() 完整类名
  • c.getSimpleName() 简写类名

4.3 成员变量的定义信息

  • getFields()
    获得所有可见的变量,包含从父类继承的变量

  • getDeclaredFields()
    获得本类定义的变量,包括私有,不包含继承的变量

  • getField(变量名)

  • getDeclaredField(变量名)

4.4 构造方法的定义信息

  • getConstructors()
    获得所有可见的构造方法

  • getDeclaredConstructors()
    获得所有构造方法,包括私有

  • getConstructor(参数类型列表)

  • getDeclaredConstructor(参数类型列表)

4.5 方法的定义信息

  • getMethods()
    获得所有可见的方法,包含从父类继承的方法

  • getDeclaredMethods()
    获得所有本类定义的方法,包括私有,不包含继承的方法

  • getMethod(方法名,参数类型列表)

  • getDeclaredMethod(方法名,参数类型列表)

4.6 反射创建对象

  • 执行无参构造
    Object obj = c.newInstance()

  • 执行有参构造

    1. 获得构造方法
      Constructor t = c.getDeclaredConstructor(int.class, String.class);

    2. 创建对象,并执行这个构造方法
      Object obj = t.newInstrance(5, "sdfsd");

4.7 反射调用成员变量

  • Field f = c.getDeclaredField("name"); 获取变量

  • f.setAccessible(true) 使私有变量,也能被访问

  • f.set(对象, "张三"); 为变量赋值

    第一个参数,指定对象,为指定对象的变量赋值
    静态变量,第一个参数给 null

  • f.get(对象) 访问变量的值

4.8 反射调用方法

  • 获取方法
    Method t = c.getDeclaredMethod("f1",int.class,String.class);

  • 使私有方法可以被调用
    t.setAccessible(true);

  • 反射调用方法
    Object r = t.invoke(对象, 5, "abc")

    静态方法,第一个参数给 null
    如果没有返回值,得到 null

5. 反射应用实例

5.1 在泛型为Integer的ArrayList中存放一个String类型的对象

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

/**
 * ClassName: Test1
 * Function:  在泛型为Integer的ArrayList中存放一个String类型的对象
 * date:     2020/01/20
 * author     Luzy
 * version    V1.0
 */
public class Test1 {
    public static void main(String[] args) throws Exception {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(123);
        list.add(520);
        Method method = list.getClass().getMethod("add", Object.class);
        method.invoke(list, "Java反射机制实例");
        System.out.println(list.toString());

    }
}

输出结果:

[123, 520, Java反射机制实例]

5.2 通过反射取得并修改数组的信息

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Arrays;

/**
 * ClassName: Test2
 * Function:  通过反射修改String
 * date:     2020/01/20
 * author     Luzy
 * version    V1.0
 */
public class Test2 {
    public static void main(String[] args) throws Exception {
        String str = new String("恭喜发财!");//
        System.out.println("输出原本str的内容:" + str);
        //1、获取String类对应的Class
        Class<? extends String> clazz = str.getClass();
        //2、通过clazz获取类中的value属性
        Field value = clazz.getDeclaredField("value");
        //3、需要设置私有属性可以被操作后才能操作属性值
        value.setAccessible(true);
        //4、获取value属性里面的值(内存地址)
        //由于 String里面的值是存放在char[] 数组里面的,所以需要用一个char[]接收
        char[] temp = (char[])value.get(str);
        //5、通过temp的地址引用 找到真实String对象中的数组,修改数组内的每一个元素
        //由于此处是char类型,所以要用单引号
        temp[0] = '新';
        temp[1] = '年';
        temp[2] = '快';
        temp[3] = '乐';
        System.out.println("反射操作过后的str内容:"+str);
    }
}


输出结果:

输出原本str的内容:恭喜发财!
反射操作过后的str内容:新年快乐!

5.3 反射机制应用于工厂模式

/**
 * ClassName: Test3
 * Function:  反射机制应用于工厂模式
 * date:     2020/01/20
 * author     Luzy
 * version    V1.0
 */
interface Fruit {
    public abstract void eat();
}
class Apple implements Fruit {
    public void eat() {
        System.out.println("Apple");
    }
}
class Orange implements Fruit {
    public void eat() {
        System.out.println("Orange");
    }
}
class Factory {
    public static Fruit getInstance(String ClassName) {
        Fruit f = null;
        try {
            f = (Fruit) Class.forName(ClassName).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return f;
    }
}
/**
 * 对于普通的工厂模式当我们在添加一个子类的时候,就需要对应的修改工厂类。 当我们添加很多的子类的时候,会很麻烦。
 *
 * 现在我们利用反射机制实现工厂模式,可以在不修改工厂类的情况下添加任意多个子类。
 *
 * 但是有一点仍然很麻烦,就是需要知道完整的包名和类名,这里可以使用properties配置文件来完成。
 *
 * java 读取 properties 配置文件 的方法可以自行百度
 *
 *
 */
public class Test3 {
    public static void main(String[] args) throws Exception {
        Fruit f = Factory.getInstance("Apple");
        if (f != null) {
            f.eat();
        }
    }
}

输出结果:

Apple

posted @ 2020-07-14 11:30  你在谁的风景里a  阅读(116)  评论(0编辑  收藏  举报