java面试题网站:www.javaoffers.com
本人的编程母语是java 语言,java语言入门很简单,但是随着时间自己对java的底层越发的有兴趣, 因为你不知道底层在做开发的时候种感觉有种我能开发代码但是不知道代码为什么会这样。同样底层知道的多一点对你的代码优化也是有好处的。先来个经典的类加载图片: 这个图片在 网上可以说是烂大街了,但是 真正能解释清楚的很少,甚至在我看到的文章中总是感觉有很多点矛盾,在网上看到最多的类加载讲解大部分是这样的:第一步: 加载: #1:通过一个类的全限定名来获取定义此类的二进制字节流
# 2:将这个字节流所代表的静态存储结构转化为方法区的运行时结构 #3:在java堆中生成一个代表这个类的java.lang.Class对象>,作为方法区的数据访问入口。 第二步:连接中又分 验证,准备,解析: #验证: 验证是连接阶段的第一步,这一阶段的目的是确保Class对象的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机的自身安全>。主要有:1,文件格式验证。2,元数据验证。3,字节码验证。4,符号引用验证。 # 准备:准备阶段就是正式为类变量分配内存并设置类变量的初始值的>阶段,这些内存都将在方法区中进行分配。 # 解析:解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过>程。
第三步:初始化:初始化阶段是真正开始执行类中定义的java程序代码。
第四步:使用
第五步: 下载 。
看到这样的讲解我会:很不解,有很多不明白的地方困扰我,这会使我难受,@1: 加载在什么情况下由什么加载? @2: 在加载的时候就已经创建Class对象,那 后面是怎么验证的,还是存在其他方式。 @3 : 初始化做了什么和实例化的区别是什么? @4: 第二步中的准备和解析是起什么作用? @5: 加载的类对象存在哪里?
根据以上这几点我进行总结:
#1:在我们执行主方法main的时候时候,会启动javaw.exe这个程序, 这个程序会加载JAVA_HOME/lib-->JAVA_HOME/ext-->我们自己的项目即ClassPath下的jar,这里面会设计到三个类加载器:1启动类加载器RootClassLoader(有时也称BootstrapClassLoader), 2扩展类加载器(Extension ClassLoader)3: 应用程序类加载器(Application ClassLoader)这三个对应加载 lib,ext,classpath下的程序class文件和jar, 这里还涉及到双亲委派机制,那么双亲委派机制是什么?他的作用是什么? &双亲委派:在加载一个类的时候先让此加载器的父类去加载,然后在用父类的父类去加载,一直向上找到最后一个父类加载器去加载这个类,正常都是找到了顶级父类加载器 BootstrapClassLoader ,加载不到这个Class(因为上面说过BootstrapClassLoader只负责lib下),然后 Extension ClassLoader 加入也加载不到,这时就该轮到 Application ClassLoader 加载我们classPath 下,如果找到了就加在并 将Class 对象放入永久代中也称静态方法区,否则报错ClassNotFound异常。
&双亲委派的作用: 双亲委派的机制我们已经了解到了,现在加入我们自定义了一个String类,该String类的包名和jdk中自带的String一摸一样, 假如说双亲委派的机制是不委托父类的,直接就被类加载器加载的,此类被加载到静态方法区(永久代)中,现在不讨论在永久代中是否覆盖了jdk中的String 的情况下,那么我们程序在引用JDK的String类的逻辑在运行时JVM会分不清到底是我们自己的String还是JDK的String(因为包名类名完全一样),从也导致jvm无法正确运行。所以双亲委派机制是保证jvm在运行时那些jdk基类是必须正确的,保证jvm运行的稳定。
#2 加载完成后开始验证这种说法是不对的,通常加载完成也代表Class 类对象创建完成,而校验是指,我在创建Class对象时是否class文件格式正确,是否符合jvm编译规范。只有一切都为正确才能创建Class对象,否则是创建不了的,所以加载和校验是交错前进的,也可以理解为两者并行前进,并不是把Class加载完后再去校验的,这是一个错误的观念,只要Class对象被创建了就是可以被使用的就是已经被校验成功的。
#3,初始化指的是类Class对象的 创建完成,而实例化指的是一个Class类的一个new 对象(也可通过其他方法创建,比如反射,clone,反序列化等,这里用new 来讲解方便理解) ,通常Class对象放在静态方法区(有些书上也称永久代),而new 对象通常经过yong代,或old代,当然也有存在永久代(也叫方法区)中比如:静态的实例对象。明白初始化和实例化的区别,初始化的作用主要为类变量引用赋值
#4 :(1准备)为类变量引用分配内存空间 (2解析) 常量符替换,比如: final int a=5,System.out.println(a); 他会将a直接替换成5 ,即:System.out.println(5);准备时分配了内存,但是内存中并没有数据,所以需要初始化赋值。
#5:Class对象创建完毕后会放在永久代(也叫方法区)中