#include是个宏命令,在文件编译阶段,会先将#include展开,也就是说被#include引用的文件会在源文件中展开。
举例:
有两个.c源文件,一个是my.c还有一个是main.c
假如my.c文件内容是:
void fun(int x){printf("%d\n",x);}
而main.c文件的内容是:
#include"my.c"
int main()
{
return 0;
}
那么编译之前的预处理步骤,就会把#include的内容在main.c中展开,则变成:
void fun(int x){printf("%d\n",x);}
int main()
{
return 0;
}
当然,一般来讲是#include .h文件,而不是.c文件。
然后编译器实际上是对新的内容进行编译处理。
编译的步骤则是:编译步骤
而java的import是否也是将代码展开呢?
答案是否定的。
java语言中,每一个类都必须要用包名.类名的形式来描述。
只给出类名是无法完全描述一个类的,比如我们要用HashMap这个类就需要写成这样:
java.util.HashMap map = new java.util.HashMap();
这样的话每个类的调用都要这么麻烦,要记住包名,这样很不利于写项目代码。
java作为一门先进的现代编程语言,它的开发者也想到了这个问题,于是使用import来指定包或者直接指定类的
名字,这样就不需要每次都将类完全描述,使得开发者能够专注于软件开发而不是各种包的导入。
有了import之后,代码就变成了这样:
import java.util.*;
...
HashMap map = new HashMap();
...
不像c中的#include直接展开,这样做实际上只是指定了要引用的类,而具体的被引用类的代码则在执行的时候才调入内存(这个步骤是类加载器来实现的)。
这样做的好处是编译(编译成.class文件)速度较快,运行速度稍慢,而c语言的展开方式编译会非常慢,运行则很快。
当然c语言中也有运行时将代码调入内存的技术,即动态连接技术。
不过c语言的动态连接实在是麻烦,就好像是在现代社会中我要获得火种,非得钻木取火。
而且动态连接的前提还得是有动态链接库才行,如果没有就得自己写一个,写完之后编译的命令(编译成库文件和编译成可执行文件的命令)还不不一样。
有了库之后,就可以调用了,但是调用库又变成了一个无法理解的事情。
比如调用windows下的dll动态链接库中的函数:
#include <windows.h>
#include <stdio.h>
typedef int (*Fun)(int,int);
int main()
{
HINSTANCE hDll;
Fun Add;
hDll=LoadLibrary("myDll.dll");
if (hDll==NULL)
{
printf("%s","failed to load dll!\n");
}
else
{
printf("%s","succeeded in loading dll\n");
Add=(Fun)GetProcAddress(hDll,"add");
if (Add!=NULL)
{
printf("%d\n",Add(12,94));
}
}
FreeLibrary(hDll);
return 0;}
在这个动态库调用中,明明已经知道有个函数add(int,int),还非得写一堆东西,上面的那个typedef是定义了一个函数指针,为了指向add这个函数,但是???what???开发人员调用库的目的就是为了简单,就是为了方便,就是为了好用,如果调库还需要这么麻烦,还需要定义什么指针,那还调库干嘛。
而java的动态连接则不存在这种问题,因为java的设计原则中只有库,class文件就是库,而.jar不过是一堆的.class文件。在java编程中引用一个类实际上就进行了一次动态连接,并且这种动态连接却和调用本类一样方便,就比如自己写了一个my.MyMap类,在别的类里直接new my.MyMap()就可以了,完全没有上面的那些步骤。
当然能这么方便是得益于虚拟机中的类加载器。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。