一、package
C/C++ 的 #include會(huì)把所包含的內(nèi)容在編譯時(shí)添加到程序文件中,而java的import則不同。
這里我們先了解一下Java 的 package 到底有何用處。
package名稱就像是我們的姓,而class名稱就像是我們的名字 。package和package的附屬關(guān)系用"."來(lái)連接,這就像是復(fù)姓。比如說(shuō) java.lang.String就是復(fù)姓 java.lang,名字為 String 的類別;java.io.InputStream 則是復(fù)姓 java.io,名字為 InputStream的類別。
Java 會(huì)使用 package 這種機(jī)制的原因也非常明顯,就像我們?nèi)⌒彰粯?,光是一間學(xué)校的同一屆同學(xué)中,就有可能會(huì)出現(xiàn)不少同名的同學(xué),如果不取姓的話,那學(xué)校在處理學(xué)生資料,或是同學(xué)彼此之間的稱呼,就會(huì)發(fā)生很大的困擾。相同的,全世界的 Java 類數(shù)量,恐怕比日本人還多,如果類別不使用package名稱,那在用到相同名稱的不同類時(shí), 就會(huì)產(chǎn)生極大的困擾。所以package這種方式讓極大降低了類之間的命名沖突。
Java 的package名稱我們可以自己取,不像人的姓沒(méi)有太大的選擇 ( 所以出現(xiàn)很多同名同姓的情況 ),如果依照 Sun 的規(guī)范來(lái)取套件名稱,那理論上不同人所取的套件名稱不會(huì)相同 ( 需要的話請(qǐng)參閱 “命名慣例” 的相關(guān)文章 ),也就不會(huì)發(fā)生名稱沖突的情況。
可是現(xiàn)在問(wèn)題來(lái)了,因?yàn)楹芏鄍ackage的名稱非常的長(zhǎng),在編程時(shí),要使用一個(gè)類要將多個(gè)包名.類名完全寫出,會(huì)讓代碼變得冗長(zhǎng),減低了簡(jiǎn)潔度。例如
java.io.InputStream is = java.lang.System.in;
java.io.InputStreamReader isr= new java.io.InputStreamReader(is);
java.io.BufferedReader br = new java.io.BufferedReader(isr);
顯得非常麻煩,于是Sun公司就引入了import。
二、import
import就是在java文件開頭的地方,先說(shuō)明會(huì)用到那些類別。
接著我們就能在代碼中只用類名指定某個(gè)類,也就是只稱呼名字,不稱呼他的姓。
首先,在程序開頭寫:
import java.lang.System;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
于是我們就可以在程序中這樣寫到:
InputStream = System.in;
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
一個(gè)java文件就像一個(gè)大房間,我們?cè)陂T口寫著在房間里面的class的姓和名字,所以在房間里面提到某個(gè)class就直接用他的名字就可以。例如:
System 就是指 java.lang.System,而 InputStream 就是指 java.io.InputStream。
但是如果一個(gè)java文件里面有多個(gè)同個(gè)“姓”,即包名相同的類(例如上面的InputStream,InputStreamReader,BufferedReader都是java.io中的類),我們一一寫出顯得比較繁雜,所以Sun就讓我們可以使用
import java.lang.*;
import java.io.*;
表示文件里面說(shuō)到的類不是java.lang包的就是java.io包的。編譯器會(huì)幫我們選擇與類名對(duì)應(yīng)的包。
那我們可不可以再懶一點(diǎn)直接寫成下面聲明呢?
import java.*;
歷史告訴我們,這樣是不行的。因?yàn)槟切╊悇e是姓 java.io 而不是姓 java。就像姓『諸葛』的人應(yīng)該不會(huì)喜歡你稱他為『諸』 先生吧。這樣寫的話只會(huì)將java包下的類聲明,而不不會(huì)聲明子包的任何類。
這里注意,java.lang包里面的類實(shí)在是太常太常太常用到了,幾乎沒(méi)有類不用它的, 所以不管你有沒(méi)有寫 import java.lang,編譯器都會(huì)自動(dòng)幫你補(bǔ)上,也就是說(shuō)編譯器只要看到?jīng)]有姓的類別,它就會(huì)自動(dòng)去lang包里面查找。所以我們就不用特別去 import java.lang了。
一開始說(shuō) import 跟 #include 不同,是因?yàn)閕mport 的功能到此為止,它不像#include 一樣,會(huì)將其他java文件的內(nèi)容載入進(jìn)來(lái)。import 只是讓編譯器編譯這個(gè)java文件時(shí)把沒(méi)有姓的類別加上姓,并不會(huì)把別的文件程序?qū)戇M(jìn)來(lái)。你開心的話可以不使用import,只要在用到類別的時(shí)候,用它的全部姓名來(lái)稱呼它就行了(就像例子一開始那樣),這樣跟使用import功能完全一樣。
三、import的兩種導(dǎo)入聲明
- 單類型導(dǎo)入(single-type-import)(例:import java.util.ArrayList; )
- 按需類型導(dǎo)入(type-import-on-demand)(例:import java.util.*;)
有如下屬性:
- java以這樣兩種方式導(dǎo)入包中的任何一個(gè)public的類和接口(只有public類和接口才能被導(dǎo)入)
- 上面說(shuō)到導(dǎo)入聲明僅導(dǎo)入聲明目錄下面的類而不導(dǎo)入子包,這也是為什么稱它們?yōu)轭愋蛯?dǎo)入聲明的原因。
- 導(dǎo)入的類或接口的簡(jiǎn)名(simple name)具有編譯單元作用域。這表示該類型簡(jiǎn)名可以在導(dǎo)入語(yǔ)句所在的編譯單元的任何地方使用.這并不意味著你可以使用該類型所有成員的簡(jiǎn)名,而只能使用類型自身的簡(jiǎn)名。例如: java.lang包中的public類都是自動(dòng)導(dǎo)入的,包括Math和System類.但是,你不能使用它們的成員的簡(jiǎn)名PI()和gc(),而必須使用Math.PI()和System.gc().你不需要鍵入的是java.lang.Math.PI()和java.lang.System.gc()。
- 程序員有時(shí)會(huì)導(dǎo)入當(dāng)前包或java.lang包,這是不需要的,因?yàn)楫?dāng)前包的成員本身就在作用域內(nèi),而java.lang包是自動(dòng)導(dǎo)入的。java編譯器會(huì)忽略這些冗余導(dǎo)入聲明(redundant import declarations)。即使像這樣import java.util.ArrayList;import java.util.*;多次導(dǎo)入,也可編譯通過(guò)。編譯器會(huì)將冗余導(dǎo)入聲明忽略.
四、static import靜態(tài)導(dǎo)入
在Java程序中,是不允許定義獨(dú)立的函數(shù)和常量的。即什么屬性或者方法的使用必須依附于什么東西,例如使用類或接口作為掛靠單位才行(在類里可以掛靠各種成員,而接口里則只能掛靠常量)。
如果想要直接在程序里面不寫出其他類或接口的成員的掛靠單元,有一種變通的做法 :
將所有的常量都定義到一個(gè)接口里面,然后讓需要這些常量的類實(shí)現(xiàn)這個(gè)接口(這樣的接口有一個(gè)專門的名稱,叫(“Constant Interface”)。這個(gè)方法可以工作。但是,因?yàn)檫@樣一來(lái),就可以從“一個(gè)類實(shí)現(xiàn)了哪個(gè)接口”推斷出“這個(gè)類需要使用哪些常量”,有“會(huì)暴露實(shí)現(xiàn)細(xì)節(jié)”的問(wèn)題。
于是J2SE 1.5里引入了“Static Import”機(jī)制,借助這一機(jī)制,可以用略掉所在的類或接口名的方式,來(lái)使用靜態(tài)成員。static import和import其中一個(gè)不一致的地方就是static import導(dǎo)入的是靜態(tài)成員,而import導(dǎo)入的是類或接口類型。
如下是一個(gè)有靜態(tài)變量和靜態(tài)方法的類
package com.assignment.test;
public class staticFieldsClass {
static int staticNoPublicField = 0;
public static int staticField = 1;
public static void staticFunction(){}
}
平時(shí)我們使用這些靜態(tài)成員是用類名.靜態(tài)成員的形式使用,即staticFieldsClass.staticField或者staticFieldsClass.staticFunction()。
現(xiàn)在用static import的方式:
//**精準(zhǔn)導(dǎo)入**
//直接導(dǎo)入具體的靜態(tài)變量、常量、方法方法,注意導(dǎo)入方法直接寫方法名不需要括號(hào)。
import static com.assignment.test.StaticFieldsClass.staticField;
import static com.assignment.test.StaticFieldsClass.staticFunction;
//或者使用如下形式:
//**按需導(dǎo)入**不必逐一指出靜態(tài)成員名稱的導(dǎo)入方式
//import static com.assignment.test.StaticFieldsClass.*;
public class StaticTest {
public static void main(String[] args) {
//這里直接寫靜態(tài)成員而不需要通過(guò)類名調(diào)用
System.out.println(staticField);
staticFunction();
}
}
這里有幾個(gè)問(wèn)題需要弄清楚:
- Static Import無(wú)權(quán)改變無(wú)法使用本來(lái)就不能使用的靜態(tài)成員的約束,上面例子的StaticTest和staticFieldsClass不是在同一個(gè)包下,所以StaticTest只能訪問(wèn)到staticFieldsClass中public的變量。使用了Static Import也同樣如此。
- 導(dǎo)入的靜態(tài)成員和本地的靜態(tài)成員名字相同起了沖突,這種情況下的處理規(guī)則,是“本地優(yōu)先。
- 不同的類(接口)可以包括名稱相同的靜態(tài)成員。例如在進(jìn)行Static Import的時(shí)候,出現(xiàn)了“兩個(gè)導(dǎo)入語(yǔ)句導(dǎo)入同名的靜態(tài)成員”的情況。在這種時(shí)候,J2SE 1.5會(huì)這樣來(lái)加以處理:
- 如果兩個(gè)語(yǔ)句都是精確導(dǎo)入的形式,或者都是按需導(dǎo)入的形式,那么會(huì)造成編譯錯(cuò)誤。
- 如果一個(gè)語(yǔ)句采用精確導(dǎo)入的形式,一個(gè)采用按需導(dǎo)入的形式,那么采用精確導(dǎo)入的形式的一個(gè)有效。
大家都這么聰明上面的幾個(gè)特性我就不寫例子了。
static import這么叼那它有什么負(fù)面影響嗎?
答案是肯定的,去掉靜態(tài)成員前面的類型名,固然有助于在頻繁調(diào)用時(shí)顯得簡(jiǎn)潔,但是同時(shí)也失去了關(guān)于“這個(gè)東西在哪里定義”的提示信息,理解或維護(hù)代碼就呵呵了。
但是如果導(dǎo)入的來(lái)源很著名(比如java.lang.Math),這個(gè)問(wèn)題就不那么嚴(yán)重了。
五、按需導(dǎo)入機(jī)制
使用按需導(dǎo)入聲明是否會(huì)降低Java代碼的執(zhí)行效率?
**絕對(duì)不會(huì)! **
1.import的按需導(dǎo)入
import java.util.*;
public class NeedImportTest {
public static void main(String[] args) {
ArrayList tList = new ArrayList();
}
}
編譯之后的class文件 :
//import java.util.*被替換成import java.util.ArrayList
//即按需導(dǎo)入編譯過(guò)程會(huì)替換成單類型導(dǎo)入。
import java.util.ArrayList;
public class NeedImportTest {
public static void main(String[] args) {
new ArrayList();
}
}
2.static import的按需導(dǎo)入
import static com.assignment.test.StaticFieldsClass.*;
public class StaticNeedImportTest {
public static void main(String[] args) {
System.out.println(staticField);
staticFunction();
}
}
上面StaticNeedImportTest 類編譯之后 :
//可以看出 :
//1、static import的精準(zhǔn)導(dǎo)入以及按需導(dǎo)入編譯之后都會(huì)變成import的單類型導(dǎo)入
import com.assignment.test.StaticFieldsClass;
public class StaticNeedImportTest {
public static void main(String[] args) {
//2、編譯之后“打回原形”,使用原來(lái)的方法調(diào)用靜態(tài)成員
System.out.println(StaticFieldsClass.staticField);
StaticFieldsClass.staticFunction();
}
}
六、附加
這是否意味著你總是可以使用按需導(dǎo)入聲明?
**是,也不是! **
在類似Demo的非正式開發(fā)中使用按需導(dǎo)入聲明顯得很有用。
然而,有這四個(gè)理由讓你可以放棄這種聲明:
- 編譯速度:在一個(gè)很大的項(xiàng)目中,它們會(huì)極大的影響編譯速度.但在小型項(xiàng)目中使用在編譯時(shí)間上可以忽略不計(jì)。
- 命名沖突:解決避免命名沖突問(wèn)題的答案就是使用全名。而按需導(dǎo)入恰恰就是使用導(dǎo)入聲明初衷的否定。
- 說(shuō)明問(wèn)題:畢竟高級(jí)語(yǔ)言的代碼是給人看的,按需導(dǎo)入看不出使用到的具體類型。
- 無(wú)名包問(wèn)題:如果在編譯單元的頂部沒(méi)有包聲明,Java編譯器首選會(huì)從無(wú)名包中搜索一個(gè)類型,然后才是按需類型聲明。如果有命名沖突就會(huì)產(chǎn)生問(wèn)題。
Sun的工程師一般不使用按需類型導(dǎo)入聲明.這你可以在他們的代碼中找到:
在java.util.Properties類中的導(dǎo)入聲明:
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.io.BufferedWriter;
import java.security.AccessController;
import java.security.PrivilegedAction;
可以看到他們用單類型導(dǎo)入詳細(xì)的列出了需要的java.io包中的具體類型。
以上就是Java中的import關(guān)鍵字的詳細(xì)內(nèi)容,想要了解更多關(guān)于java中其他關(guān)鍵字的資料,請(qǐng)關(guān)注W3Cschool其它相關(guān)文章!也希望大家能夠多多支持!