在soso上面看到有人问为什么java.lang的包中的东西在java中不需要导入。引发了我对java编译的思考。
所谓的默认导入java.lang的,这一步我们是在哪里做的呢?应该是在编译期。那在编译器的什么时候呢?
我们import包的方式有两种: 1.单类型导入(single-type-import),例如import java.io.File;
2.按需类型导入(type-import-on-demand),例如 import java.io.*;
第一种,我们就不说了,大家都知道。第二种吗,估计很多人有个误区,认为是把java.io包下面的所有类都导入,其实是错了,对于按需类型导入则比较复杂,编译器会把包名和文件名进行排列组合,然后对所有的可能性进行类文件查找定位。例如:
package com;
import java.io.*;
import java.util.*;
当你的类文件中用到了File类,那么可能出现File类的地方如下:
1、File // File类属于无名包,就是说File类没有package语句,编译器会首先搜索无名包
2、com.File // File类属于当前包,就是我们当前编译类的包路径
3、java.lang.File //编译器会自动导入java.lang包
4、java.io.File
5、java.util.File
。。。
需要注意的地方就是,编译器找到java.io.File类之后并不会停止下一步的寻找,而要把所有的可能性都查找完以确定是否有类导入冲突。假设此时的顶层路径有三个,那么编译器就会进行3*5=15次查找。
所以呢,各位在编写代码的时候,最后使用单类型导入,这样会提高编译效率(但想想编译快不快对程序跑的快不快没有影响。。。囧)
好了,回正题了,我们看到第三点了吗?在我们编译按需类型的导入前就已经导入了java.lang包,那是不是说编译器在所有的import之前就引入java.lang包中的东西。进行到这边还是不知道啊,还是需要深入的学习啊。
其实上面所述内容属于java编译期的预处理阶段,也只是预处理的一部分。我们现在就根据import这条路往下走,代码中所有引用的对象都被确定好了路径并存储下来,即两种类型的处理方式。当预处理完了以后,这时候就要将所有的字符转换成字节码了。
说到这,就又得说说编译器在java的可移植性上面起到的重要作用,我们都知道java是跨平台的,那具体的原因呢?其实在编译的过程中,它已经为后面的跨平台做准备了。不同于c/c++,在c/c++中编译生成一个对象的代码时,该代码是在为某一特定硬件平台的运行而生产的,因此在编译过程中,将所有的对符号的引用转换成了内存偏移量。java不是,java编译器不将变量、方法的引用编译为数值引用,也不确定程序执行过程中的内存布局,而是将这些符号的字节引用信息保存在字节码中,由解释器创建内存布局,再通过查表来确定方法、变量所在的地址。这样就为java的可移植性和安全性提供了基础。同时这也是java在很多方面性能无法提升到c++同等位置的重要原因之一。