又是一年春招之时,在写完论文并提交盲审之后,我又开始了自己的学习总结之路。我想到了一个比较基础的题目(不喜勿喷),如果我是面试官,我一定会从这道问题展开面试。
问:请从结合下面一段代码,讲一下你所掌握的Java知识点,多多益善。
public class Demo { public Demo(){} public void print(){ System.out.println("Hello world"); } public static void main(String[] args) { Demo demo=new Demo(); demo.print(); } } 复制代码
一段很普通的代码,结合这一普通的问题,我一开始确实想不到啥特殊的内容,但是仔细一想,里面的涉及到的Java基础内容却蛮多的。且听我慢慢道来:
一般这个文件,我们会将其命名为Demo.java。一个Java文件中,可以有多个类,但只能有一个public类,并且public的类名必须与文件名一致。关于这一问题,《Think in Java》中有这样几段话:
当java源文件不存在public类时,会出现什么情况呢?
此时对该程序使用javac的编译命令时(这一过程其实还可以继续深入下去,此处先不展开),编译通过,在相应路径下产生三个.class文件。所以,一个Java文件中可以存在多个类,在编译时,会产生多个不同的.class文件,.class文件便是程序运行的数据来源。Java将public类作为每个编译单元的入口,只能有一个。当一个Java文件有多个非public类时,运行时需要对数据来源进行选择。
前面提到了javac命令,通过javac Demo.java文件,可以生成Demo.class字节码文件,直接打开Demo.class字节码文件是一堆乱码,我们无从下手。为了得到Hello world的输出,需要使用java Demo的命令得到输出。通过javap反编译命令,可以得到字节码文件的详细内容。
这里面的内容就真的值得我们去深入研究学习了。但是目前我功力尚浅,还不能完全解释这些内容,所以不能大胆的去解释,害怕形成误导。需要注意的几点:例如invokespecial,invokevirtual,getstatic等内容需要注意,想要成为一名出色的Java程序员,这些东西是必须要学习的。**javac的编译过程主要涉及jdk中的一些处理操作,这些内容与编译原理高度相关。并未涉及到JVM的相关操作。**在编译之后,得到我们熟悉的字节码文件.class。使用java Demo过程,就是Java面试过程中常问的jvm相关内容。
在控制台输入java Demo时,会在内存区域创建一个Java虚拟机(JVM),JVM会将上述过程生成的Demo.class文件加载进方法区(1.8之后变为元数据区,放在直接内存中,但是仍然有相关的GC收集器进行管理)进行类加载过程,具体的有加载->验证->准备->解析->初始化->使用->卸载的过程。这部分在周志明老师的书中有专门的一部分进行了详细的解析。 类加载过程详解
几个关键步骤:
1.验证:主要验证class文件的版本能不能兼容当前JVM版本,class文件是否满足jvm规范
2.准备:把类成员初始化为初始值(final的类变量除外)。final变量直接初始化为变量值。
3.解析:把符号引用解析为直接引用,符号引用就是我们写的xx变量,xxx对应的引用,如Integer xxx=new Integer();
解析就是要把类似于xxx这种符号引用变为直接引用即内存地址。
4.初始化:把我们定义的static变量或者static静态代码块按顺序组织成(即类构造器)来初始化变量。
这里仅仅涉及到JVM内存的方法区的知识,方法区是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。方法区有时候也被人称为永久代,方法区和永久代的关系很像Java中接口和类的关系,类实现了接口,而永久代是HotSpot虚拟机对虚拟机规范中方法区的一种实现方式。永久代是HotSpot的概念,方法区是Java虚拟机规范中的定义,是一种规范,而永久代是一种实现,一个是标准一个是实现,其他的虚拟机实现并没有永久代这一说法。
Demo demo = new Demo(); 复制代码
这一句话,有对象创建的过程。new Demo()操作会创建一个对象,demo为一个对象的声明, 对象的声明就是对象的引用指向堆内存中开辟的对象。新创建的对象存储在堆空间里,相关的引用存储在栈(虚拟机栈)中。
在上述程序中,首先执行的是main代表的主线程,当执行遇到new关键字时,主线程便在自己的虚拟机栈中声明一个对象hello,在JVM的堆内存中申请一片内存区域,然后将Demo相关的信息,例如实例变量,实例方法等从方法区中加载到堆内存中。
对象中比较关键的是头部信息,主要有三部分构成:
1.MarkWord:主要描述 持有当前对象锁的线程ID和持有对象锁的线程个数 , 在gc中存活的生命周期数 , 偏向锁的标志等
2.类指针:指向方法区对应类信息的指针。
3.对齐填充
这些仅仅是通过几行简短的程序所能扩展出来的知识点。所以不要小看hello world,还是很有意思的。
如果在Demo.java文件中加入静态代码块,非静态代码块,继承等机制,难度会增加一个level。所以假如我是面试官,我会通过这一问题,考察你对jvm和java基础的理解程度。