1. 程式人生 > >java的I/O學習記錄(以及一些其他知識點)

java的I/O學習記錄(以及一些其他知識點)

JAVA的I/O


1 I/O的基礎知識
    1.1 流
    Java程式通過流執行I/O。
    流是一種抽象,要麼產生資訊,要麼使用資訊。
    流通過java的I/O系統連結到物理裝置。
    所有流的行為方式都是相同的,儘管與他們連線的物理裝置是不同的。
    因此,可以為任意型別的裝置應用相同的I/O類和方法。
    這意味著可以將許多不同型別的輸入——磁碟檔案、鍵盤或網路socket抽象為輸入流。
    與之對應,輸出流可以引用控制檯、磁碟檔案或網路連線。
    流是處理輸入/輸出的一種清晰方式,例如,程式碼中的所有部分都不需要理解鍵盤和網路之間的區別。
    流是Java在由java.io包定義的類層次中實現的。
注意:除了在java.io包中定義的基於流的I/O外,Java還提供了基於緩衝和基於通道的I/O,
它們實在java.nio及其子包中定義的。


1.2 位元組流和字元流
·位元組流為處理位元組的輸入和輸出提供了方法。例如,當讀取和寫入二進位制資料時,使用的就
是位元組流。
·字元流為處理字元的輸入和輸出提供了方便的方法。它們使用Unicode編碼,所以可以被
國際化。
·在某些情況下,字元流比位元組流高效。
·在最底層,所有I/O仍然時面向位元組的。基於字元的流只是為處理字元提供了一種方便
和高效的方法。


1.2.1 位元組流類
·位元組流時通過兩個類層次定義的。在頂級時兩個抽象類:InputStream和OutputStream。
·每個抽象類都有幾個處理各種不同裝置的具體子類,例如,磁碟檔案、網路連線甚至記憶體緩衝區
·抽象類InputStream和OuputStream定義了其他流類實現的一些關鍵方法。其中最重要的兩個
方法是read()和write(),這兩個方法分別讀取和寫入位元組資料。
每個方法都有抽象形式,派生的流類必須重寫這兩個方法。


1.2.2 字元流類
·字元流是通過兩個類層次定義的。在頂層是兩個抽象類:Reader和Writer。這兩個抽象類處理
Unicode字元流。Java為這兩個類提供了幾個具體的子類。
·抽象類Reader和Writer定義了其他幾個流類實現的重要方法。最重要的兩個方法是read()和
write(),這兩個方法分別讀取和寫入字元資料。每個方法都有抽象形式,派生的流類必須實現
這兩個方法。


1.3 預定義流
·所有Java程式都自動匯入java.lang包。這個包定義了System類,該類封裝了執行時環境的
某些方法。
·System還包含3個預定義的流變數:in、out、err。這些變數在System類中被宣告為public、
static和final。這意味著,程式中的其他任何部分都可以使用他們,而不需要引用特定的
System物件。
····System.out引用標準的輸出流,預設情況下是控制檯。(PrintStream型別的物件)
····System.in引用標準的輸入流,預設情況下是鍵盤。(InputStream型別的物件)
····System.err引用標準的錯誤流,預設情況下也是控制檯。(PrintStream型別的物件)
但是這些流可以本重定向到任何相容的I/O裝置。
····這3個都是位元組流,儘管它們通常用於從控制檯讀取字元以及向控制檯寫入字元。可以看出
,如果願意的話,可以在基於字元的流中封裝這些流。


2 讀取控制檯輸入
 使用面向字元的流可以是程式更容易國際化和維護。
·BufferedReader支援緩衝的輸入流。其構造如下所示:
 BufferedReader(Reader inputReader)
·其中InputReader是與即將建立的BufferedReader例項連結的流。
·Reader是抽象類,InputStreamReader是它的一個具體子類,該類將位元組轉換成字元。
·為了獲取與System.in連結的InputStreamReader物件,使用下面的建構函式:
 BufferReader br=new BufferReader(new InputStreamReader(System.in));
 br就是通過System.in與控制檯連結的基於字元的流。
 
2.1 讀取字元 
為了從BufferReader物件讀取字元,需要使用read()方法。
int read() throws IOException
每次呼叫read()方法都會從輸入流讀取一個字元,並將之作為整形值返回。


2.2 讀取字串
·為了從鍵盤讀取字串,可以使用BufferedReader類的readLine()方法,該方法的一般形式如下:
String readLine() throws IOException


3 向控制檯寫輸出
·使用print()和println()是向控制檯寫輸出的最容易方式,這兩個方法是由PrintStream
類(也就是System.out引用的物件的型別)定義的。System.out儘管是位元組流,但它對於簡單的程式仍然可以使用。
·因為PrintStream是派生自OutputStream的輸出流,所以還實現了低階的write()方法。
以此可以使用write()向控制檯輸出。PrintStream定義的最簡單形式的write()方法如下所示:
void write(int byteval)
 該方法輸出由byteval指定的位元組。儘管byteval被宣告為整數,但是隻有低8位被輸出。
·通常不會使用write()執行控制檯輸出(儘管在某些情況下這麼做是有用的),因為print()
和println()確實更容易使用。


4 PrintWriter類
儘管使用System.out向控制檯輸出是可以接受的,但是最好將其用於除錯或用於示例程式。
對於實際的程式,使用Java向控制檯輸出的推薦方法是通過PrintWriter流。
··PrintWriter類定義了幾個建構函式,其中之一如下所示:
  PrintWriter(OutputStream outputStream,boolean flushingOn)
  其中,outputStream是OutputStream型別的物件,flushingOn控制Java是否在每次呼叫
  println()方法時重新整理輸出流。如果flushingOn為true,就自動重新整理;如果為false,那麼
  不會自動重新整理。
··PrintWriter支援print()和println()方法。因此,可以使用與System.out相同的方式使用
它們。如果引數不是簡單型別,PrintWriter方法會呼叫物件的toString()方法,然後輸出結果。
··為了使用PrintWriter向控制檯輸出,為輸出流指定System.out,然後在每個新行之後重新整理流。
例如:
PrintWriter pw =new PrintWriter(System.out,true);
上面的程式碼建立了一個連結到控制檯輸出的PrintWriter.
使用PrintWriter可以使實際的應用程式更容易國際化。


5 讀/寫檔案
··對於讀寫檔案,最常用的兩個流類時FileInputStream和FileOutputStream,這兩個類建立與
檔案連結的位元組流。
··為了開啟檔案,只需要簡單的建立這些類中某個類的物件,指定檔名作為建構函式的引數
即可。建構函式如下所示(其中的一種):
FileInputStream(String fileName) throws FileNotFoundException
FileInputStream(String fileName) throws FileNotFoundException
·其中fileName是指定希望開啟的檔案的名稱。
·當建立輸入流時,如果檔案不存在,就會丟擲FileNotFoundException異常。
·對於輸出流,如果不能開啟檔案或不能建立檔案,也會丟擲FileNotFoundException異常。
·FileNotFoundException是IOException的子類。當開啟輸出檔案時,先前存在的同名文
件將被銷燬。
注意:對於存在安全管理器的情況,如果在試圖開啟檔案時發生安全性違規,那麼檔案類
FileInputStream和FIleOutputStream會丟擲SecurityException異常。
預設情況下,通過java執行的應用程式不會使用安全管理器。


··檔案使用完之後必須關閉。關閉檔案時通過close()方法完成的,FileInputStream和FileOutputStream
都實現了該方法。該方法宣告如下:
void close() throws IOException
··關閉檔案會釋放為檔案分配的系統資源,從而允許其他檔案使用這些資源。
關閉檔案失敗會導致“記憶體洩漏”,因為未使用的資源沒有被釋放。
注意:從jdk7開始,close()方法是由java.lang包中的AutoCloseable介面指定的。java.io包
中的Closeable介面繼承了AutoCloseable介面。所有流類都實現了這兩介面,包括FileInputStream
和FileOutputStream。


··為了讀取檔案,可以使用在FileInputStream中定義的read()版本。如下:
int read() throws IOException
··每次呼叫read()方法時,都會從檔案讀取一個位元組,並作為整形值返回。當達到
檔案末尾時,read()方法返回-1。該方法可能丟擲IOException異常。
··為了向檔案中寫入內容,可以使用FileOutputStream定義的write()方法,該方法的最簡單
形式如下:
void write(int byteval) throws IOException


6 自動關閉檔案
自動資源管理基於try語句的擴充套件形式,它的一般形式如下所示:
try(resource-specification){
//use the resource
}
try語句中宣告的資源被隱式宣告為final。這意味著,在建立資源變數後,
不能將其他資源賦給該變數。資源的作用域侷限於帶資源的try語句。


其中,resource-specification是用來宣告和初始化資源(例如檔案流)的語句。
該語句包含一個變數宣告,在該變數宣告中使用將被管理的物件引用初始化變數。
當try程式碼塊結束時,自動釋放資源。對於檔案,這意味著會自動關閉檔案(
因此,不需要顯示地呼叫close()方法)
···只有對於哪些實現了AutoCloseable介面地資源,才能使用帶資源地try語句。
AutoCloseable介面由java.lang包定義,該介面定義了close()方法。java.io包中的Closeable
介面繼承自AutoCloseable介面。所有流類都實現了這兩個介面。因此,當使用流時——包括檔案流
,可以使用帶資源的try語句。


7 applet(不用看)
8 transient和volatile修飾符
··如果將例項變數宣告 為transient,那麼當儲存物件時,例項變數的值將不需要永久儲存。
clas T{
  transient int a;//will not persist
  int b;//will persist
}
··修飾符volatile告訴編譯器,由volatile修飾的變數可以被程式的其他部分隨意修改。(
多執行緒)


9 使用instanceof運算子
運算子instanceof的一般形式如下: objref instanceof type


10 strictfp
通過使用strictfp修飾類、方法或介面,可以確保採用與Java以前版本使用的相同方式
執行浮點計算(以及所有截斷)。如果使用strictfp對類進行了修飾,那麼類中的所有方法也將自動使用stritfp進行修飾。
strictfp class MyClass{//...
//坦白地說,大多數程式設計師永遠不需要使用strictfp隻影響很少地一類問題。


11 本地方法
native用於宣告原生代碼方法。
public native int meth();
將c程式碼整合到java程式中的機制稱為Java本地介面(Java Native Interface,JNI)


javah -jni NativeDemo(這個命令將生成檔名為NativeDemo.h的標頭檔案)


本地方法存在的問題
1、潛在的安全性風險 因為本地方法執行實際的機器程式碼,所以可以訪問宿主系統的
所有部分。也就是說,原生代碼可以離開Java執行環境。這有可能導致感染病毒。
2、可以執行丟失 原生代碼因為包含在DLL中,所以必須存在於執行Java程式的機器上。
而且,因為使用本地方法依賴於CPU和作業系統,所以DLL本身時不可移植的。因此,使用本地
方法的Java程式只能運行於已經安裝了相容DLL的機器上。


13 使用assert
assert 是用在開發期間建立斷言,斷言是在程式執行期間應當為true的條件。
斷言經常用於證實在測試期間實際遇到了某些期望的條件。在釋出程式碼中通常不使用它們。
assert關鍵字有兩種形式:
··第一種:assert condition;
其中,condition是一個求值結果必須為布林型的表示式。如果結果為true,那麼斷言為真,
不會發生其他動作。如果條件為false,那麼斷言失敗並丟擲預設的AssertionError物件。
··第二種:assert condition:expr;
expr是傳遞給AssertionError建構函式的值。這個值被轉換成相應的字串格式,並且如果
斷言失敗,將會顯示該字串。典型地,將為expr指定一個字串,不過也可以指定任何非
void表示式,只要定義了合理地字串轉換即可。


·啟用和禁用斷言
-ea 啟用斷言
-da 禁用所有斷言。
通過在-ea或-da選項後面指定包的名稱,並在後面跟上三個點,可以啟用或禁用指定包(及其
所有子包)中地斷言。例如:-ea:MyPack...(啟用MyPack包中的斷言)
 -da:MyPack...(禁用MyPack包中的斷言)
還可以為-ea或-da選項具體指定某個類。例如:
-ea:AssertDemo(啟用AssertDemo類中的斷言)


13 靜態匯入
靜態匯入擴充套件了import關鍵字的功能。
通過在import後面新增static關鍵字,可以使用import語句匯入類或介面的靜態成員。
靜態匯入語句有兩種形式。
第一種形式:引入單個名稱:import static 完整包名.成員名稱;
第二種形式:匯入給定類或介面的所有靜態成員,如下所示:
import static java.lang.Math.*;


14 通過this()呼叫過載的建構函式
this()最適合用於包含大量初始化程式碼的建構函式,而不適合用於那些只簡單設定少量域
變數值的建構函式。
限制:1、在呼叫this()時不能使用當前類的任何例項變數。
      2、在同一個建構函式中不能同時使用super()和this(),因為它們都必須是構造
      函式中第一條語句。


15 緊湊API配置檔案
jdk8新增了一項功能,將API庫的子集組織成所謂的緊湊配置檔案。它們分別叫做compact1、
compact2、compact3.每個配置檔案包含的一個子集。此外,compact2包含整個compact1,
compact3包含整個compact2.
因此,每個配置檔案以前一個配置檔案為基礎。緊湊配置檔案的優勢在於,用不到完整庫的應用
不需要下載整個庫。


編譯程式時,使用-profile選項可以確定程式是否使用了緊湊配置檔案中定義的API。
javac -profile profileName programName
其中,profileName指定了配置檔案,必須是compact1、compact2或compact3
例如:
javac -profile compact2 Test.java
這裡指定了compact2配置檔案。如果Test.java包含不屬於compact2的API,就會引發
編譯錯誤。