对于类型与反射,我的理解是在运行时可以动态地对类进行一些操作。
比如:XXX x = new XXX()
这个 XXX
我是可以外部输入的,输入"Cat" 的时候就是 Cat x = new Cat()
,输入"Dog" 的时候,就是 Dog x = new Dog()
。
但是这在一般的编程是很难这样动态地做到的,因此需要反射。
运行 Java 文件的过程
Java 文件的运行过程是这样的:
xxx.java -> 编译 -> xxx.class -> 运行
通俗来讲,每个类型要变成一份“说明书”(xxx.class),然后在 new XXX()
的时候是根据这份“说明书”来创建对象的。
这不禁让我们想到:能不能在运行的时候去修改这份说明书,比如加入变量,修改里面方法等,然后再根据这份“修改过的说明书”去 new 对象呢?
ClassLoader
为了去更深入想上面的问题,先来看看这个玩意:ClassLoader,它负责从外部加载一个类,也就是我们的 xxx.java -> 编译 -> xxx.class
这一步。这个很容易理解,就相当于编译器嘛。
不过它也有下面特点:
- 对应的 Java 文件不一定存在,可以是 Stream,自己创建 Class
- 对应的字节码不一定存在,动态创建 Class
上面的这些特点不禁让我们想到可以“动态地”修改“说明书”了。
反射
反射就是可以在运行的时候去修改这份说明书的,然后我们可以调用 ClassLoader 去编译其实就是我们所说的动态XXXX。有了反射,我们可以
- 动态创建一个对象
- 动态调用一个方法
- 动态获取一个属性
动态创建一个对象
String className = args[0];
Class c = Class.forName(className);
Object obj = c.getConstructor().newInstance();
动态调用一个方法
Cat cat = new Cat();
cat.getClass().getMethod(args[0]).invoke();
动态获取一个属性
Cat cat = new Cat();
cat.getClass().getField(args[0]).get(cat)
双亲委派模型
看名字就看不懂,意思其实很简单,如果一个类如 String 已经声明,然后你再别的地方再声明 String,那你的 String 并不会被加载。伪代码如下:
if (java.lang 包里有没有 String 类被加载?) {
不管了,直接结束
} else {
load(自己写的包的 String 类)
}
网友评论