jvm如何创建实例
在学习反射之前,我们首先要理解一下jvm创建一个实例的过程,首先我们先用java写一个类
public class User {
private int id;
private String password;
public boolean login(int id, String password) {
if (this.id == id && this.password.equals(password))
return true;
return false;
}
}
而在main方法中有这条语句时
User user = new User();
-
要通过这条命令编译成.class文件也就是字节码文件
javac user.java
-
jvm会通过ClassLoader加载.class文件到内存
-
生成Class对象Class
-
执行new,申请一块内存空间
-
构造器执行构造代码块和初始化语句
.class文件
.class文件是编译后的文件,打开后是01代码。是给计算机读的。JVM对.class也有自己的一套读法。
类加载器
类加载器的核心方法只有loadClass(),告诉类名他会加载生成Class对象
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先,检查是否已经加载该类
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
// 如果尚未加载,则遵循父优先的等级加载机制(所谓双亲委派机制)
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// 模板方法模式:如果还是没有加载成功,调用findClass()
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
加载.class文件可以分为3个步骤
- 检查文件是否被加载,加载就直接返回
- 当前缓存没有该类,遵守父优先加载机制,加载.class文件
- 上面步骤行不通,调用findClass()方法加载
public Class<?> findClass(String name) throws ClassNotFoundException {
try {
//通过IO流从指定位置读取xxx.class文件得到字节数组
byte[] datas = getClassData(name);
if(datas == null) {
throw new ClassNotFoundException("类没有找到:" + name);
}
//调用类加载器本身的defineClass()方法,由字节码得到Class对象
return defineClass(name, datas, 0, datas.length);
} catch (IOException e) {
e.printStackTrace();
throw new ClassNotFoundException("类找不到:" + name);
}
}
findClass首先是获取该文件的字节数组,之后通过defineClass方法来创建class对象,defineClass()是native方法。
Class类
首先我们先看下Class的基本属性
private static class ReflectionData<T> {
volatile Field[] declaredFields;
volatile Field[] publicFields;
volatile Method[] declaredMethods;
volatile Method[] publicMethods;
volatile Constructor<T>[] declaredConstructors;
volatile Constructor<T>[] publicConstructors;
// Intermediate results for getFields and getMethods
volatile Field[] declaredPublicFields;
volatile Method[] declaredPublicMethods;
volatile Class<?>[] interfaces;
// Value of classRedefinedCount when we created this ReflectionData instance
final int redefinedCount;
ReflectionData(int redefinedCount) {
this.redefinedCount = redefinedCount;
}
}
源码中我们看到这个Class类的反射数据类型,这里就很熟悉了把,这是一个类的基本组成部分
- declaredFields:类中的属性(包括私有)
- publicFields:类中仅公开的属性
- declaredMethods:类中所有的方法
- publicMethods:类中公开的方法
- declaredConstructors:类中所有构造器
- publicConstructors:类中公开构造器
我们也很容易看出Field Method Constructor也是类,而一个class类中的相应反射数据就保存在这些类的数组中。
Constructor
我们再来看下构造器这个类,我们获取到一个类的类类型(也就是某个类的Class对象),可以通过newInstance方法来创建该类的实例对象
我们可以看到newInstance()底层是获取一个无参的构造器对象,通过这个构造器的newInstance()的方法创建实例对象。
method
我们再看下method这个类的一些基本属性
private Class<?> clazz;
private int slot;
// This is guaranteed to be interned by the VM in the 1.4
// reflection implementation
private String name;
private Class<?> returnType;
private Class<?>[] parameterTypes;
private Class<?>[] exceptionTypes;
private int modifiers;
// Generics and annotations support
private transient String signature;
// generic info repository; lazily initialized
private transient MethodRepository genericInfo;
private byte[] annotations;
private byte[] parameterAnnotations;
private byte[] annotationDefault;
private volatile MethodAccessor methodAccessor;
- name:方法名
- returnType:返回类型
- parameterTypes:参数类型
- annotations:注解
我们在获取这个类的某个方法时可以用getMethod()
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Method method = getMethod0(name, parameterTypes, true);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}
可以看到,传入的参数是方法的名字和方法的参数的类型型,底层是在这个类的方法集合中根据这两个参数查找这个方法。
private static Method searchMethods(Method[] methods,
String name,
Class<?>[] parameterTypes)
{
Method res = null;
String internedName = name.intern();
for (int i = 0; i < methods.length; i++) {
Method m = methods[i];
if (m.getName() == internedName
&& arrayContentsEq(parameterTypes, m.getParameterTypes())
&& (res == null
|| res.getReturnType().isAssignableFrom(m.getReturnType())))
res = m;
}
return (res == null ? res : getReflectionFactory().copyMethod(res));
}
可以看到查找方法的流程是
- 判断方法名是否相等
- 比较参数类型是否相等
- 比较返回类型是否相等
走到这里,我们其实也可以明白为什么参数中要传类类型了吧,因为每个类的类类型是唯一的,在方法匹配时自然可以判断这个方法的参数是否为需要的类型。也就是说传的是这个类的引用,如果我们传的是类名(如String),那么这个引用就是string的值得引用。那么可以正常的进行方法的查找么?
通过这里我们也可以更深入的理解方法重载了吧。