1. 讲一下Java的内存模型?
Java虚拟机规范中将Java运行时数据分为五种。
1.程序计数器:程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。Java虚拟机的多线程就是通过线程轮流切换并分配处理器时间来实现的,为了线程切换后能恢复到正确的位置,每条线程都需要一个独立的程序计数器,互不影响,该区域为“线程私有”。
2.Java虚拟机栈:线程私有的,与线程生命周期相同,用于存储局部变量表,操作栈,方法返回值。局部变量表放着基本数据类型,还有对象的引用。
3.本地方法栈:跟虚拟机栈很像,不过它是为虚拟机使用到的Native方法服务。
4.Java堆:所有线程共享的一块内存区域,对象实例几乎都在这分配内存。
5.方法区:各个线程共享的区域,储存虚拟机加载的类信息,常量,静态变量,编译后的代码。
友情链接:Java中JVM虚拟机详解
2. 你能不能谈谈,java GC是在什么时候,对什么东西,做了什么事情?
在什么时候:
1.新生代有一个Eden区和两个survivor区,首先将对象放入Eden区,如果空间不足就向其中的一个survivor区上放,如果仍然放不下就会引发一次发生在新生代的minor GC,将存活的对象放入另一个survivor区中,然后清空Eden和之前的那个survivor区的内存。在某次GC过程中,如果发现仍然又放不下的对象,就将这些对象放入老年代内存里去。
2.大对象以及长期存活的对象直接进入老年区。
3.当每次执行minor GC的时候应该对要晋升到老年代的对象进行分析,如果这些马上要到老年区的老年对象的大小超过了老年区的剩余大小,那么执行一次Full GC以尽可能地获得老年区的空间。
对什么东西:从GC Roots搜索不到,而且经过一次标记清理之后仍没有复活的对象。
做什么: 新生代:复制清理; 老年代:标记-清除和标记-压缩算法; 永久代:存放Java中的类和加载类的类加载器本身。
GC Roots都有哪些: 1. 虚拟机栈中的引用的对象 2. 方法区中静态属性引用的对象,常量引用的对象 3. 本地方法栈中JNI(即一般说的Native方法)引用的对象。
3. 类加载器工作机制?
1.加载:将Java二进制代码导入jvm中,生成Class文件。
2.连接:a)校验:检查载入Class文件数据的正确性 b)准备:给类的静态变量分配存储空间 c)解析:将符号引用转成直接引用
3:初始化:对类的静态变量,静态方法和静态代码块执行初始化工作。
双亲委派模型:类加载器收到类加载请求,首先将请求委派给父类加载器完成 用户自定义加载器->应用程序加载器->扩展类加载器->启动类加载器。
4. JAVA 中堆和栈的区别,说下java 的内存机制?
a.基本数据类型比变量和对象的引用都是在栈分配的
b.堆内存用来存放由new创建的对象和数组
c.类变量(static修饰的变量),程序在一加载的时候就在堆中为类变量分配内存,堆中的内存地址存放在栈中
d.实例变量:当你使用java关键字new的时候,系统在堆中开辟并不一定是连续的空间分配给变量,是根据零散的堆内存地址,通过哈希算法换算为一长串数字以表征这个变量在堆中的”物理位置”,实例变量的生命周期–当实例变量的引用丢失后,将被GC(垃圾回收器)列入可回收“名单”中,但并不是马上就释放堆中内存
e.局部变量: 由声明在某方法,或某代码段里(比如for循环),执行到它的时候在栈中开辟内存,当局部变量一但脱离作用域,内存立即释放
5. GC是什么?为什么要有GC?
GC是垃圾收集的意思,内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。Java程序员不用担心内存管理,因为垃圾收集器会自动进行管理。要请求垃圾收集,可以调用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但JVM可以屏蔽掉显示的垃圾回收调用。
垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低优先级的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。在Java诞生初期,垃圾回收是Java最大的亮点之一,因为服务器端的编程需要有效的防止内存泄露问题,然而时过境迁,如今Java的垃圾回收机制已经成为被诟病的东西。移动智能终端用户通常觉得iOS的系统比Android系统有更好的用户体验,其中一个深层次的原因就在于Android系统中垃圾回收的不可预知性。
6. 怎样判断一个对象是否需要收集?
GC的两种判定方法:引用计数与可达性分析法。
引用计数:给对象添加一个引用计数器,每当有一个地方引用该对象时,计数器值加1,当引用失效时,计数器值减1。任何时候计数器都为0的对象就是不可能再被使用的。引用计数缺陷:引用计数无法解决循环引用问题:假设对象A,B都已经被实例化,让A=B,B=A,除此之外这两个对象再无任何引用,此时计数器的值就永远不可能为0,但是引用计数器无法通知gc回收他们。
可达性分析法(GC Roots Traceing): 通过一系列名为“GC Roots”的对象作为起点,从这些节点开始向下搜索,搜索走过的路径成为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象不可用。 GC Roots对象一般是:虚拟机栈中的引用对象,方法区中类静态属性引用的对象,方法区常量引用的对象等。
对于可达性分析算法而言,未到达的对象并非是“非死不可”的,若要宣判一个对象死亡,至少需要经历两次标记阶段。
1. 如果对象在进行可达性分析后发现没有与GCRoots相连的引用链,则该对象被第一次标记并进行一次筛选,筛选条件为是否有必要执行该对象的finalize方法,若对象没有覆盖finalize方法或者该finalize方法已经被虚拟机执行过了,则均视作不必要执行该对象的finalize方法,即该对象将会被回收。反之,若对象覆盖了finalize方法并且该finalize方法并没有被执行过,那么,这个对象会被放置在一个叫F-Queue的队列中,之后会由虚拟机自动建立的、优先级低的Finalizer线程去执行,而虚拟机不必要等待该线程执行结束,即虚拟机只负责建立线程,其他的事情交给此线程去处理。
2.对F-Queue中对象进行第二次标记,如果对象在finalize方法中拯救了自己,即关联上了GCRoots引用链,如把this关键字赋值给其他变量,那么在第二次标记的时候该对象将从“即将回收”的集合中移除,如果对象还是没有拯救自己,那就会被回收。注意,它只能拯救自己一次,第二次就被回收了。
7. Java中的四种引用?
强引用:程序代码中的普通引用。如Object obj = new Object(),只要强引用存在,垃圾回收器就不会回收。
软引用:描述一些有用但并非必须的对象。对于软引用关联的对象在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。
弱引用(SoftRefence):描述非必须对象,比软引用弱一些。被弱引用关联的对象只能生存到下一次垃圾收集发生之前。无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
虚引用(WeakRefence):最弱的引用,不管是否有虚引用存在,完全不会对对象生存时间构成影响,也无法通过虚引用来取得一个对象实例。唯一目的是希望能够在这个对象被垃圾收集器回收之前收到系统通知。
8. 对象创建方法,对象的内存分配,对象的访问定位?
Object obj = new Object(); obj 保存在java栈中的局部变量表里,作为一个引用数据出现。New Object()会在java堆上分配一块存储Object类型实例的所有数值的结构化内存,根据类型以及虚拟机实现的对象内存布局不同。这块内存是不固定的。 对象访问方式有两种:句柄和直接指针。
句柄:在java堆中会划分出一块内存作为句柄池,reference中存储的对象是句柄地址。而句柄中包含对象实例数据和类型数据各自的具体地址信息。最大的好处是如果对象地址发生变化不需要改变reference的值,只需要改变句柄中实例数据指针。
直接指针访问:reference直接存储对象的地址,最大的好处是速度更快。
9. JVM分代垃圾回收策略,为什么要分代?
分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率。
在Java程序运行的过程中,会产生大量的对象,其中有些对象是与业务信息相关,比如Http请求中的Session对象、线程、Socket连接,这类对象跟业务直接挂钩,因此生命周期比较长。但是还有一些对象,主要是程序运行过程中生成的临时变量,这些对象生命周期会比较短,比如:String对象,由于其不变类的特性,系统会产生大量的这些对象,有些对象甚至只用一次即可回收。
试想,在不进行对象存活时间区分的情况下,每次垃圾回收都是对整个堆空间进行回收,花费时间相对会长,同时,因为每次回收都需要遍历所有存活对象,但实际上,对于生命周期长的对象而言,这种遍历是没有效果的,因为可能进行了很多次遍历,但是他们依旧存在。因此,分代垃圾回收采用分治的思想,进行代的划分,把不同生命周期的对象放在不同代上,不同代上采用最适合它的垃圾回收方式进行回收。
10.如何分代?
虚拟机中的共划分为三个代:年轻代(Young Generation)、年老代(Old Generation)和持久代(Permanent Generation)。其中持久代主要存放的是Java类的类信息,与垃圾收集要收集的Java对象关系不大。年轻代和年老代的划分是对垃圾收集影响比较大的。
11.年轻代、年老代、和持久代介绍?
年轻代:
所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区。一个Eden区,两个Survivor区(一般而言)。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor区也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来的对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor区过来的对象。而且,Survivor区总有一个是空的。同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。
新生代有划分为Eden、From Survivor和To Survivor三个部分,他们对应的内存空间的大小比例为8:1:1,也就是,为对象分配内存的时候,首先使用Eden空间,经过GC后,没有被回收的会首先进入From Survivor区域,任何时候,都会保持一个Survivor区域(From Survivor或To Survivor)完全空闲,也就是说新生代的内存利用率最大为90%。From Survivor和To Survivor两个区域会根据GC的实际情况,进行互换,将From Survivor区域中的对象全部复制到To Survivor区域中,或者反过来,将To Survivor区域中的对象全部复制到From Survivor区域中。
年老代:
在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。
GC过程中,当某些对象经过多次GC都没有被回收,可能会进入到年老代。或者,当新生代没有足够的空间来为对象分配内存时,可能会直接在年老代进行分配。
持久代:
用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=<N>进行设置。
永久代实际上对应着虚拟机运行时数据区的“方法区”,这里主要存放类信息、静态变量、常量等数据。一般情况下,永久代中对应的对象的GC效率非常低,因为这里的的大部分对象在运行都不要进行GC,它们会一直被利用,直到JVM退出。
12. JVM内存分配与回收策略?
1)对象优先在Eden区分配
对象通常在新生代的Eden区进行分配,当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC,与Minor GC对应的是Major GC、Full GC。
Minor GC:指发生在新生代的垃圾收集动作,非常频繁,速度较快。
Major GC:指发生在老年代的GC,出现Major GC,经常会伴随一次Minor GC,同时Minor GC也会引起Major GC,一般在GC日志中统称为GC,不频繁。
Full GC:指发生在老年代和新生代的GC,速度很慢,需要Stop The World。
2)大对象直接进入老年代
需要大量连续内存空间的Java对象称为大对象,大对象的出现会导致提前触发垃圾收集以获取更大的连续的空间来进行大对象的分配。虚拟机提供了-XX:PretenureSizeThreadshold参数来设置大对象的阈值,超过阈值的对象直接分配到老年代。
3)长期存活的对象进入老年代
每个对象有一个对象年龄计数器,与前面的对象的存储布局中的GC分代年龄对应。对象出生在Eden区、经过一次Minor GC后仍然存活,并能够被Survivor容纳,设置年龄为1,对象在Survivor区每次经过一次Minor GC,年龄就加1,当年龄达到一定程度(默认15),就晋升到老年代,虚拟机提供了-XX:MaxTenuringThreshold来进行设置。
4)动态对象年龄判断
对象的年龄到达了MaxTenuringThreshold可以进入老年代,同时,如果在survivor区中相同年龄所有对象大小的总和大于survivor区的一半,年龄大于等于该年龄的对象就可以直接进入老年代。无需等到MaxTenuringThreshold中要求的年龄。
5)空间分配担保
在发生Minor GC时,虚拟机会检查老年代连续的空闲区域是否大于新生代所有对象的总和,若成立,则说明Minor GC是安全的,否则,虚拟机需要查看HandlePromotionFailure的值,看是否运行担保失败,若允许,则虚拟机继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,若大于,将尝试进行一次Minor GC;若小于或者HandlePromotionFailure设置不运行冒险,那么此时将改成一次Full GC,以上是JDK Update 24之前的策略,之后的策略改变了,只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC,否则将进行Full GC。
冒险是指经过一次Minor GC后有大量对象存活,而新生代的survivor区很小,放不下这些大量存活的对象,所以需要老年代进行分配担保,把survivor区无法容纳的对象直接进入老年代。
13. 内存溢出和内存泄漏 ?
内存溢出:通俗理解就是内存不够,程序所需要的内存远远超出了你虚拟机分配的内存大小,就叫内存溢出。
内存泄漏:内存泄漏也称作“存储渗漏”,用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏。
14. 内存溢出了怎么办?
通过内存映像工具如jhat,jconsole等对dump出来的堆转存储快照进行分析,重点是确认内存是出现内存泄露还是内存溢出。 如果是内存泄露进一步使用工具查看泄露的对象到GC Roots的引用链。于是就能找到泄露对象是通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收它们。掌握泄露对象的信息,以及GC Roots引用链的信息,就可以比较准确定位泄露代码的位置。 如果不存在**内存泄露,那就需要通过jinfo,Jconsole等工具分析java堆参数与机器物理内存对比是否还可以调大,从代码上检查是否存在某些对象生命周期过长,持有状态过长的情况,尝试减少程序的运行消耗。
15. Java中有内存泄露吗?
有,Java中,造成内存泄露的原因有很多种。典型的例子是长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景,通俗地说,就是程序员可能创建了一个对象,以后一直不再使用这个对象,这个对象却一直被引用,即这个对象无用但是却无法被垃圾回收器回收的,这就是java中可能出现内存泄露的情况,例如,缓存系统,我们加载了一个对象放在缓存中(例如放在一个全局map对象中),然后一直不再使用它,这个对象一直被缓存引用,但却不再被使用。检查java中的内存泄露,一定要让程序将各种分支情况都完整执行到程序结束,然后看某个对象是否被使用过,如果没有,则才能判定这个对象属于内存泄露。(采用什么工具?) 如果一个外部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持有外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄露。
16. 什么时候会发生jvm堆内存溢出?
简单的来说 java的堆内存分为两块:permantspace(持久代) 和 heap space。 持久带中主要用于存放静态类型数据,如 Java Class,,Method 等, 与垃圾收集器要收集的Java对象关系不大。 而heap space分为年轻代和年老代 年轻代的垃圾回收叫 Young GC, 年老代的垃圾回收叫 Full GC。 在年轻代中经历了N次(可配置)垃圾回收后仍然存活的对象,就会被复制到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象 年老代溢出原因有 循环上万次的字符串处理、创建上千万个对象、在一段代码内申请上百M甚至上G的内存 持久代溢出原因动态加载了大量Java类而导致溢出,以及生产大量的常量。 永久代内存泄露:以一个部署到应用程序服务器的Java web程序来说,当该应用程序被卸载的时候,你的EAR/WAR包中的所有类都将变得无用。只要应用程序服务器还活着,JVM将继续运行,但是一大堆的类定义将不再使用,理应将它们从永久代(PermGen)中移除。如果不移除的话,我们在永久代(PermGen)区域就会有内存泄漏。
17. OOM你遇到过哪些情况?
java.lang.OutOfMemoryError:Java heap space ------>java堆内存溢出,此种情况最常见,一般由于内存泄露或者堆的大小设置不当引起。
java.lang.OutOfMemoryError:PermGen space ------>java永久代溢出,即方法区溢出了,一般出现于大量Class或者jsp页面,或者采用cglib等反 射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。
java.lang.StackOverflowError ------> 不会抛OOM error,但也是比较常见的Java内存溢出。JAVA虚拟机栈溢出,一般是由于程序中存在死循环或者深度递归调用造成的,栈大小设置太小也会出现此种溢出。可以通过虚拟机参数-Xss来设置栈的大小。
18. GC的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方?
标记清除:
首先标记所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象,它的标记的对象。缺点是效率低,且存在内存碎片。主要用于老生代垃圾回收。
复制算法:
将内存按容量划分为大小相等的一块,每次只用其中一块。当内存用完了,将还存活的对象复制到另一块内存,然后把已使用过的内存空间一次清理掉。实现简单,高效。一般用于新生代。一般是将内存分为一块较大的Eden空间和两块较小的Survivor空间。HotSpot虚拟机默认比例是8:1,。每次使用Eden和一块Survivor,当回收时将这两块内存中还存活的对象复制到Survivor然后清理掉刚才Eden和Survivor的空间。如果复制过程内存不够使用则向老年代分配担保。
标记整理:
首先标记所有需要回收的对象,在标记完成后让所有存活的对象都向一端移动,然后直接清理掉端边界意外的内存。用于老年代。
分代收集算法:
根据对象的生存周期将内存划分为新生代和老年代,根据年代的特点采用最适当的收集算法。
19. Minor GC 与 Full GC 分别在什么时候发生?
FullGC 一般是发生在老年代的GC,出现一个FullGC经常会伴随至少一次的Minor GC。速度比MinorGC慢10倍以上。FULL GC发生的情况:
1) 老年代空间不足。老年代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出如下错误:java.lang.OutOfMemoryError: Java heap space 。措施:为避免以上两种状况引起的FullGC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。
2) Permanet Generation(方法区或永久代)空间满。PermanetGeneration中存放的为一些class的信息等,当系统中要加载的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满,在未配置为采用CMS GC的情况下会执行Full GC。如果经过Full GC仍然回收不了,那么JVM会抛出如下错误信息: java.lang.OutOfMemoryError: PermGen space。措施:为避免Perm Gen占满造成Full GC现象,可采用的方法为增大Perm Gen空间或转为使用CMS GC。
3) CMS GC时出现promotion failed和concurrent mode failure 对于采用CMS进行老年代GC的程序而言,尤其要注意GC日志中是否有promotion failed和concurrent mode failure两种状况,当这两种状况出现时可能会触发Full GC。 promotion failed是在进行Minor GC时,survivor space放不下、对象只能放入老年代,而此时老年代也放不下造成的; concurrent mode failure: CMS在执行垃圾回收时需要一部分的内存空间并且此刻用户程序也在运行需要预留一部分内存给用户程序,如果预留的内存无法满足程序需求就出现一次"Concurrent mod failure",并触发一次Full GC。 应对措施为:增大survivor space、老年代空间或调低触发并发GC的比率。
4) 统计得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间。 Hotspot为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象,在进行Minor GC时,做了一个判断,如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间,那么就直接触发Full GC。如果小于并且不允许担保失败也会发生一次Full GC。
MinorGC 指发生在新生代的垃圾收集动作,非常频繁,回收速度也快。一般发生在新生代空间不足时。另外一个FullGC经常会伴随至少一次的Minor GC.。当虚拟机检测晋升到老年代的平均大小是否小于老年代剩余空间大小,如果小于并且允许担保失败,则执行Minor GC。
20. 类加载的五个过程:加载、验证、准备、解析、初始化 ?
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。
加载:
根据全限定名来获取定义类的二进制字节流,然后将该字节流所代表的静态结构转化为方法区的运行时数据结构,最后在java堆上生成一个代表该类的Class对象,作为方法区这些数据的访问入口。
验证:
主要是为了确保class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。包含四个阶段的验证过程:
1)文件格式验证:保证输入的字节流能够正确地解析并存储在方法区之内,格式上符合描述一个java类型信息的要求。
2)元数据验证:字节码语义信息的验证,以保证描述的信息符合java语言规范。验证点有:这个类是否有父类等。
3)字节码验证:主要是进行数据流和控制流分析,保证被校验类的方法在运行时不会做出危害虚拟机安全的行为.。
4)符号引用验证:对符号引用转化为直接引用过程的验证。
准备:
为类变量分配内存并设置变量的初始值,这些内存在方法区进行分配。
这个阶段正式为类变量(被static修饰的变量)分配内存并设置类变量初始值,这个内存分配是发生在方法区中。
1)注意这里并没有对实例变量进行内存分配,实例变量将会在对象实例化时随着对象一起分配在JAVA堆中。
2)这里设置的初始值,通常是指数据类型的零值。
privatestaticinta = 3;
这个类变量a在准备阶段后的值是0,将3赋值给变量a是发生在初始化阶段。
解析:
将虚拟机常量池中的符号引用转化为直接引用的过程。解析主要是针对类或接口,字段,类方法。
初始化:
执行静态变量的赋值操作以及静态代码块,完成初识化。初始化过程保证了父类中定义的初始化优先于子类的初始化,但接口不需要执行父类的初始化。
初始化是类加载机制的最后一步,这个时候才正真开始执行类中定义的JAVA程序代码。在前面准备阶段,类变量已经赋过一次系统要求的初始值,在初始化阶段最重要的事情就是对类变量进行初始化。在Java中对类变量进行初始值设定有两种方式:
1)声明类变量时指定初始值
2)使用静态代码块为类变量指定初始值