1:Java类加载机制
简介:
虚拟机把数据从Class文件加载到内存,并对数据进行校验,解析和初始化,最终形成可以被虚拟机直接使用的java类型。
2:JVM加载Class文件的原理机制
Java中的所有类,都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类,而它的工作就是把class文件从硬盘读取到内存中。
在写程序的时候,我们几乎不需要关心类的加载,因为这些都是隐式装载的,除非我们有特殊的用法,像是反射,就需要显式的加载所需要的类。
3:类装载方式
1.隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中
2.显式装载, 通过class.forname()等方法,它只是先加载一些基础类到jvm中,至于其他类,则在需要的时候才加载。这样可以节省内存开销
4:类装载的执行过程
类装载分为以下 5 个步骤:
加载:根据查找路径找到相应的 class 文件然后导入;
验证:检查加载的 class 文件的正确性;
准备:给类中的静态变量分配内存空间;
解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址;
初始化:对静态变量和静态代码块执行初始化工作。
5:类加载器
主要有一下四种类加载器:
启动类加载器(Bootstrap ClassLoader):用来加载java核心类库,无法被java程序直接引用。
扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的 实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载Java类。(JDK9之后,扩展类加载器被重命名为平台类加载器)。
系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。
用户自定义类加载器,通过继承 java.lang.ClassLoader类的方式实现。
6:反射机制
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;他能这种动态获取的信息以及动态调用对象的方法
1:优点
运行期类型的判断,动态加载类,提高代码灵活度。
2:缺点
性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,过程比直接的java代码要多了一步委托的过程,反射需要类加载器通过双亲委派模型实现动态编译,效率较低。
7:静态编译与动态编译
静态编译:在编译时确定类型
动态编译:运行时确定类型,绑定对象
8:双亲委派机制
JVM中有三大类加载器:启动类加载器(最顶层),平台类加载器(中层),系统类加载器(下层)。
双亲委派模型就是指一个类加载器收到了类加载请求,它不会直接自己先加载,而把请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,就再往上委托,赴到最顶层的类加载器,如果父类加载器可以完成类加载任务,就成功返回,若不能,就向下传递,让子加载器去加载,这就是双亲委派模式。双亲委派模型主要是用来保证同一个类只能被一个类加载器加载。
9:如何破坏双亲委派机制
一般在自定义类加载器中,我们不希望通过双亲委派机制一层层向上再下来,而是希望直接通过自己定义的类加载器直接实现类加载,来提升加载性能,比如Tomcat中的web容器类加载器就是破坏了双亲委托模式的,里面的WebApplicationClassLoader除了核心类库外,都是优先加载自己路径下的Class。
要打破双亲委派机制,只要在重写loadclass的过程中,不遵从JVM规范就行了,也就是不盲目优先向Parednt的ClassLoader查找即可。
10:反射的使用场景
JDK动态代理,Spring中的注入属性,调用方法等。
反射更多是为了灵活舍弃一部分性能,自己使用一般用在工具类中,比如频繁通过参数名来调用指定的方法时,可以用通过反射去匹配指定的方法名,然后实现功能。
11:Java中获取反射的三种方法
1、类名.class属性;
2、对象名.getClass()方法;
3、Class.forName(全类名)方法
12:反射可以获取“私有”方法或构造函数或私有成员变量
有专门反射私有构造函数的方法 clazz.getDeclaredConstructor(int.class);来读取私有的构造函数,私有成员变量和私有方法也一样,但用这个方法读取完还需要设置一下暴力反射才行:c.setAccessible(true)。
13:静态方法和实例方法的区别
1、静态方法属于整个类所有,因此调用它不需要实例化,可以直接调用(类.静态方法())。实例方法必须先实例化,创建一个对象,才能进行调用(对象.实例方法())。
2、静态方法只能访问静态成员,不能访问实例成员;而实例方法可以访问静态成员和实例成员。 3、在程序运行期间,静态方法是一直存放在内存中,因此调用速度快,但是却占用内存。实例方法是使用完成后由回收机制自动进行回收,下次再使用必须再实例化。
4、一般来说,公共的函数、经常调用的可以写成静态方法,比如数据连接等(SqlHelper)。