IO流
- File類的使用
-
- File類
- 常用構造器
- 路徑分隔符
- 常用方法
-
- File類的擷取功能
- File類的重命名功能
- File類的判斷功能
- File類的建立功能
- File類的删除功能
- IO流原理及流的分類
-
- Java IO原理
- 流的分類
- IO 流體系
- 節點流和處理流
- InputStream & Reader
-
- InputStream
- Reader
- OutputStream & Writer
-
- OutputStream
- Writer
- 節點流(或檔案流)
-
- 讀取檔案
- 寫入檔案
- 緩沖流
- 轉換流
-
- InputStreamReader
- OutputStreamWriter
- 标準輸入、輸出流
- 列印流
- 資料流
- 對象流
-
- 對象的序列化
- 使用對象流序列化對象
- 随機存取檔案流
File類的使用
File類
- File類的一個對象,代表一個檔案或一個檔案目錄(俗稱:檔案夾)
- File類聲明在java.io包下
- File 能建立、删除、重命名檔案和目錄,但 File 不能通路檔案内容本身。 如果需要通路檔案内容本身,則需要使用輸入/輸出流。
- 後續File類的對象常會作為參數傳遞到流的構造器中,指明讀取或寫入的"終點"
常用構造器
絕對路徑:是一個固定的路徑,從盤符開始
相對路徑:是相對于某個位置開始
- public File(String pathname)
- 以pathname為路徑建立File對象,可以是絕對路徑或者相對路徑,如果 pathname是相對路徑,則預設的目前路徑在系統屬性user.dir中存儲。
File file1 = new File("hello.txt");//相對于目前module
File file2 = new File("D:\\IO\\hi.txt");
- public File(String parent,String child)
- 以parent為父路徑,child為子路徑建立File對象。
- public File(File parent,String child)
- 根據一個父File對象和子檔案路徑建立File對象
File file = new File("D:\\IO\\hi.txt");
File file1 = new File(file,"hi.txt");
路徑分隔符
路徑中的每級目錄之間用一個路徑分隔符隔開。
路徑分隔符和系統有關windows和DOS系統預設使用“\”來表示
UNIX和URL使用“/”來表示
Java程式支援跨平台運作,File類提供了一個常量public static final String separator。可根據作業系統,動态的提供分隔符。
常用方法
File類的擷取功能
- public String getAbsolutePath():擷取絕對路徑
- public String getPath() :擷取路徑
- public String getName() :擷取名稱
- public String getParent():擷取上層檔案目錄路徑。若無,傳回null
- public long length() :擷取檔案長度(即:位元組數)。不能擷取目錄的長度。
- public long lastModified() :擷取最後一次的修改時間,毫秒值
@Test
public void test(){
File file1 = new File("hello.txt");
File file2 = new File("D:\\IO\\hi.txt");
System.out.println(file1.getAbsolutePath());
System.out.println(file1.getPath());
System.out.println(file1.getName());
System.out.println(file1.getParent());
System.out.println(file1.length());
System.out.println(new Date(file1.lastModified()));
System.out.println();
System.out.println(file2.getAbsolutePath());
System.out.println(file2.getPath());
System.out.println(file2.getName());
System.out.println(file2.getParent());
System.out.println(file2.length());
System.out.println(file2.lastModified());
}
- public String[] list() :擷取指定目錄下的所有檔案或者檔案目錄的名稱數組
- public File[] listFiles() :擷取指定目錄下的所有檔案或者檔案目錄的File數組
@Test
public void test3(){
File file = new File("D:\\IO");
String[] list = file.list();
for(String s : list){
System.out.println(s);
}
System.out.println();
File[] files = file.listFiles();
for(File f : files){
System.out.println(f);
}
File類的重命名功能
- public boolean renameTo(File dest):把檔案重命名為指定的檔案路徑
//以file1.renameTo(file2)為例
// 要想保證傳回true,需要file1在硬碟中是存在的,且file2不能在硬碟中存在
@Test
public void test4(){
File file1 = new File("hello.txt");
File file2 = new File("D:\\IO\\hi.txt");
boolean renameTo = file2.renameTo(file1);
System.out.println(renameTo);
}
File類的判斷功能
- public boolean isDirectory():判斷是否是檔案目錄
- public boolean isFile() :判斷是否是檔案
- public boolean exists() :判斷是否存在
- public boolean canRead() :判斷是否可讀
- public boolean canWrite() :判斷是否可寫
- public boolean isHidden() :判斷是否隐藏
File類的建立功能
- public boolean createNewFile() :建立檔案。若檔案存在,則不建立,傳回false
- public boolean mkdir() :建立檔案目錄。如果此檔案目錄存在,就不建立了。 如果此檔案目錄的上層目錄不存在,也不建立。
- public boolean mkdirs() :建立檔案目錄。如果上層檔案目錄不存在,一并建立
注意事項:如果你建立檔案或者檔案目錄沒有寫盤符路徑,那麼,預設在項目 路徑下。
File類的删除功能
-
public boolean delete():删除檔案或者檔案夾
删除注意事項: Java中的删除不走資源回收筒。 要删除一個檔案目錄,請注意該檔案目錄内不能包含檔案或者檔案目錄
IO流原理及流的分類
Java IO原理
I/O是Input/Output的縮寫, I/O技術是非常實用的技術,用于處理裝置之間的資料傳輸。如讀/寫檔案,網絡通訊等。
Java程式中,對于資料的輸入/輸出操作以“流(stream)” 的 方式進行。
java.io包下提供了各種“流”類和接口,用以擷取不同種類的資料,并通過标準的方法輸入或輸出資料。
輸入input:讀取外部資料(磁 盤、CD光牒等儲存設備的資料)到程式(記憶體)中。
輸出output:将程式(記憶體) 資料輸出到磁盤、CD光牒等儲存設備中。
流的分類
- 按操作資料機關不同分為:位元組流(8 bit),字元流(16 bit)
- 按資料流的流向不同分為:輸入流,輸出流
- 按流的角色的不同分為:節點流,處理流
- Java的IO流共涉及40多個類,實際上非正常則,都是從如上4個 抽象基類派生的。
- 由這四個類派生出來的子類名稱都是以其父類名作為子類名字尾。
IO 流體系
節點流和處理流
- 節點流:直接從資料源或目的地讀寫資料
- 處理流:不直接連接配接到資料源或目的地,而是“連接配接”在已存在的流(節點流或處理流)之上,通過對資料的處理為程式提供更為強大的讀寫功能。
InputStream & Reader
- InputStream 和 Reader 是所有輸入流的基類。
- 程式中打開的檔案 IO 資源不屬于記憶體裡的資源,垃圾回收機制無法回收該資源,是以應該顯式關閉檔案 IO 資源。
- FileInputStream 從檔案系統中的某個檔案中獲得輸入位元組。FileInputStream 用于讀取非文本資料之類的原始位元組流。要讀取字元流,需要使用 FileReader
InputStream
- int read()
- 從輸入流中讀取資料的下一個位元組。傳回 0 到 255 範圍内的 int 位元組值。如果因 為已經到達流末尾而沒有可用的位元組,則傳回值 -1。
- int read(byte[] b)
- 從此輸入流中将最多 b.length 個位元組的資料讀入一個 byte 數組中。如果因為已 經到達流末尾而沒有可用的位元組,則傳回值 -1。否則以整數形式傳回實際讀取的位元組數。
- int read(byte[] b, int off,int len)
- 将輸入流中最多 len 個資料位元組讀入 byte 數組。嘗試讀取 len 個位元組,但讀取的位元組也可能小于該值。以整數形式傳回實際讀取的位元組數。如果因為流位于檔案末尾而沒有可用的位元組,則傳回值 -1。
- public void close() throws IOException
- 關閉此輸入流并釋放與該流關聯的所有系統資源。
Reader
- int read()
- 讀取單個字元。作為整數讀取的字元,範圍在 0 到 65535 之間 (0x00-0xffff)(2個 位元組的Unicode碼),如果已到達流的末尾,則傳回 -1
- int read(char[] cbuf)
- 将字元讀入數組。如果已到達流的末尾,則傳回 -1。否則傳回本次讀取的字元數。
- int read(char[] cbuf,int off,int len)
- 将字元讀入數組的某一部分。存到數組cbuf中,從off處開始存儲,最多讀len個字元。如果已到達流的末尾,則傳回 -1。否則傳回本次讀取的字元數。
- public void close() throws IOException
- 關閉此輸入流并釋放與該流關聯的所有系統資源。
@Test
public void testFileReader() {
FileReader fr = null;
try {
//1.執行個體化File類的對象,指明要操作的檔案
File file = new File("hello.txt");//相較于目前Module
//2.提供具體的流
fr = new FileReader(file);
//3.資料的讀入
//read():傳回讀入的一個字元。如果達到檔案末尾,傳回-1
char[] ch = new char[10];
int len;
while((len = fr.read(ch)) != -1){
System.out.println(new String(ch,0,len));;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.流的關閉操作
try {
if(fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
OutputStream & Writer
- 因為字元流直接以字元作為操作機關,是以 Writer 可以用字元串來替換字元數組, 即以 String 對象作為參數
- FileOutputStream 從檔案系統中的某個檔案中獲得輸出位元組。FileOutputStream 用于寫出非文本資料之類的原始位元組流。要寫出字元流,需要使用 FileWriter
OutputStream
- void write(int b)
- 将指定的位元組寫入此輸出流。write 的正常協定是:向輸出流寫入一個位元組。要寫 入的位元組是參數 b 的八個低位。b 的 24 個高位将被忽略。 即寫入0~255範圍的。
- void write(byte[] b)
- 将 b.length 個位元組從指定的 byte 數組寫入此輸出流。write(b) 的正常協定是:應該 與調用 write(b, 0, b.length) 的效果完全相同。
- void write(byte[] b,int off,int len)
- 将指定 byte 數組中從偏移量 off 開始的 len 個位元組寫入此輸出流。
- public void flush()throws IOException
- 重新整理此輸出流并強制寫出所有緩沖的輸出位元組,調用此方法訓示應将這些位元組立 即寫入它們預期的目标。
- public void close() throws IOException
- 關閉此輸出流并釋放與該流關聯的所有系統資源。
Writer
- void write(int c)
- 寫入單個字元。要寫入的字元包含在給定整數值的 16 個低位中,16 高位被忽略。 即 寫入0 到 65535 之間的Unicode碼。
- void write(char[] cbuf)
- 寫入字元數組。
- void write(char[] cbuf,int off,int len)
- 寫入字元數組的某一部分。從off開始,寫入len個字元
- void write(String str)
- 寫入字元串。
- void write(String str,int off,int len)
- 寫入字元串的某一部分。
- void flush()
- 重新整理該流的緩沖,則立即将它們寫入預期目标。
- public void close() throws IOException
- 關閉此輸出流并釋放與該流關聯的所有系統資源。
//從記憶體中寫出資料到硬碟的檔案裡
//1. 輸出操作,對應的File可以不存在的。并不會報異常
// 2.File對應的硬碟中的檔案如果不存在,在輸出的過程中,會自動建立此檔案。File對應的硬碟中的檔案如果存在:
//如果流使用的構造器是:FileWriter(file,false) / FileWriter(file):對原有檔案的覆寫
//如果流使用的構造器是:FileWriter(file,true):不會對原有檔案覆寫,而是在原有檔案基礎上追加内容
@Test
public void testFileWriter() {
FileWriter fw = null;
try {
//1.提供File類的對象,指明寫出到的檔案
File file = new File("hello1.txt");
//2.提供FileWriter的對象,用于資料的寫出
fw = new FileWriter(file,false);
//3.寫出的操作
fw.write("I have a dream!\n");
fw.write("We have a dream !");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.流資源的關閉
try {
if(fw != null)
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
- 複制文本
@Test
public void testFileReaderFileWriter(){
FileReader fr = null;
FileWriter fw = null;
try {
//1.建立File類的對象,指明讀入和寫出的檔案
File srcFile = new File("rw.next");
File destFile = new File("rw1");
//2.建立輸入流和輸出流的對象
fr = new FileReader(srcFile);
fw = new FileWriter(destFile);
//3.資料的讀入和寫出操作
char[] cbuf = new char[5];
int len;//記錄每次讀入到cbuf中的字元的個數
while((len = fr.read(cbuf)) != -1){
//每次寫出len個字元
fw.write(cbuf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.關閉流資源
try {
if(fw != null)
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
節點流(或檔案流)
讀取檔案
寫入檔案
- 定義檔案路徑時,注意:可以用“/”或者“\”。
- 在寫入一個檔案時,如果使用構造器FileOutputStream(file),則目錄下有同名檔案将被覆寫。
- 如果使用構造器FileOutputStream(file,true),則目錄下的同名檔案不會被覆寫, 在檔案内容末尾追加内容。
- 在讀取檔案時,必須保證該檔案已存在,否則報異常。
- 位元組流操作位元組,比如:.mp3,.avi,.rmvb,mp4,.jpg,.doc,.ppt
- 字元流操作字元,隻能操作普通文本檔案。最常見的文本檔案:.txt,.java,.c,.cpp 等語言的源代碼。尤其注意.doc,excel,ppt這些不是文本檔案。
緩沖流
為了提高資料讀寫的速度,Java API提供了帶緩沖功能的流類,在使用這些流類時,會建立一個内部緩沖區數組,預設使用8192個位元組(8Kb)的緩沖區。
緩沖流要“套接”在相應的節點流之上,根據資料操作機關可以把緩沖流分為:
BufferedInputStream(read(byte[] buffer) 和 BufferedOutputStream (write(byte[] buffer,0,len) / flush())
BufferedReader(read(char[] cbuf) / readline()) 和 BufferedWriter(write(char[] cbuf,0,len) / flush())
- 當讀取資料時,資料按塊讀入緩沖區,其後的讀操作則直接通路緩沖區 。
- 當使用BufferedInputStream讀取位元組檔案時,BufferedInputStream會一次性從 檔案中讀取8192個(8Kb),存在緩沖區中,直到緩沖區裝滿了,才重新從檔案中 讀取下一個8192個位元組數組。
- 向流中寫入位元組時,不會直接寫到檔案,先寫到緩沖區中直到緩沖區寫滿, BufferedOutputStream才會把緩沖區中的資料一次性寫到檔案裡。使用方法 flush()可以強制将緩沖區的内容全部寫入輸出流。
- 關閉流的順序和打開流的順序相反。隻要關閉最外層流即可,關閉最外層流也 會相應關閉内層節點流 。
- flush()方法的使用:手動将buffer中内容寫入檔案 。
//實作非文本檔案的複制
@Test
public void BufferedStreamTest(){
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1.造檔案
File srcFile = new File("圖檔.jpg");//參數填想要複制的非文本檔案的名稱
File destFile = new File("圖檔1.jpg");
//2.造流
//節點流
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
//緩沖流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3.複制的細節:讀取、寫入
byte[] buffer = new byte[10];
int len;
while ((len = bis.read(buffer)) != -1){
bos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.資源關閉
//要求:先關閉外層的流,再關閉内層的流
//關閉外層流的同時,内層流也會自動的進行關閉。關于内層流的關閉,我們可以省略.
if(bos != null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bis != null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
轉換流
轉換流提供了在位元組流和字元流之間的轉換
位元組流中的資料都是字元時,轉成字元流操作更高效。
轉換流屬于字元流
InputStreamReader
實作将位元組的輸入流按指定字元集轉換為字元的輸入流。 \
- 構造器·
- public InputStreamReader(InputStream in)
- public InputSreamReader(InputStream in,String charsetName)
//nputStreamReader的使用,實作位元組的輸入流到字元的輸入流的轉換
@Test
public void test(){
InputStreamReader isr = null;
try {
FileInputStream fis = new FileInputStream("abcd.txt");
//InputStreamReader isr = new InputStreamReader(fis);//使用預設的字元集
//參數二指明了字元集。字元集取決于檔案abcd.txt儲存時使用的字元集
isr = new InputStreamReader(fis,"utf-8");
char[] cbuf = new char[1024];
int len;
while ((len = isr.read(cbuf)) != -1){
String str = new String (cbuf,0,len);
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(isr != null){
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
OutputStreamWriter
實作将字元的輸出流按指定字元集轉換為位元組的輸出流。
- 構造器
- public OutputStreamWriter(OutputStream out)
- public OutputSreamWriter(OutputStream out,String charsetName)
@Test
public void test1(){
InputStreamReader isr = null;
OutputStreamWriter osw = null;
try {
//1.造檔案、造流
File file1 = new File("bcde.txt");
File file2 = new File("bcde_gbk.txt");
FileInputStream fis = new FileInputStream(file1);
FileOutputStream fos = new FileOutputStream(file2);
isr = new InputStreamReader(fis,"utf-8");
osw = new OutputStreamWriter(fos,"gbk");
//2.讀寫過程
char[] cbuf = new char[20];
int len;
while((len = isr.read(cbuf)) != -1){
osw.write(cbuf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.關閉資源
if(isr != null){
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(osw !=null){
try {
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
标準輸入、輸出流
System.in:标準的輸入流,預設從鍵盤輸入
System.out:标準的輸出流,預設從控制台輸出
System類的setIn(InputStream is) / setOut(PrintStream ps)方式重新指定輸入和輸出的流。
列印流
實作将基本資料類型的資料格式轉化為字元串輸出
列印流:PrintStream和PrintWriter
- 提供了一系列重載的print()和println()方法,用于多種資料類型的輸出
- PrintStream和PrintWriter的輸出不會抛出IOException異常
- PrintStream和PrintWriter有自動flush功能
- PrintStream 列印的所有字元都使用平台的預設字元編碼轉換為位元組。
- 在需要寫入字元而不是寫入位元組的情況下,應該使用 PrintWriter 類。
- System.out傳回的是PrintStream的執行個體
資料流
DataInputStream 和 DataOutputStream
作用:用于讀取或寫出基本資料類型的變量或字元串
對象流
ObjectInputStream和OjbectOutputSteam
用于存儲和讀取基本資料類型資料或對象的處理流。它的強大之處就是可 以把Java中的對象寫入到資料源中,也能把對象從資料源中還原回來。
序列化:用ObjectOutputStream類儲存基本類型資料或對象的機制
反序列化:用ObjectInputStream類讀取基本類型資料或對象的機制
ObjectOutputStream和ObjectInputStream不能序列化static和transient修飾的成員變量
對象的序列化
- 對象序列化機制允許把記憶體中的Java對象轉換成平台無關的二進制流,從 而允許把這種二進制流持久地儲存在磁盤上,或通過網絡将這種二進制流傳 輸到另一個網絡節點。當其它程式擷取了這種二進制流,就可以恢複成原來的Java對象
- 序列化的好處在于可将任何實作了Serializable接口的對象轉化為位元組資料, 使其在儲存和傳輸時可被還原
作用:用于存儲和讀取基本資料類型資料或對象的處理流。它的強大之處就是可以把Java中的對象寫入到資料源中,也能把對象從資料源中還原回來。
要想一個java對象是可序列化的,需要滿足相應的要求:
- 需要實作接口:Serializable
- 目前類提供一個全局常量:serialVersionUID
- 除了目前Person類需要實作Serializable接口之外,還必須保證其内部所有屬性也必須是可序列化的。(預設情況下,基本資料類型可序列化)
- ObjectOutputStream和ObjectInputStream不能序列化static和transient修飾的成員變量
凡是實作Serializable接口的類都有一個表示序列化版本辨別符的靜态變量: private static final long serialVersionUID;
serialVersionUID用來表明類的不同版本間的相容性。簡言之,其目的是以序列化對象 進行版本控制,有關各版本反序列化時是否相容。
如果類沒有顯示定義這個靜态常量,它的值是Java運作時環境根據類的内部細節自 動生成的。若類的執行個體變量做了修改,serialVersionUID 可能發生變化。故建議, 顯式聲明。
使用對象流序列化對象
若某個類實作了 Serializable 接口,該類的對象就是可序列化的:
建立一個 ObjectOutputStream
調用 ObjectOutputStream 對象的 writeObject(對象) 方法輸出可序列化對象
注意寫出一次,操作flush()一次
反序列化
建立一個 ObjectInputStream
調用 readObject() 方法讀取流中的對象
強調:如果某個類的屬性不是基本資料類型或 String 類型,而是另一個 引用類型,那麼這個引用類型必須是可序列化的,否則擁有該類型的 Field 的類也不能序列化
序列化機制:
對象序列化機制允許把記憶體中的Java對象轉換成平台無關的二進制流,進而允許把這種二進制流持久地儲存在磁盤上,或通過網絡将這種二進制流傳輸到另一個網絡節點。當其它程式擷取了這種二進制流,就可以恢複成原來的Java對象。
随機存取檔案流
RandomAccessFile類
- RandomAccessFile 聲明在java.io包下,但直接繼承于java.lang.Object類。并 且它實作了DataInput、DataOutput這兩個接口,也就意味着這個類既可以讀也可以寫。
- RandomAccessFile 類支援 “随機通路” 的方式,程式可以直接跳到檔案的任意地方來讀、寫檔案 (支援隻通路檔案的部分内容 ;可以向已存在的檔案後追加内容 )
RandomAccessFile 對象包含一個記錄指針,用以标示目前讀寫處的位置。 RandomAccessFile 類對象可以自由移動記錄指針:
long getFilePointer():擷取檔案記錄指針的目前位置
void seek(long pos):将檔案記錄指針定位到 pos 位置
構造器
public RandomAccessFile(File file, String mode)
public RandomAccessFile(String name, String mode)
建立 RandomAccessFile 類執行個體需要指定一個 mode 參數,該參數指定 RandomAccessFile 的通路模式:
r: 以隻讀方式打開
rw:打開以便讀取和寫入
rwd:打開以便讀取和寫入;同步檔案内容的更新
rws:打開以便讀取和寫入;同步檔案内容和中繼資料的更新
如果RandomAccessFile作為輸出流時,寫出到的檔案如果不存在,則在執行過程中自動建立。
如果寫出到的檔案存在,則會對原有檔案内容進行覆寫。(預設情況下,從頭覆寫)