java--反射

一、反射的概述

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.


以上的总结就是什么是反射
反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
如图是类的正常加载过程:反射的原理在与class对象。
熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。

二、Java中为什么需要反射?反射要解决什么问题?

Java中编译类型有两种:

  • 静态编译:在编译时确定类型,绑定对象即通过。
  • 动态编译:运行时确定类型,绑定对象。动态编译最大限度地发挥了Java的灵活性,体现了多态的应用,可以减低类之间的耦合性。

Java反射是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public、static等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。

Reflection可以在运行时加载、探知、使用编译期间完全未知的classes。即Java程序可以加载一个运行时才得知名称的class,获取其完整构造,并生成其对象实体、或对其fields设值、或唤起其methods。

反射(reflection)允许静态语言在运行时(runtime)检查、修改程序的结构与行为。
在静态语言中,使用一个变量时,必须知道它的类型。在Java中,变量的类型信息在编译时都保存到了class文件中,这样在运行时才能保证准确无误;换句话说,程序在运行时的行为都是固定的。如果想在运行时改变,就需要反射这东西了。

实现Java反射机制的类都位于java.lang.reflect包中:

  1. Class类:代表一个类
  2. Field类:代表类的成员变量(类的属性)
  3. Method类:代表类的方法
  4. Constructor类:代表类的构造方法
  5. Array类:提供了动态创建数组,以及访问数组的元素的静态方法

一句话概括就是使用反射可以赋予jvm动态编译的能力,否则类的元数据信息只能用静态编译的方式实现,例如热加载,Tomcat的classloader等等都没法支持。

三、使用

先创建一个对象

package com.example.jdk8demo;

import com.alibaba.fastjson.JSON;

public class Student {
    private String name;
    private Integer age;
    private Status status;

    public String name1;
    public Integer age1;

    private String getName1(){
        return this.name1;
    }

    private String setName(String name){
        this.name = name;
        return "调用setName成功,设置为"+this.name;
    }

    private String setName1(String name){
        this.name1 = name;
        return "调用setName1成功,设置为"+this.name1;
    }

    public Student(String name,Integer age){
        this.name = name;
        this.age = age;
    }

    public Student(){

    }

    public Student(String name,Integer age, Status status){
        this.name = name;
        this.age = age;
        this.status = status;
    }

    public String getName() {
        return name;
    }

    public Integer getAge() {
        return age;
    }

    public Status getStatus(){
        return status;
    }



    public enum Status{
        FREE,
        BUSY,
        VOCATION
    }

}

反射使用:

(1)获取反射对象

package com.example.jdk8demo;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

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

@SpringBootTest
@Slf4j
class FanSheTest {

    @Test
    public void test() throws Exception{
        //获取反射对象
        Student student = new Student();
        Class studentClass1 = student.getClass();
        log.info("student.getClass()反射结果{}",studentClass1.getName());
        Class studentClass2 = Student.class;
        log.info("Student.class反射结果{}",studentClass2.getName());
        log.info("Student.class==student.getClass()结果{}",studentClass1==studentClass2);
        Class studentClass3 = Class.forName("com.example.jdk8demo.Student");
        log.info("Class.forName反射结果{}",studentClass3.getName());
        log.info("Student.class==Class.forName结果{}",studentClass3==studentClass2);
}

 测试结果:

 由测试结果可见,三种方式得到的结果是一致的

(2)获取相关内容

//获取构造函数集合
        Constructor[] cons = studentClass1.getConstructors();
        log.info("反射获取所有构造函数{}", JSON.toJSONString(cons));
        //获取无参构造
        Constructor con = studentClass1.getConstructor();
        log.info("反射获取无参构造函数{}", JSON.toJSONString(con));
        //获取有参构造
        Constructor con1 = studentClass1.getConstructor(String.class, Integer.class);
        log.info("反射获取有参构造函数{}", JSON.toJSONString(con));

        //获取公有字段,此处字段必须是public修饰,否则会报错
        Field field = studentClass1.getField("name1");
        log.info("公有字段{}", JSON.toJSONString(con));

(3)动态调用方法

        //获取私有方法
        Method setName1 = studentClass1.getDeclaredMethod("setName1", String.class);
        setName1.setAccessible(true);
        String s = (String)setName1.invoke(Student.class.newInstance(),"lcl");
        log.info("========={}", s);

        //获取公有方法
        Method setName = studentClass1.getDeclaredMethod("setName", String.class);
        String ss = (String)setName1.invoke(Student.class.newInstance(),"mm");
        log.info("========={}", ss);

测试结果:

 这里需要特别说明,如果动态调用的是私有方法,则需要使用 setName1.setAccessible(true);  进行解绑操作,才可以使用invoice方法调用。

声明:本文根据https://blog.csdn.net/grandgrandpa/article/details/84832343写的,里面大量复制内容,感谢博主

原文地址:https://www.cnblogs.com/liconglong/p/13036388.html