1.1 Java运行时一个类是什么事时候被加载的?
一个类在什么时候被加载虚拟机并没有强制约束,对于HotSpot虚拟机,是按需加载,当真正要用到的时候才开始加载此类;
1.2 一个类的加载过程
1、加载:
将类的class二进制字节流读进来,在内存中生成一个代表这个类的java.lang.Class对象放入元空间,此阶段程序员可以干预,可以自定义类的加载器来实现类的加载;
2、链接
验证:验证Class文件的字符流中包含的信息是否符合java虚拟机规范中全部约束条件;(这样能保证虚拟机的安全);cafe babe
准备: 对类变量赋默认初始值(实例变量要等到new对象的时候才开始初始化),int为0,long为0L,boolean为false,引用类型为null;常量直接进行赋正式值;
解析:把符号引用翻译为直接引用;
3、初始化:new一个对象,访问一个类的静态属性,修改一个类的静态属性,调用一个类的静态方法,用反射API对一个类进行调用,初始化当前类,其父类也会被初始化......
具体的初始化细节后面叙述
4、使用:使用这个类;
5、卸载:当满足以下几点的时候该类被卸载:大部分情况很难满足
1、该类的所有实例都被GC,也就是JVM中不存在该Class的任何实例;实例没了
2、加载该类的ClassLoader已经被GC;加载器没了
3、该类的java.lang.Class对象没有在任何地方被引用,比如:不能再任何地方通过反射访问该类的方法;Class没了
1.3 一个类被初始化的过程
静态变量:准备阶段赋值为null/0,初始化阶段赋值为初始值;
实例变量:创建对象的时候赋值;
静态初始化块:初始化阶段执行;
1.4 继承时父子类的初始化顺序是怎样的?
1、父类静态变量和静态代码块(先声明的先执行);
2、子类静态变量和静态代码块(先声明的先执行);
3、父类的变量和代码块(先声明的先执行);
4、父类的构造函数;
5、子类的变量和代码块(先声明的先执行);
6、子类的构造函数。
1.5 什么是类加载器
在加载阶段:
类加载器是一段代码,通过此代码将类以二进制字节流的形式读取到JVM中;
类加载器通过一个类的全限定名来获取描述该类的二进制字节流;
1.6 JVM有哪些类加载器
一、从JVM虚拟机角度
1、启动类加载器:C++语言实现;
2、其他所有的类加载器:java语言实现,继承于抽象类java.lang.ClassLoader;
二、从开发者角度
BootstrapClassLoader:启动类加载器
ExtClassLoader:扩展类加载器
AppClassLoader:应用类加载器(系统类加载器)
1.7 JVM中不同的类加载器加载哪些文件?
1、启动类加载器:
<JAVA_HOME>\jre\lib\rt.jar,resources.jar\charsets.jar
2、扩展类加载器
<JAVA_HOME>\jre\lib\ext
被java.ext.dirs系统变量所指定的路径中所有的类库
3、应用类加载器
加载用户类路径上所有的类库:自己写的类、引入的第三方jar包;
1.8 JVM三层类加载器之间的关系是继承吗?
不是;AppClassLoader/ExtClassLoader都是Launcher里面的内部类;他们两个都是继承ClassLoader;
1.9 你了解双亲委派模型吗?
首先通过系统类加载器加载,然后系统类加载器不加载,委派给扩展类加载器加载,同样,扩展类加载器也不加载,委派给启动类加载器加载;然后开始自顶向下去尝试加载,如果没有找到就交给下面的去尝试加载,直到最后加载成功;
1.10 为什么要设计双亲委派模型,有什么好处?
1、确保安全,避免Java核心库被修改;
2、避免重复加载;
3、保证类的唯一性;
1.11 可以打破双亲委派模型吗?如何打破JVM双亲委派模型;
想要打破这种模型,就自定义一个类的加载器,继承Classloder类,重写其中的loadClass(这个方法是实现双亲委派机制的)方法,使其不进行双亲委派即可;
1.12 如何自定义自己的类加载器
1、继承ClassLoader;
2、覆盖findClass(String name)方法、或者 loadClass方法;
覆盖findClass(String name)方法 不会打破双亲委派;
loadClass()方法:会打破双亲委派机制;
1.13 ClassLoader 中 的loadClass()、findClass()、defineClass()区别?
loadClass()就是主要进行类加载的方法,默认的双亲委派机制就是在在这个方法中;
findClass()根据名称或位置加载.class字节码;
definclass():这个方法底层是用C++写的,把字节码流转化为java.lang.Class;
1、当我们想要自定义一个类加载器的时候,并且向破环双亲委派模型时,就会重写LoadClass方法;
2、如果我们不想破环双亲委派模型就重写findClass方法即可;在findClass方法中实现自己加载的逻辑就可以了;
1.14 加载一个类采用Class.forName()和ClassLoader有什么区别
1、用Class.forName加载的时候类的信息会被初始化;加载、链接、初始化;
2、如果使用ClassLoader加载类的时候不会初始化,只会进行加载、链接;只有在加载之后使用newInstance之后才会进行初始化;
1.15 你了解 TomCat的类的加载机制吗?
灰色部分为java中的类加载器结构,白色部分为TomCat中的类加载器结构;
commonClassLoader、CatalinaClassLoader、ShareClassLoader是三个基础类加载器,
是通过conf/catalina.properties进行配置的;
webApp加载TomCat中webApps中的文件;重写了findclass和loadclass方法,打破了双亲委派机制;(可以有多个webApp加载器,多个项目就有多个)
jsp类加载器用来加载jsp文件;jsp在运行的时候会转换为java,通过jsp转换为class文件;重写了loadclass方法,也打破了双亲委派模型的机制;(可以有多个jsperLoader加载器,多个jsp页面就有多个jsperLoader加载器);
打破了双亲委派的机制,也就是说如果收到类加载的请求首先会尝试自己去加载,如果自己找不到再交给父类加载器去加载,目的就是为了优先记载Web应用自己的类;
1.16 为什么Tomca要打破双亲委派机制模型?
Tomcat是web容器,那么一个容器就可能需要部署多个应用程序;
1、部署在同一个Tomcat上的两个Web应用所使用的java类库要相互隔离;
比如说:一个项目使用了spring5的版本,一个项目使用了spring4的版本,两个项目版本不同,可能存在问题,所以不同的项目他们所需要的java类库需要相互隔离,所以需要不同的 加载器去进行记载;
2、部署在同一个Tomcat上的两个Web应用所使用的java类库要相互共享;
也就是和上面相反,多个项目之间存在许多版本相同的java类库,重复加载放在元空间中会造成浪费,所以可以将tm都放在lib目录中进行加载,然后多个项目就可以共享lib中的jar包;
3、保证Tomcat服务器自身的安全不受部署的web应用程序的影响;
Tomcat是由java写的,他自己在运行加载java类库的时候和部署的项目进行隔离,不让web项目影响到Tomcat自身的运行安全;(自己的webapp加载器,加载自己的web项目互不影响);
4、需要支持jsp页面的热部署和热加载;(不重启服务器让页面生效,其实就是启动了一个新的jsp记载器去加载jsp页面);当文件发生变化之后,就会启动一个新的jsp启动器加载;
1.17 有没有听说过热加载或热部署,如何自己实现一个热加载?
热加载:是指可以在不重启服务的情况员下让更改的代码生效,热加载看一看显著的提高开发以及调式效率。他是基于java的类加载器实现的;
热部署:是指可以在不重启服务的情况下重新部署整个项目,比如Tomcat热部署就是在程序运行时,如果我们修改了War包中的内容,那么Tomcat就会删除之前的War包解压的文件夹,重新解压新的War包生成新的文件夹;
热加载就是在运行时重新加载class,后台会启动一个线程不断检测你的class是否发生改变;
热部署是在运行时重新部署整个项目,耗时相对较高;
如何实现一个热加载?
1、实现自己的类加载器;
2、用自己的类加载器加载要热加载的类;
3、不断的轮训要加载的类class文件是否有更新,如果有更新,重新加载;(相当于一个监听的作用);