Java反射——Class和ClassLoader
这里写自定义目录标题
- 概述
- API
- 1、Class
- 2、ClassLoader
- 默认类加载器
- 双亲委派
- 小结
概述
当你准备招待客人时,如果你知道你的客人是谁,那么直接去迎合他的喜好就好了。
但如果你还并不清楚具体客人是谁,那你需要注意了,你所做的准备,要适合所有的客人。
类是对对象的抽象,类的属性、方法、继承关系,其对象都同样拥有。同理,在很多场景下,我们需要对不同的类做一定的归类、合并和统一处理。这样,我们就需要对不同的类做一定的抽象,才能达到我们的目的。也就是说,在编译阶段,我们并不知道具体的类是谁,而真正的类是动态赋予的,由此,引出反射系列。
API
反射相关的类主要在java.lang 和 java.lang.reflect包下。
其中,java.lang下主要有以下两个。
1、Class
类是对象的抽象,Class就是所有类的抽象。
也可以说,Class表示类的类类型。
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement {
}
私有构造方法,仅JVM能创建Class对象。
private Class(ClassLoader loader) {
// Initialize final field for classLoader. The initialization value of non-null
// prevents future JIT optimizations from assuming this final field is null.
classLoader = loader;
}
重要方法:
a.创建类型的方法
newInstance()
创建类的新的实例-通过类类型可以创建类的实例对象(需要有无参构造方法)
b.查询类型的方法
forName()
根据给定的string 类名、类加载器(可选)返回相对应的Class对象
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
Class<?> caller = null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// Reflective call to get caller class is only needed if a security manager
// is present. Avoid the overhead of making this call otherwise.
caller = Reflection.getCallerClass();
if (sun.misc.VM.isSystemDomainLoader(loader)) {
ClassLoader ccl = ClassLoader.getClassLoader(caller);
if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
sm.checkPermission(
SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
}
}
return forName0(name, initialize, loader, caller);
}
/** Called after security check for system loader access checks have been made. */
private static native Class<?> forName0(String name, boolean initialize,
ClassLoader loader,
Class<?> caller)
throws ClassNotFoundException;
可以看到,forName()重载方法,最终都调用了native方法forName0()。其中,forName(String className)方法中设置initialize为true。
注意:
a.指定的类加载器是用于加载类或者接口的。
b.若该方法中,未给出指定的加载器,会通过bootstrap类加载来加载相应的类。
c.当参数中的initialize为true,并且该类之前未被初始化过,才会对类进行初始化。
补充:其他获取Class的方式
类名为A
- A.class (class是类的隐含的静态成员变量)
- new A().getClass() (Object的方法getClass)
涉及到类加载机制的部分,下一篇详细说明。
其他查询方法:
getName()
getClassLoader()
getField()
getMethod()
getConstructors()
2、ClassLoader
public abstract class ClassLoader {
}
默认类加载器
先看几个简单的例子:
ps:注释为打印结果,其中,由于Bootstrap ClassLoader为C++语言编写,在java下无法打印,因此得到的打印结果为null。
System.out.println(ClassTest.class.getClassLoader());//AppClassLoader
System.out.println(ClassTest.class.getClassLoader().getParent());//ExtClassLoader加载器
System.out.println(ClassTest.class.getClassLoader().getParent().getParent());//Bootstrap加载器,打印结果为null
由上可知,类加载器有三种默认类型:Bootstrap ClassLoader、ExtClassLoader、AppClassLoader。那这三个有什么分别呢?
- Bootstrap ClassLoader:Java中最顶层的类加载器,负责加载JDK中的核心类库,如:rt.jar、resources.jar、charsets.jar等。
- ExtClassLoader:主要负责加载Java的扩展类库,负责加载JDK中的扩展类库。其父类加载器为Bootstrap ClassLoader。
- AppClassLoader:应用类加载器(系统类加载器),负责加载classpath路径下的jar包。其父类加载器为ExtClassLoader。
除了这三个默认类加载器,当然用户可以自定义类加载器,但自定义类加载器的父类是AppClassLoader。关系如下:
那么一个类到底是如何加载的呢?这几个默认类加载器之间是如何工作的呢?
双亲委派
首先,明确下类加载亲在jvm内存模型中的位置。
什么是双亲委派?
若一个类加载器要加载一个类,先看其父类是否能加载,以此类推,直到最顶层的Bootstrap ClassLoader,若顶层类加载器可以加载即进行加载,若无法加载,才有最初的类加载器进行加载。
通俗的说,儿子接到活之后,一律不自己做,先给了爸爸,爸爸给了爷爷,爷爷如果可以做就做,不能做,再由儿子一层一层检查是否可以做。
为什么要用这种实现方式?
- 安全
若用户自定义了核心或扩展类库中的同名类,则不会被成功加载,因为这些系统默认类已由默认类加载器加载。 - 避免重复加载
类加载器之间有了层次优先级关系,就避免了类的重复加载。
java.lang.reflect包下的主要类有:
1、Field
该类提供了动态访问、设置类或接口的字段功能。
public final
class Field extends AccessibleObject implements Member {
}
2、Method
3、Constructor
4、Array
小结
本篇介绍了反射中的常用的相关类,Class和ClassLoader,介绍了Class抽象的思维,以及类加载机制的“双亲委派”机制。下篇我们重点介绍本文中说明的reflect包的代理。
还没有评论,来说两句吧...