一.类的加载
1:概述
1:当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载-连接-初始化主要三步来实现这个类进行初始化.
2:得到类加载器: Class#getClassLoader()
3:类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例。
任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在java虚拟机中的唯一性
1.加载
1.1.1就是指将class文件读入内存(JVM方法区),并为之创建一个class对象(成员变量,成员方法,构造方法).
1.1.2任何类被使用系统都会建立一个class对象.
2:连接
1.2.1:验证
是否有正确的内部结构,并和其他类协调一致
1.2.2:准备
负责为类的静态成员分配内存(静态是随着类的加载而加载),并设置默认初始化值
1.2.3:解析
将类的二进制数据中的符号引用替换为直接引用
.
3:初始化
2:类加载(初始化)时机
1.2.1:创建类的实例
1.2.2:访问类的静态变量或者为静态变量赋值
1.2.3:调用类的静态方法
1.2.4:使用反射方法来强制创建某个类或接口对应的java.lang.Class对象
1.2.5:初始化某个类的子类
1.2.6:直接使用java.exe命令来运行某个主类
3:类加载器的组成分类
1:类的加载机制:就是将class文件加载到内存中,并为之生成对应的class对象,
2:根类加载器>扩展类加载器>系统类加载器
1:根类加载器(引导类加载器) (Bootstrap classLoader)
1.负责Java核心类的加载,,作为虚拟机的一部分,比如system.string,在JDK的jre的lib目录下的rt.jar文件中
2:扩展类加载器(Extension classLoader)
2.负责jre的扩展目录下的jar包的加载,在JDK中jre的lib目录下的ext目录
3:系统类加载器 (system classLoader)
1.负责在jvm启动时加载来自Java命令的class文件,以及classpath环境变量所指定的jar包和类的路径,包含了所有jdk提供的类(rt-jar).包含开发人员写的类和第三方的jar包
2:加载classpath下的类
3:也叫Application ClassLoader ,sun.misc.Launcher$AppClassLoader, 它是System.getClassLoader()的返回值
4:类加载器的委托机制
1:类加载器的关系
1:系统类加载器的上层领导----------->扩展类加载器
2:扩展类加载器的上层领导----------->根类加载器
2:委托机制执行顺序
1:创建了一个对象 User user=new User();
2:系统类加载器发现了new User这个类
1:系统会给自动的领导打电话,让扩展去自己的地盘去加载user类
2:扩展会给自己的领导打电话,让根类加载器去自己的地盘去加载user类
1:根类加载器去jre下的rt.jar去寻找A类
->1:如果找到了,就进行加载,然后返回user对象的class对象给扩展类加载器,扩展类加载器将这个user.class文件返回给系统类加载器
->2:如果没找到
1:根类加载器返回给扩展一个null,扩展会在自己的地盘上寻找user类
->1:如果扩展类找到了user类,就进行加载,返回user对象的class文件给系统类加载器
-> 2:如果没找到
1:扩展类返回给系统类加载器一个null,系统去自己的地盘(应用程序) 加载A类
->1:如果找到了,加载,返回这个class文件,结束
->2:如果没找到,抛出classNotFoundException类为未找到异常
3:双亲委派模型
1:概述
双亲委派模型的过程:如果一个类加载器收到了类加载的请求,首先不会自己去加载,而是把请求为派给自己的父类加载器去完成,每一个层次的类加载器都是如此。因此所有的加载请求最终都应该传送到顶层的启动类加载器中。只有当父类反馈自己无法完成这个加载请求时,子类加载器才会尝试自己完成
2:破坏双亲委派模型
1:三次大规模破坏该模型的情况
1:JDK 1.0->1.2 , loadClass() -> findClass()
2:模型缺陷:JNDI服务,SPI扩展类是由厂商自己实现,而启动类加载又不可能认识这些类。只好引入 线程上下文 类加载器Thread Context ClassLoader. 该类加载器可以通过setContextClassLoader()设置,如果创建线程时未设置,将会从父线程继承。如果在应用的全局范围内都没有设置,那就默认是AppClassLoader. 有了这个,JNDI服务就可以去加载所需的SPI扩展代码,也就是父类加载器请求子类加载器去完成类加载的动作。这其实也就违背了双亲委派模型的一般性原则,但无可奈何。
3:程序动态性的追求: “热替换” , OSGI. JSR-291
2:开发自己的类加载器
4:类加载安全机制
1:类的加载会从根类加载器---扩展类加载器---系统类加载器一层一层的往下找,不会出现重复加载的情况
2:自己写一个类Java.lang.string,想去替换Java根类库
1:是不会加载的,因为根类加载器中有Java.lang.string.是不会加载自己写的类的
5:类加载器与线程
1:每一个线程都有一个单独的类加载器和cpu寄存器(记录线程的工作状态)
2:getContextClassLoader:获取当前线程的类加载器
二:自定义类加载器
1:应用背景
主 流的Java Web服务器,比如Tomcat,都实现了自定义的类加载器(一般都不止一个)。因为一个功能健全的Web服务器,要解决如下几个问题:
1:部署在同一个服务器上的两个Web应用程序所使用的Java类库可以实现相互隔离。这是最基本的要求,两个不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求一个类库在一个服务器中只有一份,服务器应当保证两个应用程序的类库可以互相使用
2:部署在同一个服务器上的两个Web应用程序所使用的Java类库可以相互共享。这个需求也很常见,比如相同的Spring类库10个应用程序在用不可能分别存放在各个应用程序的隔离目录中
3:支持热替换,我们知道JSP文件最终要编译成.class文件才能由虚拟机执行,但JSP文件由于其纯文本存储特性,运行时修改的概率远远大于第三方类库或自身.class文件,而且JSP这种网页应用也把修改后无须重启作为一个很大的优势看待
4:由于存在上述问题,因此Java提供给用户使用的ClassLoader就无法满足需求了。Tomcat服务器就有自己的ClassLoader架构,当然,还是以双亲委派模型为基础的:
2:简述
1:自定义classLoader加载类是通过loadClass()方法类完成
2:自定义类加载器必须继承ClassLoader这个静态类
3:在JVM的方法区中查询已被加载过的类,判断当前类是否被加载过
3:Java.lang.classLoader源代码
1:加载类通过loadClass方法
2: synchronized (getClassLoadingLock(name) 线程同步,线程锁
4:自定义类加载器的Demo
1:将class文件以.分割的替换为\\磁盘资源转移符,并加上后缀.class
2:将class文件也流的方式读入内存中
classpath,类加载器管理的片区+.class文件名字