likes
comments
collection
share

假如我是面试官,从Hello World来考你

作者站长头像
站长
· 阅读数 5

又是一年春招之时,在写完论文并提交盲审之后,我又开始了自己的学习总结之路。我想到了一个比较基础的题目(不喜勿喷),如果我是面试官,我一定会从这道问题展开面试。问:请从结合下面一段代码,讲一下你所掌握的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基础内容却蛮多的。且听我慢慢道来:

1. Demo命名中的知识点

一般这个文件,我们会将其命名为Demo.java。一个Java文件中,可以有多个类,但只能有一个public类,并且public的类名必须与文件名一致。关于这一问题,《Think in Java》中有这样几段话:

  1. 每个编译单元(文件)都只能有一个public类,这表示每个编译单元都有单一的公共接口,用public类来表现。该接口可以按要求包含众多的支持包访问权限的类。如果在某编译单元内有一个以上的public类,编译器就会给出错误信息。(按照我的理解,每个类代表一个对象,在编译过程中,以该对象为主体展开,如果存在多个主体,编译器会报错)
  2. public类的名称必须完全与含有编译单元的文件名相同,包含大小写,如果不匹配,同样将得到编译错误。(这就是规范问题了,就好像为啥那种生物叫鸡,另外一种生物叫鹅一样)
  3. 虽然不常用,但是编译单元内完全不带public类也是可能的。这种情况下,可以随意对文件命名。

当java源文件不存在public类时,会出现什么情况呢?

假如我是面试官,从Hello World来考你此时对该程序使用javac的编译命令时(这一过程其实还可以继续深入下去,此处先不展开),编译通过,在相应路径下产生三个.class文件。所以,一个Java文件中可以存在多个类,在编译时,会产生多个不同的.class文件,.class文件便是程序运行的数据来源。Java将public类作为每个编译单元的入口,只能有一个。当一个Java文件有多个非public类时,运行时需要对数据来源进行选择。

2. javac与java命令

前面提到了javac命令,通过javac Demo.java文件,可以生成Demo.class字节码文件,直接打开Demo.class字节码文件是一堆乱码,我们无从下手。为了得到Hello world的输出,需要使用java Demo的命令得到输出。通过javap反编译命令,可以得到字节码文件的详细内容。

假如我是面试官,从Hello World来考你这里面的内容就真的值得我们去深入研究学习了。但是目前我功力尚浅,还不能完全解释这些内容,所以不能大胆的去解释,害怕形成误导。需要注意的几点:例如invokespecial,invokevirtual,getstatic等内容需要注意,想要成为一名出色的Java程序员,这些东西是必须要学习的。**javac的编译过程主要涉及jdk中的一些处理操作,这些内容与编译原理高度相关。并未涉及到JVM的相关操作。**在编译之后,得到我们熟悉的字节码文件.class。使用java Demo过程,就是Java面试过程中常问的jvm相关内容。假如我是面试官,从Hello World来考你在控制台输入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虚拟机规范中的定义,是一种规范,而永久代是一种实现,一个是标准一个是实现,其他的虚拟机实现并没有永久代这一说法。

3.对象的创建

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基础的理解程度。