执行一个Java程序,实质是调用jdk\bin下的java.exe,把这个Java程序作为参数传递给java.exe,此命令会启动一个JVM进程,不管这个Java程序有多少个线程、有多复杂,这个Java程序中所有的线程、变量都处于此JVM进程中,它们使用的都是此JVM进程的内存区。
当出现以下情况时,JVM进程会被终止:
当JVM进程结束后,OS会回收此进程占用的内存,java程序在内存中所有的数据都会丢失。
不同的JVM进程是相互独立的,不同享内存(数据)。比如先运行一遍,再运行一遍,后一遍使用的不是前一遍终止时的数据,而是从头开始的。
类加载也叫类初始化,指的是:当程序要使用某个类时,如果该类还没有被加载到内存中,JVM会通过加载、连接、初始化三个步骤来对该类进行初始化。
我们习惯把这三个步骤作为一个整体,称为类加载或类初始化。
加载指的是:JVM将该类的.class文件读到内存中,并为之创建一个java.lang.Class对象。
类是对对象的抽象,java.lang.Class是对类的抽象,JVM中所有的类都是java.lang.Class的实例。
加载由类加载器来完成,类加载器的任务是将类的.class文件读到内存中,并为之创建一个对应的java.lang.Class对象。
JVM提供了3种类加载器:
我们可以继承ClassLoader类来自定义类加载器。ClassLoader类的常用方法:
加载顺序:根加载器、扩展加载器、系统加载器、自定义的类加载器
JVM的类加载机制主要有3种:
加载的大致步骤:
如果父类加载器不存在,说明当前加载器是根加载器,那就用根加载器来加载。
加载时,会找到此类对应的.class文件,根据.class文件来创建Class对象,创建成功就返回Class对象,创建失败则进行下一步处理,如果没有下一步处理,则抛出ClassNotFoundException,因为默认代码是没问题的,加载失败是因为.class文件没有找到。
加载后,生成的Class对象用全类名唯一标识。
一个类只加载一次,因为第一次加载此类过后缓存中会有此类的Class对象,以后加载此类时直接从缓存中获取Class对象,不再重新加载。
类加载是按需加载,程序要使用此类时才会加载。
类加载会自动加载依赖、引用的其他类,比如父类。
加载获得的Class对象是二进制数据,连接是把Class对象合并到JRE中(把Class对象连接到JRE)。JRE即Java Runtime Environment,Java运行时环境。
连接分为3个阶段:
Class对象是加载时生成的,此时Class对象中的成员变量还没分配内存,不知道该成员变量在内存中的地址,只能用符号来暂时表示该成员变量在内存中的地址。
准备阶段给成员变量分配内存以后,有了内存地址,下一步自然就是用内存地址(直接引用)替换掉这些符号。
初始化指的是:JVM对类进行初始化。注意是对类进行初始化,不是对类的对象进行初始化,所以类初始化只执行stati成员(类所有),不初始化普通成员变量。
初始化执行的static成员包括:
如果某个static成员变量使用了final修饰,并且设置了初始值(常量),则在编译时就能确定这个final static成员变量的值,编译时会用其值(常量)来替换所有出现这个final static成员变量的地方,这个final static成员变量相当于常量。类初始化时不再初始化这个final static成员变量。
当JVM初始化一个类时,会保证这个类的所有父类(直接父类+间接父类)都已经初始化了,因为要从父类继承成员,如果不先初始化父类,那继承什么?所以JVM最先初始化的总是java.lang.Object类。
类加载|初始化并不局限于类,还包括接口。
通过反射可以创建对象(类的实例):
1 Class<?> studentClass = Class.forName("test.Student"); //必须写成全类名 2 Constructor<?> constructor = studentClass.getConstructor(int.class, String.class, int.class, int.class); 3 Object obj = constructor.newInstance(1, "chy", 20, 100); 4 Student student = (Student) obj;
第一句 Class.forName("test.Student") 就是加载、初始化指定的类,返回指定类的Class对象。因为可能多个包下都有这个类,所以必须写成全类名,唯一标识这个类。
类的加载、初始化只是把类(.class文件)装入内存,生成一个Class对象,这个Class对象具有此类的实例的公共部分(因为初始化了static成员),普通成员变量也都分配了内存,设置为该种数据类型的默认值。这个Class对象相当于这个类的实例的模板,JVM用这个模板来生产类的实例。
并不是说加载、初始化类之后,就得到了这个类的对象,初始化的是类,不是这个类的某个对象。
以 new Student(1,"chy", 20, 100) 的执行过程为例:
1、加载|初始化Student类,得到Student的Class对象
Class.forName("test.Student"); //返回Class对象
2、根据实参表的数据类型,通过Class对象获取对应的构造器
Class对象.getConstructor(int.class, String.class, int.class, int.class); //返回构造器
JVM中并没有什么Int类、String类,JVM中存在的是这些类的Class对象,所以要写成.class的形式,表示这个类的Class对象。
3、使用获取到的构造器,传入实参值,创建类的实例
构造器.newInstance(1, "chy", 20, 100); //返回Student类的实例
原文:https://www.cnblogs.com/chy18883701161/p/11380694.html