#include是個宏命令,在文件編譯階段,會先將#include展開,也就是說被#include引用的文件會在源文件中展開。
舉例:
有兩個.c源文件,一個是my.c還有一個是main.c
假如my.c文件內容是:html
void fun(int x){printf("%d\n",x);}
而main.c文件的內容是:java
#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文件。
而後編譯器其實是對新的內容進行編譯處理。
編譯的步驟則是:編譯步驟windows
而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()就能夠了,徹底沒有上面的那些步驟。
固然能這麼方即是得益於虛擬機中的類加載器。code