一、Java反射机制概述
- 反射是被视为动态语言的关键,反射机制允许程序在执行期借助于反射API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
- 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以我们形象称之为:反射
1.反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
- 可以解耦,提高程序的可扩展性
2.动态语言 vs 静态语言
- 动态语言是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang。
- 静态语言与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++。Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!
3.关于java.lang.Class类的理解
-
类的加载过程:
程序经过Javac.exe命令后会生成一个或多个字节码文件(.class结尾)
接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于某个字节码文件加载到内存中,此过程就称为类的加载
加载到内存中的类,我们就称为运行时类,此运行时类就作为Class的一个实例 -
换句话说,Class的实例就对应着一个运行时类
-
加载到内存中的运行时类,会缓存一定时间,在此时间内,我们可以通过不同的方式来获取此运行时类
4.获取Class实例的四种方式
- 源代码阶段,
class.forName(“类的路径”)
,将字节码文件加载进内存,返回Class对象。(多用于配置文件,将类名定义在配置文件中。读取文件,加载类)【最常使用】 - Class类对象阶段,
类名.class
,通过类名的属性class获取。(多用于参数的传递,编译的时候已经写死了类名,而我们想的是在运行时想取得哪个类的对象就取得哪个类的对象,所以体现不出动态性) - Runtime运行时阶段,
对象.getClass()
,getClass( )方法在Object类中定义着。(多用于对象的获取字节码的方式,也就是看看这个对象是哪个类造的) - 使用类的加载器:ClassLoader【了解】
package 反射;
/**
* @BelongsProject: untitled
* @BelongsPackage: 反射
* @Author: mcc
* @CreateTime: 2020-11-29 14:18
* @Description:获取Class实例的四种方式
*/
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package 反射;
import org.junit.Test;
/**
* @BelongsProject: untitled
* @BelongsPackage: 反射
* @Author: mcc
* @CreateTime: 2020-11-29 14:21
* @Description:
*/
public class ReflectionTest {
@Test
public void test1() throws Exception{
//1.class.forName(“类的路径”)
Class cls1 = Class.forName("反射.Person");
System.out.println(cls1);
//2.类名.class
Class cls2 = Person.class;
System.out.println(cls2);
//3.对象.getClass()
Person p = new Person();
Class cls3 = p.getClass();
System.out.println(cls3);
//4.比较同一个类的Class对象是否相同
System.out.println(cls1 == cls2);
System.out.println(cls1 == cls3);
//5.使用类的加载器:ClassLoader
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class cls4 = classLoader.loadClass("反射.Person");
System.out.println(cls4);
System.out.println(cls1 == cls4);
}
}
同一个字节码在一次程序运行过程中,只会被加载一次,不论通过哪一种方法获取的Class对象都是同一个。
5.哪些类可以有Class对象
- class:外部类,成员(成员内部类、静态内部类),局部内部类,匿名内部类
- interface:接口
- []:数组
- enum:枚举
- annotation:注解@interface
- primitive type:基本数据类型
- void
@Test
public void test2(){
Class c1 = Object.class;
Class c2 = Comparable.class;
Class c3 = String[].class;
Class c4 = int[][].class;
Class c5 = ElementType.class;
Class c6 = Override.