最近被一条与Java有关的玩梗戳中了笑点:单身的程序员们总是调侃自己,不惧“单身”,随时随地都有“对象”,因为咱们只需要“new一个对象而已”。这不禁让程序员们会心一笑,不过在玩梗的背后,略微思考一番,若是想要new一个对象,至少得需要一个模板吧,那我们就至少需要有一个明确的类才行。那么问题来了,是不是只有通过new关键字才能得到相应的对象呢?
在开始正题之前,需要回顾一个知识点:Java程序运行的状态有哪些?众所周知,程序有编译和运行两种状态,在编译时期,程序会通过javac命令,生成一个或多个.class字节码文件(每个类都对应着一个.class文件),而运行时,会通过Java命令,将一个或多个.class字节码文件加载到内存中,程序才能运行,这个过程是由JVM提供的类加载器完成的。
对于具体的类,就是用于来描述现实生活中的一类事物,类中有属性,方法。但是类又可以是抽象的,因为万物皆可成为类。对于具体的类,我们可以通过NEW关键字来调用,因为在编译期就已经确定,创建什么类的对象,操作什么属性,调用什么方法。比如,创建一个Man类:
public class Man {
String name;
int age;
public Man() {
}
public Man(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;
}
但是,在某种情况下(在实际工作中,需要操作各种类型的数据,比如查询数据库的数据,如果每个类的数据都去查询,那么就写出大量的查询语句,重复且冗余,这时候咱们就得写一个通用的方法),我们需要得知并使用一个在编译时完全未知的类,创建其对象,操作其属性,调用其方法,那么我们需要怎么做呢?这时候就可以不把查询的类写死,用泛型就可以代表通用的查询内容,代码如下:
public class ReflectionTest {
public T get(Classclazz) throws InstantiationException, IllegalAccessException {
//N行查询数据库语句
return clazz.newInstance();
}
}
所以,在编译时不确定的类型用泛型,不确定的对象就用反射。这就是反射的应用场景。接下来就让我们正式步入正题。
反射机制(Reflection):被视为动态语言的关键,可以在运行时创建任意类的对象,操作任意对象的属性,调用任意对象的方法。那么反射机制是如何做到在运行时创建任意类的对象,操作任意对象的属性,调用任意对象的方法的呢?这就得了解JVM的内存结构和类的结构。
在Java中类是如何定义的呢?类是将现实生活中一类事物的共性内容提取出来,抽象成相应的java类,使用Java中的类对其进行描述,这就是类。那么类与类之间是不是也应该存在某些共性内容呢,这是必然的,所以类与类的上层是不是应该还有一个更大的类呢?咱们接下来继续讨论。在JVM运行时,JVM会生成一个Class,而这个Class就是用来描述类的类。这样描述起来比较抽象,话不多说,直接上图:
结合JVM的内存存放规则,如图所示:
由上可知,类是可以通过Class实例得到的,相应的类中的所有信息也能拿到,这就是反射机制的原理。



