Fork me on GitHub

浅谈Java的反射原理

Java的编译过程

谈及反射,不得不先了解一下,java的整个编译过程,整体的java编译过程可以参考 之前的一篇 一个java文件被执行的历程 

这里我们只针对 对象这一层级来讨论,一个java文件,我们经过编译,会得出 一个 字节码文件(.class),这时候,进入解释阶段,编译器会将这个.class加载进内存中,此时,它首先会生成一个 Class对象。

Class对象与对象形成的过程

 Class对象

在java世界里,一切皆对象。从某种意义上来说,java有两种对象:实例对象和Class对象。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。

Class对象会有以下几个特点:
  1. Class 对象只能由系统建立对象
  2. 一个类在 JVM 中只会有一个Class实例
  3. –每个类的实例都会记得自己是由哪个 Class 实例所生成

class对象包含的信息有:

  • 数据成员名
  • 内部方法
  • 构造方法
  • 集成自哪个接口、类

 对象的形成过程

我们的实例对象是通过Class对象来创建的。

每一个类都有一个Class对象,每当编译一个新类就产生一个Class对象,基本类型 (boolean, byte, char, short, int, long, float, and double)有Class对象,数组有Class对象,就连关键字void也有Class对象(void.class)。Class对象对应着java.lang.Class类,如果说类是对象抽象和集合的话,那么Class类就是对类的抽象和集合。

Class类没有公共的构造方法,Class对象是在类加载的时候由Java虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。

在类加载阶段,类加载器首先检查这个类的Class对象是否已经被加载。如果尚未加载,默认的类加载器就会根据类的全限定名查找.class文件。在这个类的字节码被加载时,它们会接受验证,以确保其没有被破坏,并且不包含不良java代码。一旦某个类的Class对象被载入内存,我们就可以它来创建这个类的所有对象。

反射的本质及应用

  上面的基础了解完毕,我们进入今天的主体,何为反射。

所谓反射,官方的定义是: 指计算机程序在运行时(runtime) 可以访问、检测和修改它本身状态或行为的一种能力。通俗说,反射就是程序在运行的时候能够“观察”并且修改自己的行为,是程序对自身的反思、自检、修改。

  理解反射,首先得知道它的对立面,“正射”

“正射”

前面说到了Class对象,每个类的运行时的类型信息就是用Class对象表示的。系统会自动创建唯一一个Class对象,它包含了与类有关的信息。此时的java文件(一个类)处于一个中间状态,并不是我们使用的对象,只有当我们使用  “ new  Object()”时,才会在JVM堆中根据这个Class对象来产生真正供我们使用的实例对象。其实也就是上面部分的对象的形成过程。

正射的使用意义是,我事先定义了一个对象的某些东西,然后当我需要的时候,我会通知内存去创建这个对象,然后我事先知道这个对象有什么,所以我会精准的调用它的某个方法,某个成员变量。

用一个我们习以为常的demo来举一下例:

class Human {
    String name;
    int age;
    String nation;
    Human(String name,int age,String nation) {
        this.name=name;
        this.age=age;
        this.nation=nation;
    }
    void changeName(String name){
        this.name=name;
    }
}
public class Main {
    public static void main(String[] args){
        Human human=new Human("张三",22,"中国");
        human.changeName("李四");
    }
}

在上面Main类的main方法中,我之所以可以写human.changeName(“张三”) 是因为Human类也是我自己写的,我清楚的知道,human作为Human的实例对象,可以调用changeName方法,如果我手抖写了调用changeNamee方法,我也会立即改回来,因为我知道Human里没有这个方法。假如我不知道Human里有没有一个改名字的方法,即Human类对我来说是不透明的第三方类,我尝试性的在程序中调用了一个newName方法,保存、编译。这时候编译器会通知我,这样写程序是不对的,Human里没有一个叫newName的方法,编译失败。

反射

假如此时我只想使用对象某一个方法,某一个成员变量,而不想用其他的部分,这时候如果用“正射”(走正常的对象创建过程,new一下),就会被迫创建一个完整的该对象(有一说一,有点浪费),此时我可以根据类的全路径+名称,去内存中拿出这个类的Class对象,根据这个Class对象,灵活的去获取这个类片面的信息。这种在运行时访问、修改对象的状态和行为,可以给程序带来极大的灵活性。这便是反射

反射可提供的功能

  1. 在运行时判断任意一个对象所属的类。

  2. 在运行时构造任意一个类的对象。

  3. 在运行时判断任意一个类所具有的成员变量和方法。

  4. 在运行时调用任意一个对象的方法。

  5. 生成动态代理。

详细的API使用,可以查阅java的官方文档。

反射的使用

  使用Java注解配合反射可以开发出各种工具、框架。例如,Spring中的注解、工厂模式中创建对象的方式等

这里实现一个用自定义注解 @AutoField 实现为属性赋值。

定义注解@AutoField 

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoField {
    String value() default "";
}

编写解析类。BeanFactory中的createBean方法通过反射拿到注解 @AutoField的值并赋给对象。

public class BeanFactory {
    public static <T> T createBean(Class<T> clazz) {
        T o = null;
        try {
            o = clazz.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (declaredField.isAnnotationPresent(AutoField.class)) {
                AutoField AutoField = declaredField.getAnnotation(AutoField.class);
                if (!declaredField.isAccessible())
                    declaredField.setAccessible(true);
                try {
                    if (declaredField.getType().getSimpleName().equals("String"))
                        declaredField.set(o, AutoField.value());
                    else if (declaredField.getType().getSimpleName().equals("byte"))
                        declaredField.set(o, Byte.parseByte(AutoField.value()));
                    else if (declaredField.getType().getSimpleName().equals("short"))
                        declaredField.set(o, Short.parseShort(AutoField.value()));
                    else if (declaredField.getType().getSimpleName().equals("int"))
                        declaredField.set(o, Integer.parseInt(AutoField.value()));
                    else if (declaredField.getType().getSimpleName().equals("long"))
                        declaredField.set(o, Long.parseLong(AutoField.value()));
                    else if (declaredField.getType().getSimpleName().equals("float"))
                        declaredField.set(o, Float.parseFloat(AutoField.value()));
                    else if (declaredField.getType().getSimpleName().equals("double"))
                        declaredField.set(o, Double.parseDouble(AutoField.value()));
                    else if (declaredField.getType().getSimpleName().equals("long"))
                        declaredField.set(o, Long.parseLong(AutoField.value()));
                    else if (declaredField.getType().getSimpleName().equals("boolean"))
                        declaredField.set(o, Boolean.parseBoolean(AutoField.value()));
                    else
                        throw new RuntimeException(declaredField.getName() + " of " + clazz.getName() + " is not a value field");
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }

            }
        }
        return o;
    }
}

  定义实体类

public class Teacher {

  @AutoField("12223")
  private int id;

  @AutoField("Zhang")
  private String name;

  @AutoField("20")
  private int age;

  @AutoField("false")
  private boolean isProfessor;

  @AutoField("G")
  private String sex;

  @AutoField("CQU")
  private String school;

  public int getId() {
    return id;
  }

  public String getName() {
    return name;
  }

  public int getAge() {
    return age;
  }

  public boolean isProfessor() {
    return isProfessor;
  }

  public String getSex() {
    return sex;
  }

  public String getSchool() {
    return school;
  }

  public void setId(int id) {
    this.id = id;
  }

  public void setName(String name) {
    this.name = name;
  }

  public void setAge(int age) {
    this.age = age;
  }

  public void setProfessor(boolean professor) {
    isProfessor = professor;
  }

  public void setSex(String sex) {
    this.sex = sex;
  }

  public void setSchool(String school) {
    this.school = school;
  }

  @Override
  public String toString() {
    return "Teacher{" +
      "id=" + id +
      ", name='" + name + '\'' +
      ", age=" + age +
      ", isProfessor=" + isProfessor +
      ", sex=" + sex +
      ", school='" + school + '\'' +
    '}';
  }
}

 

测试

public class Main {
    public static void main(String[] args) {
        Teacher teacher = BeanFactory.createBean(Teacher.class);
        System.out.println(teacher);
    }

 

 

 

  

posted @ 2021-03-13 11:45  糖拌西红柿  阅读(601)  评论(0编辑  收藏  举报