反射 将类的各个组成部分封装为其他对象,这就是反射机制

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

反射就是把Java类中的各个成分映射成一个个的Java对象。即在运行状态中,

  • 对于任意一个类,都能够知道这个类的所以属性和方法;
  • 对于任意一个对象,都能调用它的任意一个方法和属性

这种动态获取信息及动态调用对象方法的功能叫Java的反射机制

之前写到了设计模式的代理模式,因为下一篇动态代理等内容需要用到反射的知识,所以在之前Java篇的基础上再写一篇有关反射的内容,还是以实际的程序为主,了解反射是做什么的、应该怎么用。

框架设计的灵魂

框架设计的灵魂.png

反射机制的功能

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

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

实现反射机制的类

Java中主要由以下的类来实现Java反射机制(这些类都位于java.lang.reflect包中):

  • Class类:代表一个类。 Field类:代表类的成员变量(成员变量也称为类的属性)。
  • Method类:代表类的方法。
  • Constructor类:代表类的构造方法。
  • Array类:提供了动态创建数组,以及访问数组的元素的静态方法。

setAccessible(true):暴力反射

设置后,可以把类中的所有私有属性获取出来

  • Class对象
  • Class对象中的功能
  • Class对象功能中的方法

获取全类名

  1. String getName()
  2. Person.class.getName()

获取Class对象的方式

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

Class.forName(“全类名”)

将字节码文件加载进内存,返回Class对象,多用于配置文件,将类名定义在配置文件中。读取文件,加载类

  1. Class.forName("cn.itcast.domain.Person");

类名.class

通过类名的属性class获取,多用于参数的传递

对象.getClass()

getClass()方法在Object类中定义着,多用于对象的获取字节码的方式

  1. // 获得对象的类
  2. Class classType=object.getClass();

Class对象功能

获取成员变量

方法 说明
Field[] getFields() 获取所有public修饰的成员变量
Field getField(String name) 获取指定名称的 public修饰的成员变量
Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
Field getDeclaredField(String name) 获取指定名称的成员变量,不考虑修饰符
Field:成员变量
- [x] 设置值 void set(Object obj, Object value)
- [x] 获取值 get(Object obj)


忽略访问权限修饰符的安全检查
setAccessible(true):暴力反射
Class<Person> personClass = Person.class;

//Field[] getFields()获取所有public修饰的成员变量
Field[] field = personClass.getFields();
Arrays.stream(field).forEach(System.out::println);

Person person = new Person();
personClass.getField("b").set(person, "张三");
System.out.println(person);

Object o = personClass.getField("b").get(person);
System.out.println(o);

//获取所有的成员变量,不考虑修饰符
Field[] declaredFields = personClass.getDeclaredFields();
Arrays.stream(declaredFields).forEach(System.out::println);

Field name = personClass.getDeclaredField("name");
name.setAccessible(true);  //暴力反射 忽略访问权限修饰符的安全检查
Object o1 = name.get(person);
System.out.println(o1);
System.out.println(name);

获取构造方法

方法 说明
Constructor<?>[] getConstructors()
Constructor getConstructor(类<?>… parameterTypes)
Constructor getDeclaredConstructor(类<?>… parameterTypes)
Constructor<?>[] getDeclaredConstructors()
Constructor:构造方法
- 创建对象 T newInstance(Object… initargs)
- 如果使用空参数构造方法创建对象,操作可以简化:
- Class对象.newInstance方法
Class<Person> personClass = Person.class;
Constructor<?>[] constructors = personClass.getConstructors();
Arrays.stream(constructors).forEach(System.out::println);

Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
Person name = constructor.newInstance("张三", 18);  //创建对象,传值
Person nullname =personClass.getConstructor().newInstance();
Person nullname1 = personClass.newInstance();

System.out.println(name);
System.out.println(nullname);

获取成员方法

方法 说明
Method[] getMethods()
Method getMethod(String name, 类<?>… parameterTypes)
Method[] getDeclaredMethods()
Method getDeclaredMethod(String name, 类<?>… parameterTypes)
Method:方法对象
- 执行方法: Object invoke(Object obj, Object… args)

案例

需求:写一个”框架”,不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
settings.properties

classname = domain.Teacher
methodName = speack

执行文件.png

设计代理模式

Customer.java

public class Customer {
    private Long id;
    private String name;
    private int age;

    public Customer() {}

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

    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id=id;
    }
    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;
    }
}

WriteFile.java

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;

/**
 * @ClassName: WriteFile
 * @Description: 写文件操作
 * @author 一缕清风
 * @date 2021年12月01日
 *
 */
public class WriteFile {
    private static String pathname = "./out.txt";
    public static void write(StringBuffer sBuffer) throws Exception {
        File file = new File(pathname);
        BufferedWriter bw = new BufferedWriter(new FileWriter(file));
        bw.write(sBuffer.toString());
        bw.close();
    }

}

ReflectTester.java

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectTester {
    public Object copy(Object object) throws Exception{
        //获得对象的类型
        Class classType=object.getClass();
        System.out.println("Class:"+classType.getName());

        //通过默认构造方法创建一个新的对象
        Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});

        //获得对象的所有属性
        Field fields[]=classType.getDeclaredFields();

        for(int i=0; i<fields.length;i++){
              Field field=fields[i];

              String fieldName=field.getName();
              String firstLetter=fieldName.substring(0,1).toUpperCase();
              //获得和属性对应的getXXX()方法的名字
              String getMethodName="get"+firstLetter+fieldName.substring(1);
              //获得和属性对应的setXXX()方法的名字
              String setMethodName="set"+firstLetter+fieldName.substring(1);

              //获得和属性对应的getXXX()方法
              Method getMethod=classType.getMethod(getMethodName,new Class[]{});
              //获得和属性对应的setXXX()方法
              Method setMethod=classType.getMethod(setMethodName,new Class[]{field.getType()});

              //调用原对象的getXXX()方法
              Object value=getMethod.invoke(object,new Object[]{});
              System.out.println(fieldName+":"+value);
              //调用拷贝对象的setXXX()方法
             setMethod.invoke(objectCopy,new Object[]{value});
        }
        return objectCopy;
     }

}

ReflexDemo.java

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @ClassName: ReflexDemo
 * @Description: 通过反射获取类、属性及方法
 * @author 一缕清风
 * @date 2021年12月01日
 *
 */
public class ReflexDemo {

    private static StringBuffer sBuffer;

    public static void getJar(String jar) throws Exception {
        try {
            File file = new File(jar);
            URL url = file.toURI().toURL();
            URLClassLoader classLoader = new URLClassLoader(new URL[] { url },
                    Thread.currentThread().getContextClassLoader());

            JarFile jarFile = new JarFile(jar);
            Enumeration<JarEntry> enumeration = jarFile.entries();

            JarEntry jarEntry;
            sBuffer = new StringBuffer();    //存数据
            while (enumeration.hasMoreElements()) {
                jarEntry = enumeration.nextElement();
                if (jarEntry.getName().indexOf("META-INF") < 0) {
                    String classFullName = jarEntry.getName();
                    if (classFullName.indexOf(".class") < 0) {
                        classFullName = classFullName.substring(0, classFullName.length() - 1);
                    } else {
                        // 去除后缀.class,获得类名
                        String className = classFullName.substring(0, classFullName.length() - 6).replace("/", ".");
                        Class<?> myClass = classLoader.loadClass(className);
                        sBuffer.append("类名\t:" + className);
                        System.out.println("类名\t:" + className);

                        // 获得属性名
                        Class<?> clazz = Class.forName(className);
                        Field[] fields = clazz.getDeclaredFields();
                        for (Field field : fields) {
                            sBuffer.append("属性名\t:" + field.getName() + "\n");
                            System.out.println("属性名\t:" + field.getName());
                            sBuffer.append("-属性类型\t:" + field.getType() + "\n");
                            System.out.println("-属性类型\t:" + field.getType());
                        }

                        // 获得方法名
                        Method[] methods = myClass.getMethods();
                        for (Method method : methods) {
                            if (method.toString().indexOf(className) > 0) {
                                sBuffer.append("方法名\t:" + method.toString().substring(method.toString().indexOf(className)) + "\n");
                                System.out.println("方法名\t:" + method.toString().substring(method.toString().indexOf(className)));
                            }
                        }
                        sBuffer.append("--------------------------------------------------------------------------------" + "\n");
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sBuffer.append("End");
            WriteFile.write(sBuffer);    //写文件
        }
    }

}

Main.java

/**Main.java
 * @ClassName: Main
 * @Description:
 * @author 一缕清风
 * @date 2021年12月01日
 *
 */
public class Main {
  private static String jar = "lib/dt.jar";
  public static void main(String[] args) throws Exception {
      ReflexDemo.getJar(jar);
  }
}