目录
Java 反射机制是 Java 语言一个很重要的特性,它是 Java “动态性”的重要体现。反射机 制可以让程序在运行时加载编译期完全未知的类,让我们的程序更加灵活、更加开放。但是,反射机制的不足就是会大大降低程序执行的效率。
反射机制在实际开发中直接使用的并不多,但是很多框架底层都会使用到反射机制。因 此,理解反射机制对于大家后面学习更加深入的知识非常必要。
动态语言
动态语言是指在程序运行时,可以改变程序的结构或变量的类型。典型的动态语言有: Python、Ruby、JavaScript 等。
JavaScript 代码演示动态改变程序结构
function test(){
var s = "var a=3;var b=5;alert(a+b);";
eval(s);
}
通过上面的代码,我们会发现 JavaScript 可以将字符串里面的源代码进行执行。也就是 说,外部传入的字符串是什么内容,我就执行什么;这样在执行的时候,完全改变了原来源 代码的结构。 这种动态性,让程序更加灵活,更加具有开放性。
Java 语言虽然具有动态性,但是 Java 并不是动态语言。我们可以利用反射机制、字节 码操作获得类似动态语言的特性。
反射机制的本质和 Class 类
学习反射机制基本就等同于学习 Class 类的用法!理解 Class 类也就是理解了反射机制。
JAVA 反射机制让我们在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法; 这种动态获取的以及动态调用 对象的方法的功能就是“Java 的反射机制”。
反射机制的本质
反射机制(Reflection ) 是 Java 动态性的重要体现。我们可以通过反射机制在运行时加载 编译期完全未知的类。通过反射机制,我们可以在运行时加载需要的类,从而动态的改变了 程序结构,使我们的程序更加灵活、更加开放。
但是反射机制也有缺点,那就是效率问题。反射会大大降低程序的执行效率。同时,反 射机制绕过了源代码,也会给代码的维护增加困难。
首先,我们先看一段极其简单的代码。这段代码创建了一个 User 对象,但是其内存结构到底是怎么样的呢?
创建 User 对象
public class Test01 {
public static void main(String[] args) {
User user = new User("王五");
}
}
实际上,我们在加载任何一个类时都会在方法区中建立“这个类对应的 Class 对象”,由 于“Class 对象”包含了这个类的整个结构信息,所以我们可以通过这个“Class 对象”来操作这 个类。
我们要使用一个类,首先要加载类;加载完类之后,在堆内存中,就产生了一个 Class 类 型的对象(一个类只有一个 Class 对象),这个对象就包含了完整的类的结构信息。我们可 以通过这个对象知道类的结构。这个对象就像一面镜子,透过这个镜子可以看到类的结构, 所以,我们形象的称之为:反射。因此,“Class 对象”是反射机制的核心。
通过 Class 类动态加载某个类
Class c = Class.forName("com.dlsfee.test.User");
Class.forName()可以让我们在运行时再决定加载什么样的类,字符串传入什么类,我就 加载什么类,完全和源代码无关,这就是“动态性”。反射让我们实现了“运行时加载、探知、 使用,而编译期间完全未知的类”。
反射机制的核心就是“Class 对象”。获得了 Class 对象,就相当于获得了类结构;我们可 以通过“Class 对象”调用这个类的所有属性、所有方法、构造方法;这样,我们就可以动态 加载、运行相关的类。
java.lang.Class 类
java.lang.Class 类是反射(Reflection)的根源。针对任何您想动态加载、运行的类,唯 有 先 获 得 相 应 的 Class 对 象 。 java.lang.Class 类 十 分 特 殊 , 用 来 表 示 Java 中 类 型(class/interface/enum/annotation/primitive type/void)本身。
Class 类的对象如何获取?
1. 运用 getClass()
2. 运用.class 语法
3. 运用 Class.forName()(最常被使用)
获取 Class 类对象 3 种方式
/**
* 测试获得Class对象的三种方式.
*/
public class Test01 {
public static void main(String[] args) throws Exception {
User user = new User("王五");
Class c1 = user.getClass(); //方式一
Class c2 = User.class; //方式二
Class c3 = Class.forName("com.reflex.User"); //方式三
System.out.println(c1 == c2); //true
System.out.println(c1 == c3);//true
//获取类的名字
System.out.println(c1.getName()); //输出“包名+类名::com.reflex.User
System.out.println(c1.getSimpleName()); //输出”类名“:User
}
}
由于系统针对每个类只会创建一个 Class 对象,因此,我们发现上面三个变量 c1/c2/c3 实际指向的是同一个对象。