1.反射简介

Reflectionη(反射)是Java被视为动态语言的关键, 反射机制允许程序在执行期借助于 Reflection API取得任何类的内部信息,并能直接操作仼意对象的內部属性及方法。

Class c= Class.forName("java.lang.String")
加载完类之后在堆内存的方法区中就产生了一个C类型的对象(一个类只有一个Ca对象),
这个对象就包含了完整的类的结构信息,我们可以通过这个对象看到类的结构。
这个对象就像一面镜子,透过这个镜子看到类的结构,所以我们形象的称之为: 反射
image.png

反射机制主要提供以下功能:

①在运行时判断任意一个对象所属的类;
②在运行时构造任意一个类的对象;
③在运行时判断任意一个类所具有的成员变量和方法;
④在运行时调用任意一个对象的方法;
⑤生成动态代理。

反射的优势与弊端:
反射虽然很灵活,能够使得写的代码,变的大幅精简,所以在用的时候,一定要注意具体的应用场景,反射的优缺点如下:
优点:
(1)能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。
(2)与Java动态编译相结合,可以实现无比强大的功能

缺点:
(1)使用反射的性能较低
(2)使用反射相对来说不安全
(3)破坏了类的封装性,可以通过反射获取这个类的私有方法和属性

任何事物,都有两面性,反射的优点,也同是就是它的缺点,所以,没有好与坏,只有最合适的场景。


2.反射的相关API

  • java.lang.Class 代表一个类
  • java.lang.reflect.Method 代表类的方法
  • java.lang.reflect.Field 代表类的成员变量
  • java.lang.reflect.Constructor 代表类的构造方法

    对象照镜子后可以得到的信息: 某个类的属性、方法和构造器、某个类到底实现了哪些接口.
    对于每个类而言,JRE都为其保留一个不变的cass类型的对象。
    一个 Class对象包含了特定某个结构( class/interface/enum/annotation/primitive type/od)的有关信息。

  • Class 本身也是一个类

  • Class 对象只能由系统建立对象
  • 一个加载的类在JVM中只会有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个.class文件
  • 每个类的实例都会记得自己是由哪个Class实例所生成
  • 通过Class可以完整地得到一个类中的所有被加载的结构
  • Class类是 Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

2.1 获取Class类的实例

获取Class对象的三种方式

方式一: 通过对象获得 (Object类中的getClass()方法)
多用于对象的获取字节码的方式

  1. Person p = new Person();
  2. Class c = p.getClass();

方式二: 类名.class 获取到字节码文件对象
(任意数据类型都具备一个class静态属性,看上去要比第一种方式简单)。
多用于参数的传递

  1. Class c2 = Person.class;

方式三: Class.forName获得
(将类名作为字符串传递给Class类中的静态方法forName即可)。
多用于配置文件, 将类名定义在配置文件中. 读取文件, 加载类

  1. Class c3 = Class.forName("com.jdxia.Person");
  • 注意:第三种和前两种的区别
    前两种你必须明确Person类型.后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了


2.2 获取Field属性

API接口

方法名 描述
Field getDeclaredField(String name) 返回一个 Field对象,它反映此表示的类或接口的指定已声明字段 类对象。
Field[] getDeclaredFields() 返回的数组 Field对象反映此表示的类或接口声明的所有字段 类对象。
Field getField(String name) 返回一个 Field对象,它反映此表示的类或接口的指定公共成员字段 类对象。
Field[] getFields() 返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段 类对象

//忽略访问权限修饰符的安全检查
d.setAccessible(true);//暴力反射

Person.class

  1. package reflect;
  2. /**
  3. * @program: practice
  4. * @description:
  5. * @author: yuanzi
  6. **/
  7. public class Person {
  8. String name;
  9. Integer age;
  10. public String a;
  11. protected String b;
  12. String c;
  13. private String d;
  14. public Person(String name, Integer age) {
  15. this.name = name;
  16. this.age = age;
  17. }
  18. Person(){}
  19. public String getName() {
  20. return name;
  21. }
  22. public Integer getAge() {
  23. return age;
  24. }
  25. public void setName(String name) {
  26. this.name = name;
  27. }
  28. public void setAge(Integer age) {
  29. this.age = age;
  30. }
  31. public void eat(){
  32. System.out.println("吃饭...");
  33. }
  34. public void eat(String str){
  35. System.out.println("吃饭..." + str);
  36. }
  37. }

测试代码

  1. package reflect;
  2. import java.lang.reflect.Field;
  3. public class ReflectDemo2 {
  4. public static void main(String[] args) throws Exception {
  5. //0.获取PersonClass对象
  6. Class personClass = Person.class;
  7. /*
  8. 1. 获取成员变量们
  9. * Field[] getFields()
  10. * Field getField(String name)
  11. * Field[] getDeclaredFields()
  12. * Field getDeclaredField(String name)
  13. */
  14. //1.Field[] getFields()获取所有public修饰的成员变量
  15. Field[] fields = personClass.getFields();
  16. for (Field field : fields) {
  17. System.out.println(field);
  18. }
  19. System.out.println("------------");
  20. //2.Field getField(String name)
  21. Field a = personClass.getField("a");
  22. //获取成员变量a 的值
  23. Person p = new Person();
  24. Object value = a.get(p);
  25. System.out.println(value);
  26. //设置a的值
  27. a.set(p,"张三");
  28. System.out.println(p);
  29. System.out.println("===================");
  30. //3. Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符
  31. Field[] declaredFields = personClass.getDeclaredFields();
  32. for (Field declaredField : declaredFields) {
  33. System.out.println(declaredField);
  34. }
  35. //4. Field getDeclaredField(String name)
  36. Field d = personClass.getDeclaredField("d");
  37. //忽略访问权限修饰符的安全检查
  38. d.setAccessible(true);//暴力反射
  39. Object value2 = d.get(p);
  40. System.out.println(value2);
  41. }
  42. }

执行结果
image.png


2.3 获取构造方法们

API接口

方法名 描述
Constructor getConstructor(class<?>… parameterTypes) 返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共 类函数。
Constructor<?>[] getConstructors() 返回的数组 Field对象反映此表示的类或接口声明的所有字段 类对象。
Constructor getDeclaredConstructor(class<?>… parameterTypes) 返回一个 Constructor对象,该对象反映 Constructor对象表示的类或接口的指定 类函数。
Constructor<?>[] getDeclaredConstructors()返回一个反映 Constructor对象表示的类声明的所有 Constructor对象的数组 类 。

测试代码

  1. package reflect;
  2. import java.lang.reflect.Constructor;
  3. public class ReflectDemo3 {
  4. public static void main(String[] args) throws Exception {
  5. //0.获取PersonClass对象
  6. Class personClass = Person.class;
  7. /*
  8. 2. 获取构造方法们
  9. * Constructor<?>[] getConstructors()
  10. * Constructor<T> getConstructor(类<?>... parameterTypes)
  11. * Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
  12. * Constructor<?>[] getDeclaredConstructors()
  13. */
  14. //Constructor<T> getConstructor(类<?>... parameterTypes)
  15. Constructor constructor = personClass.getConstructor(String.class, Integer.class);
  16. System.out.println(constructor);
  17. //创建对象
  18. Object person = constructor.newInstance("张三", 23);
  19. System.out.println(person);
  20. System.out.println("----------");
  21. Constructor constructor1 = personClass.getConstructor();
  22. System.out.println(constructor1);
  23. //创建对象
  24. Object person1 = constructor1.newInstance();
  25. System.out.println(person1);
  26. Object o = personClass.newInstance();
  27. System.out.println(o);
  28. //constructor1.setAccessible(true);
  29. }
  30. }

执行结果
image.png

2.4 获取成员方法们

API接口

方法名 描述
Method getMethod(String name, class
<?>… parameterTypes)
返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法 类对象。
Method[] getMethods() 返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 类对象,包括那些由类或接口和那些从超类和超接口继承的声明。
Method getDeclaredMethod(String name, class
<?>… parameterTypes)
返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 类对象。
Method[] getDeclaredMethods() 返回包含一个数组 方法对象反射的类或接口的所有声明的方法,通过此表示 类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。

测试代码

package reflect;
import java.lang.reflect.Method;

public class ReflectDemo4 {
    public static void main(String[] args) throws Exception {

        //0.获取Person的Class对象
        Class personClass = Person.class;
        /*
          3. 获取成员方法们:
             * Method[] getMethods()
             * Method getMethod(String name, 类<?>... parameterTypes)

             * Method[] getDeclaredMethods()
             * Method getDeclaredMethod(String name, 类<?>... parameterTypes)
         */
        //获取指定名称的方法
        Method eat_method = personClass.getMethod("eat");
        Person p = new Person();
        //执行方法
        eat_method.invoke(p);

        Method eat_method2 = personClass.getMethod("eat", String.class);
        //执行方法
        eat_method2.invoke(p,"str");

        System.out.println("-----------------");

        //获取所有public修饰的方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
            String name = method.getName();
            System.out.println(name);
            //method.setAccessible(true);
        }

        //获取类名
        String className = personClass.getName();
        System.out.println(className);//reflect.Person
    }
}

执行结果
image.png


3.反射机制的应用实例

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

package net.xsoftlab.baike;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class TestReflect {
    public static void main(String[] args) throws Exception {
        ArrayList<Integer> list = new ArrayList<Integer>();
        Method method = list.getClass().getMethod("add", Object.class);
        method.invoke(list, "Java反射机制实例。");
        System.out.println(list.get(0));
    }
}

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

package net.xsoftlab.baike;
import java.lang.reflect.Array;
public class TestReflect {
    public static void main(String[] args) throws Exception {
        int[] temp = { 1, 2, 3, 4, 5 };
        Class<?> demo = temp.getClass().getComponentType();
        System.out.println("数组类型: " + demo.getName());
        System.out.println("数组长度  " + Array.getLength(temp));
        System.out.println("数组的第一个元素: " + Array.get(temp, 0));
        Array.set(temp, 0, 100);
        System.out.println("修改之后数组第一个元素为: " + Array.get(temp, 0));
    }
}

通过反射机制修改数组的大小

package net.xsoftlab.baike;
import java.lang.reflect.Array;
public class TestReflect {
    public static void main(String[] args) throws Exception {
        int[] temp = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        int[] newTemp = (int[]) arrayInc(temp, 15);
        print(newTemp);
        String[] atr = { "a", "b", "c" };
        String[] str1 = (String[]) arrayInc(atr, 8);
        print(str1);
    }
    // 修改数组大小
    public static Object arrayInc(Object obj, int len) {
        Class<?> arr = obj.getClass().getComponentType();
        Object newArr = Array.newInstance(arr, len);
        int co = Array.getLength(obj);
        System.arraycopy(obj, 0, newArr, 0, co);
        return newArr;
    }
    // 打印
    public static void print(Object obj) {
        Class<?> c = obj.getClass();
        if (!c.isArray()) {
            return;
        }
        System.out.println("数组长度为: " + Array.getLength(obj));
        for (int i = 0; i < Array.getLength(obj); i++) {
            System.out.print(Array.get(obj, i) + " ");
        }
        System.out.println();
    }
}

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

package net.xsoftlab.baike;
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;
    }
}
/**
 * 对于普通的工厂模式当我们在添加一个子类的时候,就需要对应的修改工厂类。 当我们添加很多的子类的时候,会很麻烦。
 * Java 工厂模式可以参考
 * http://baike.xsoftlab.net/view/java-factory-pattern
 * 
 * 现在我们利用反射机制实现工厂模式,可以在不修改工厂类的情况下添加任意多个子类。
 * 
 * 但是有一点仍然很麻烦,就是需要知道完整的包名和类名,这里可以使用properties配置文件来完成。
 * 
 * java 读取 properties 配置文件 的方法可以参考
 * http://baike.xsoftlab.net/view/java-read-the-properties-configuration-file
 * 
 * @author xsoftlab.net
 */
public class TestReflect {
    public static void main(String[] args) throws Exception {
        fruit f = Factory.getInstance("net.xsoftlab.baike.Apple");
        if (f != null) {
            f.eat();
        }
    }
}

可以创建任意类的对象,可以执行任意方法

package 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目录下的配置文件
        ClassLoader classLoader = ReflectTest.class.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);
    }
}

测试结果
image.png