1.班级原则孟子曰:得民心,得天下。在Java中,这个“人心”就是Class类,我们拿到Class类之后就可以为所欲为了。让我们深入“人心”,探究Class类的原理。首先了解JVM如何构建实例。1.1JVM构建实例JVM:JavaVirtualMachine,Java虚拟机。在JVM中,分为栈、堆、方法区等,但这些都是JVM内存,本文所说的内存指的是JVM内存。.class文件是从.java文件编译而来的字节码文件。了解了以上内容,我们开始创建实例。我们以创建Person对象为例:Personp=newPerson()简单的通过new创建一个对象,过程是怎样的?见下图:这个太粗糙了,我们细化一下。战友们,你们注意到了吗?其实这里还是有一些区别的。告诉你吧,区别就是下面的字比上面的多,你要打我(别打我脸)。粗的是new创建的对象,精的是操作.class文件通过ClassLoader生成Class类创建的对象。其实通过new或者反射创建实例都需要一个Class对象。1.2.class文件文章开头提到,.class文件是字节码文件。.java是源程序。Java程序是跨平台的,一次编译到处执行,而编译就是将源文件转换成字节码文件。字节码只不过是一个由0和1组成的文件。有一个类如下:通过vim查看字节码文件:这是什么东西,没看懂。我们不需要去理解它,反正JVM对.class文件有自己的读取规则。1.3类加载器还记得上面那个精美的图吗,我们知道.class文件是通过类加载器加载到内存中的。具体的类加载器内容我会另写一篇讲解(写完后会更新这里的链接)。但是核心方法是loadClass(),只要告诉它要加载的名字,它就会帮你加载:protectedClass>loadClass(Stringname,booleanresolve)throwsClassNotFoundException{synchronized(getClassLoadingLock(name)){//1。检查类是否有Class>c=findLoadedClass(name);if(c==null){longt0=System.nanoTime();try{//2。尚未加载,遵循parent-firstlevel加载机制(parentaldelegationmechanism)if(parent!=null){c=parent.loadClass(name,false);}else{c=findBootstrapClassOrNull(name);}}catch(ClassNotFoundExceptione){//ClassNotFoundExceptionthrownifclassnotfound//fromthenon-nullparentclassloader}if(c==null){//3.如果加载不成功,调用findClass()longt1=System.nanoTime();c=findClass(name);//thisisthedefiningclassloader;recordthestatssun.misc.PerfCounter.getParentDelegationTime().addTime(t1-t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if(resolve){resolveClass(c);}returnc;}}//需要重写这个方法,默认是抛出异常protectedClass>findClass(Stringname)throwsClassNotFoundException{thrownewClassNotFoundException(name);}类加载器加载.class文件。检查类是否已经加载主要分为三个步骤。如果是,则直接返回当前不存在的类。遵循双亲委派机制。如果上面两步加载.class文件失败,调用findClass()因为ClassLoader的findClass方法默认会抛出异常,所以我们需要再写一个子类来覆盖,例如:@OverrideprotectedClass>findClass(Stringname)throwsClassNotFoundException{try{//通过IO流从指定位置读取xxx。class文件获取字节数组byte[]datas=getClassData(name);if(null==datas){thrownewClassNotFoundException("Classnotfound:"+name);}//调用类加载器的defineClass()方法本身,从字节码中获取类对象returndefineClass(name,datas,0,datas.length);}catch(IOExceptione){thrownewClassNotFoundException("Classnotfound:"+name);}}privatebyte[]getClassData(Stringname){returnbyte[]datas;}defineClass是通过字节码获取Class的方法,由ClassLoader定义我们不知道如何实现它,因为最终会调用一个本地方法:privateClass>defineClass0(Stringname,byte[]b,intoff,intlen,ProtectionDomainpd);privatenativeClass>defineClass1(Stringname,byte[]b,intoff,intlen,ProtectionDomainpd,Stringsource);privatenativeClass>defineClass2(Stringname,java.nio.ByteBufferb,intoff,intlen,ProtectionDomainpd,Stringsource);总结一下类加载器加载.class文件的步骤:通过ClassLoader类中的loadClass()方法从缓存中获取Class,直接返回缓存中不存在。以上两步通过双亲委派机制加载失败。调用findClass()通过IO流从指定位置获取.class文件,并获取字节数组。调用类加载器的defineClass()方法,从字节数组中获取Class对象1.4类class.class文件已经被类加载器加载到内存中并生成字节数组,JVM根据字节数组创建对应的Class对象。接下来我们来分析一下Class对象。我们知道Java对象会有如下信息:权限修饰类名和泛型信息接口实体注解构造函数方法这些信息在.class文件中用0101表示,最后JVM会把.class文件的信息保存到Class中.类中必须有字段来存储这些信息。我们来看一下:在Class类中,通过ReflectionData中的字段与.class的内容进行映射,分别映射字段、方法、构造函数和接口。标注数据通过annotationData进行映射,其他的不会显示。可以打开IDEA查看Class的源码。然后再看看Class类的方法1.4.1构造函数Class类的构造函数是私有的,只能通过JVM创建Class对象。于是就有了上面通过类加载器获取Class对象的过程。1.4.2Class.forNameClass.forName()方法仍然是通过类加载器获取Class对象。1.4.3newInstancenewInstance()底层是返回一个无参数的构造函数。2.综上所述,我们梳理一下之前的知识点:反思的重点是获取Class类,系统是如何获取Class类的?它使用类加载器ClassLoader通过字节数组将.class文件加载到JVM中,JVM将字节数组转换为Class对象。那种类型的loader是怎么加载的?通过ClassLoader的loadClass()方法从缓存中找到,直接返回缓存中不存在。以上两步通过双亲委托机制加载失败,通过IO流从指定位置调用findClass()。获取.class文件并获取字节数组。调用类加载器的defineClass()方法从字节数组中获取Class对象。Class类的构造函数是私有的,所以需要通过JVM获取Class。Class.forName()也是通过类加载器得到的Class对象。newInstance方法的底层也是返回的无参构造函数。
