理解Java的反射机制

首先我们需要理解动态语言和静态语言

  1. 动态语言 是一类在运行时可以改变其结构的语言: 例如新的函数、对象、甚至新的代码可以被引进,已有的函数可以被删除或是发生其他结构上的变化。 通俗点说就是在运行时代码可以根据某些条件改变自身结构。 主要动态语言:Object-C,C#,JavaScript,PHP,Python,Erlang。
  2. 静态语言 与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java(因为引入了反射机制,Java可以算是准动态语言)、C、C++。

反射机制允许程序在执行期间通过Reflection API取得任何类的内部信息,并能直接操作任意对象内部属性及方法。加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。

Java反射机制提供的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//对于给定Person类
class Person {
//Field
private String name;
int age;
String nation;

//Method
void show(){
System.out.println("I'm a person.")
}
//Constructor
Person();
Person(String name, int age);

private Person(String name);

}
  • 在运行时判断任意一个对象所属的类

  • 在运行时构造任意一个类的对象

    1
    2
    3
    4
    5
    6
    Class clazz = Person.class;Class clazz=Person.class;
    //1.通过反射,创建Person类的对象
    Constructor cons=clazz.getConstructor(String.classint.class);
    Object obj=cons.newInstance("Tom"12);
    Person p=(Person)obj;
    System.out.println(p.tostring());
  • 在运行时判断任意一个类所具有的成员变量和方法

  • 在运行时获取泛型信息

  • 在运行时调用任意一个对象的成员变量和方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //通过反射,调用对象指定的属性,方法

    //调用成员变量
    Field age=clazz.getDeclaredField("age");
    age.set(p,10);
    System.out.println(p.tostring());

    //调用方法
    Method show=clazz.getDeclaredMethod("show");
    show.invoke(p);

    //通过反射调用私有的构造器,方法,属性
    Constructor cons1=clazz.getDeclaredConstructor(String.class);
    cons1.setAccessible(true);
    Person p1=(Person)cons1.newInstance("Jerry");
    System.out.println(p1);
  • 在运行时处理注解

  • 生成动态代理

关于java.lang.CLass类的理解

  1. 类的加载过程: 程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。 接着我们使用java.exe命令对某个字芦码文件迸行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类, 我们就秋为运行时类,此运行时类,就作为CLass的一个实例。
  2. 换句话说,CLass的实例就对应着一个运行时类。
  3. 加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。

获取Class实例的四种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//方式一:调用运行时类的属性:.class
Class clazz1 = Person.class;
System.out.println(clazz1);

//方式二:通过运行附类的对象,调用getClass()
Person p1=new Person();
Class clazz2=pl.getclass();
System.out.println(clazz2);

//方式三:调用CLass的静态方法:forName(String classPath)
Class clazz3=Class.forName("com.demo.java.Person");

//方式四:使用类的加载器:CLassLoader
ClassLoader classLoader=ReflectionTest.class.getClassLoader();
Class clazz4=classLoader.loadClass(name:"com.demo.java.Person");
System.out.println(clazz4);
System.out.println(clazz1==clazz4);
什么时候使用反射构造对象?

在运行时无法确定对象的类型,需要根据动态运行结果去实时调整构造对象的时候,才去使用反射方式。比如在前端发来请求时,根据pattern,通过反射的方式构造。

1
2
3
4
5
6
7
8
9
@RequiresPermissions("system:post:list")
@PostMapping("/list") //此处就采用了反射方式
@ResponseBody
public TableDataInfo list(SysPost post)
{
startPage();
List<SysPost> list = postService.selectPostList(post);
return getDataTable(list);
}