Java import的作用


詳見:java中import作用詳解:https://blog.csdn.net/qq_25665807/article/details/74747868

可是現在問題來了,因為很多package的名稱非常的長,在編程時,要使用一個類要將多個包名.類名完全寫出,會讓代碼變得冗長,減低了簡潔度。例如

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就是在java文件開頭的地方,先說明會用到那些類別。
接着我們就能在代碼中只用類名指定某個類,也就是只稱呼名字,不稱呼他的姓。

 

但是如果一個java文件里面有多個同個“姓”,即包名相同的類(例如上面的InputStream,InputStreamReader,BufferedReader都是java.io中的類),

我們一一寫出顯得比較繁雜,所以Sun就讓我們可以使用

import java.lang.*;
import java.io.*;

表示文件里面說到的類不是java.lang包的就是java.io包的。編譯器會幫我們選擇與類名對應的包。

那我們可不可以再懶一點直接寫成下面聲明呢?

import java.*;

歷史告訴我們,這樣是不行的。因為那些類別是姓 java.io 而不是姓 java。就像姓『諸葛』的人應該不會喜歡你稱他為『諸』 先生吧。這樣寫的話只會將java包下的類聲明,而不不會聲明子包的任何類。

 

這里注意,java.lang包里面的類實在是太常太常太常用到了,幾乎沒有類不用它的, 所以不管你有沒有寫 import java.lang,編譯器都會自動幫你補上,也就是說編譯器只要看到沒有姓的類別,它就會自動去lang包里面查找。所以我們就不用特別去 import java.lang了。

一開始說 import 跟 #include 不同,是因為import 的功能到此為止,它不像#include 一樣,會將其他java文件的內容載入進來。import 只是讓編譯器編譯這個java文件時把沒有姓的類別加上姓,並不會把別的文件程序寫進來。你開心的話可以不使用import,只要在用到類別的時候,用它的全部姓名來稱呼它就行了(就像例子一開始那樣),這樣跟使用import功能完全一樣。

 

import的兩種導入聲明
單類型導入(single-type-import)
(例:import java.util.ArrayList; )
按需類型導入(type-import-on-demand)
(例:import java.util.*;)
有如下屬性:

java以這樣兩種方式導入包中的任何一個public的類和接口(只有public類和接口才能被導入)

上面說到導入聲明僅導入聲明目錄下面的類而不導入子包,這也是為什么稱它們為類型導入聲明的原因。

導入的類或接口的簡名(simple name)具有編譯單元作用域。這表示該類型簡名可以在導入語句所在的編譯單元的任何地方使用.這並不意味着你可以使用該類型所有成員的簡名,而只能使用類型自身的簡名。
例如: java.lang包中的public類都是自動導入的,包括Math和System類.但是,你不能使用它們的成員的簡名PI()和gc(),而必須使用Math.PI()和System.gc().你不需要鍵入的是java.lang.Math.PI()和java.lang.System.gc()。

程序員有時會導入當前包或java.lang包,這是不需要的,因為當前包的成員本身就在作用域內,而java.lang包是自動導入的。java編譯器會忽略這些冗余導入聲明(redundant import declarations)。即使像這樣
import java.util.ArrayList;
import java.util.*;
多次導入,也可編譯通過。編譯器會將冗余導入聲明忽略.

在Java程序中,是不允許定義獨立的函數和常量的。即什么屬性或者方法的使用必須依附於什么東西,例如使用類或接口作為掛靠單位才行(在類里可以掛靠各種成員,而接口里則只能掛靠常量)。

如果想要直接在程序里面不寫出其他類或接口的成員的掛靠單元,有一種變通的做法 :
將所有的常量都定義到一個接口里面,然后讓需要這些常量的類實現這個接口(這樣的接口有一個專門的名稱,叫(“Constant Interface”)。這個方法可以工作。但是,因為這樣一來,就可以從“一個類實現了哪個接口”推斷出“這個類需要使用哪些常量”,有“會暴露實現細節”的問題。

於是J2SE 1.5里引入了“Static Import”機制,借助這一機制,可以用略掉所在的類或接口名的方式,來使用靜態成員。static import和import其中一個不一致的地方就是static import導入的是靜態成員,而import導入的是類或接口類型。

如下是一個有靜態變量和靜態方法的類

package com.assignment.test;

public class staticFieldsClass { static int staticNoPublicField = 0; public static int staticField = 1; public static void staticFunction(){} }

平時我們使用這些靜態成員是用類名.靜態成員的形式使用,即staticFieldsClass.staticField或者staticFieldsClass.staticFunction()。

現在用static import的方式:

//**精准導入**
//直接導入具體的靜態變量、常量、方法方法,注意導入方法直接寫方法名不需要括號。
import static com.assignment.test.StaticFieldsClass.staticField;
import static com.assignment.test.StaticFieldsClass.staticFunction; //或者使用如下形式: //**按需導入**不必逐一指出靜態成員名稱的導入方式 //import static com.assignment.test.StaticFieldsClass.*; public class StaticTest { public static void main(String[] args) { //這里直接寫靜態成員而不需要通過類名調用  System.out.println(staticField); staticFunction(); } }

 

這里有幾個問題需要弄清楚:

Static Import無權改變無法使用本來就不能使用的靜態成員的約束,上面例子的StaticTest和staticFieldsClass不是在同一個包下,所以StaticTest只能訪問到staticFieldsClass中public的變量。使用了Static Import也同樣如此。

導入的靜態成員和本地的靜態成員名字相同起了沖突,這種情況下的處理規則,是“本地優先。

不同的類(接口)可以包括名稱相同的靜態成員。例如在進行Static Import的時候,出現了“兩個導入語句導入同名的靜態成員”的情況。在這種時候,J2SE 1.5會這樣來加以處理:

如果兩個語句都是精確導入的形式,或者都是按需導入的形式,那么會造成編譯錯誤。
如果一個語句采用精確導入的形式,一個采用按需導入的形式,那么采用精確導入的形式的一個有效。

 

static import這么叼那它有什么負面影響嗎?

答案是肯定的,去掉靜態成員前面的類型名,固然有助於在頻繁調用時顯得簡潔,但是同時也失去了關於“這個東西在哪里定義”的提示信息,理解或維護代碼就呵呵了。
但是如果導入的來源很著名(比如java.lang.Math),這個問題就不那么嚴重了。

按需導入機制
使用按需導入聲明是否會降低Java代碼的執行效率?

絕對不會!

一、import的按需導入

import java.util.*;

public class NeedImportTest {
public static void main(String[] args) {
ArrayList tList = new ArrayList();
}
}


編譯之后的class文件 :

//import java.util.*被替換成import java.util.ArrayList
//即按需導入編譯過程會替換成單類型導入。
import java.util.ArrayList;

public class NeedImportTest {
public static void main(String[] args) {
new ArrayList();
}
}

 

二、static import的按需導入

import static com.assignment.test.StaticFieldsClass.*;
public class StaticNeedImportTest {
public static void main(String[] args) {
System.out.println(staticField);
staticFunction();
}
}


上面StaticNeedImportTest 類編譯之后 :

//可以看出 : 
//1、static import的精准導入以及按需導入編譯之后都會變成import的單類型導入
import com.assignment.test.StaticFieldsClass;

public class StaticNeedImportTest {
public static void main(String[] args) {
//2、編譯之后“打回原形”,使用原來的方法調用靜態成員
System.out.println(StaticFieldsClass.staticField);
StaticFieldsClass.staticFunction();
}
}

附加

這是否意味着你總是可以使用按需導入聲明?
是,也不是!

在類似Demo的非正式開發中使用按需導入聲明顯得很有用。

然而,有這四個理由讓你可以放棄這種聲明:

編譯速度:在一個很大的項目中,它們會極大的影響編譯速度.但在小型項目中使用在編譯時間上可以忽略不計。
命名沖突:解決避免命名沖突問題的答案就是使用全名。而按需導入恰恰就是使用導入聲明初衷的否定。
說明問題:畢竟高級語言的代碼是給人看的,按需導入看不出使用到的具體類型。
無名包問題:如果在編譯單元的頂部沒有包聲明,Java編譯器首選會從無名包中搜索一個類型,然后才是按需類型聲明。如果有命名沖突就會產生問題。

Sun的工程師一般不使用按需類型導入聲明.這你可以在他們的代碼中找到:
在java.util.Properties類中的導入聲明:

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;

可以看到他們用單類型導入詳細的列出了需要的java.io包中的具體類型。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM