1. 程式人生 > >輸入/輸出(一)

輸入/輸出(一)

File類

訪問檔案和目錄

不管是檔案還是目錄都是使用File來操作的,File能新建、刪除、重新命名檔案和目錄,File不能訪問檔案內容本身。如果需要訪問檔案內容本身,則需要使用輸入/輸出流。
File類提供了很多方法來操作檔案和目錄:
在這裡插入圖片描述
在這裡插入圖片描述

流的分類

1. 輸入流和輸出流

  • 輸入流:只能從中讀取資料,而不能向其寫入資料;
  • 輸出流:只能向其寫入資料,而不能從中讀取資料;

Java的輸入流主要由InputStream和Reader作為基類,而輸出流則主要由OuputStream和Writer作為基類。它們都是一些抽象基類,無法直接建立例項。
2. 位元組流和字元流

  • 位元組流:操作的資料單元是8位的位元組;
  • 字元流:操作的資料單元是16位的字元;

位元組流主要由InputStream和OuputStream作為基類,而字元流主要由Reader和Writer作為基類。
3. 節點流和處理流

  • 節點流:可以從/向一個特定的IO裝置(如磁碟、網路)讀/寫資料的流,節點流也被稱為低階流;
  • 處理流:用於對一個已存在的流進行連線或封裝,通過封裝後的流來實現資料讀/寫功能,處理流也被稱為高階流;

在這裡插入圖片描述
當使用處理流進行輸入/輸出時,程式並不會直接連線到實際的資料來源,沒有和實際的輸入/輸出節點連線。

流的概念模型

Java的IO流40多個類都是從如下4個抽象基類派生的:

  • InputStream/Reader:所有輸入流的基類,前者是位元組輸入流,後者是字元輸入流;
  • OuputStream/Writer:所有輸出流的基類,前者是位元組輸出流,後者是字元輸出流;

對於InputStream和Reader而言,它們把輸入裝置抽象成一個水管,這個水管裡的每個水滴依次排列:
在這裡插入圖片描述
位元組流和字元流的處理方式其實非常相似,只是它們處理的輸入/輸出單位不同而已。輸入流使用隱式的記錄指標來表示當前正準備從哪個“水滴”開始讀取,每當程式從InputStream/Reader裡取出一個或多個“水滴”後,記錄指標自動向後移動。除此之外InputStream和Reader裡都提供一些方法來控制記錄指標的移動。

對於OuputStream和Writer而言,它們同樣把輸出裝置抽象成一個水管,只是這個水管裡沒有任何水滴:
在這裡插入圖片描述
當執行輸出時,程式相當於依次把水滴放入到輸出流的水管中。輸出流同樣採用隱式的記錄指標來標識當前水滴即將放入的位置,每當程式OuputStream和Writer裡輸出一個或多個水滴後,記錄指標自動向後移動。

處理流可以嫁接在任何已存在的流的基礎之上,這就允許Java應用程式採用相同的程式碼、透明的方式來訪問不同的輸入/輸出裝置的資料流:
在這裡插入圖片描述

InputStream和Reader

InputStream和Reader是所有輸入流的抽象基類,本身不能建立例項來執行輸入,但它們將成為所有輸入流的模板:
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
FileInputStreamTest.java

public class FileInputStreamTest
{
	public static void main(String[] args) throws IOException
	{
		//建立位元組輸入流
		FileInputStream fis = new FileInputStream("FileInputStreamTest.java");
		//建立一個長度為1024的“竹筒”
		byte[] bbuf = new byte[1024];
		//用於儲存實際讀取的位元組數
		int hasRead = 0;
		//使用迴圈來重複“取水”過程
		while ((hasRead = fis.read(bbuf)) > 0 )
		{
			//取出“竹筒”中水滴(位元組),將位元組陣列轉換成字串輸入!
			System.out.print(new String(bbuf , 0 , hasRead ));
		}
		fis.close();
	}
}

控制檯輸出:
在這裡插入圖片描述
FileReaderTest.java

            //建立字元輸入流
			FileReader fr = new FileReader("FileReaderTest.java");
			//建立一個長度為32的“竹筒”
			char[] cbuf = new char[32];

與前面FileInputStreamTest沒有什麼太大區別;
在這裡插入圖片描述

OuputStream和Writer

在這裡插入圖片描述
FileOutputStreamTest.java

public class FileOutputStreamTest
{
	public static void main(String[] args) throws IOException
	{
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try
		{
			//建立位元組輸入流
			fis = new FileInputStream("FileOutputStreamTest.java");
			//建立位元組輸入流
			fos = new FileOutputStream("newFile.txt");
			byte[] bbuf = new byte[32];
			int hasRead = 0;
			//迴圈從輸入流中取出資料
			while ((hasRead = fis.read(bbuf)) > 0 )
			{
				//每讀取一次,即寫入檔案輸出流,讀了多少,就寫多少。
				fos.write(bbuf , 0 , hasRead);
			}
		}
		catch (IOException ioe)
		{
			ioe.printStackTrace();
		}		
	}
}

執行,將看到系統當前路徑下多了一個檔案:newFile.txt。內容與FileOutputStreamTest.java一樣;
FileWriterTest.java

public class FileWriterTest
{
	public static void main(String[] args) throws IOException
	{
		//建立字元輸出流
		try(FileWriter fw = new FileWriter("poem.txt"))  //帶資源的try,java會對try塊引數中宣告的物件自動呼叫close()
		{						
			fw.write("錦瑟 - 李商隱\r\n"); 
			fw.write("錦瑟無端五十弦,一弦一柱思華年。\r\n");  //\r\n這是Windows平臺的換行符
			fw.write("莊生曉夢迷蝴蝶,望帝春心託杜鵑。\r\n");
			fw.write("滄海月明珠有淚,藍田日暖玉生煙。\r\n");
			fw.write("此情可待成追憶,只是當時已惘然。\r\n");
		}
		catch (IOException ioe)
		{
			ioe.printStackTrace();
		}		
	}
}

處理流的用法

使用處理流的典型思路是,使用處理流來包裝節點流,程式通過處理流來執行輸入/輸出功能,讓節點流與底層的I/O裝置、檔案互動。
實際識別處理流非常簡單,只要流的構造器引數不是一個物理節點,而是已經存在的流,那麼這種流就一定是處理流;而所有節點流都是直接以物理IO節點作為構造器引數的。
PrintStreamTest.java

public class PrintStreamTest
{
	public static void main(String[] args)throws IOException
	{
		
		try(FileOutputStream fos = new FileOutputStream("test.txt");//建立一個節點輸出流:FileOutputStream
				PrintStream ps = new PrintStream(fos))//以PrintStream來包裝FileOutputStream輸出流
		{											
			//使用PrintStream執行輸出
			ps.println("普通字串");
			ps.println(new PrintStreamTest());
		}
		catch (IOException ioe)
		{
			ioe.printStackTrace();
		}		
	}
}

程式使用處理流非常簡單,通常只需要在建立處理流時傳入一個節點流作為構造器引數即可;

輸入/輸出流體系

在這裡插入圖片描述
如果進行輸入/輸出的內容是文字內容,則應該考慮使用字元流;如果進行輸入/輸出的內容是二進位制內容,則應該考慮使用節點流;

轉換流

輸入/輸出流體系中還提供了兩個轉換流,這兩個轉換流用於實現將位元組流轉換成字元流:

  • InputStreamReader:將位元組輸入流轉換為字元輸入流;
  • OutputStreamWriter:將位元組輸出流轉換為字元輸出流;

為什麼沒有把字元流轉換成位元組流的轉換流呢?
因為字元流比位元組流操作更加方便,所以如果有一個流已經是字元流了,是一個用起來更加方便的流了,那麼為什麼要轉換成位元組流呢?
KeyinTest.java

public class KeyinTest
{
	public static void main(String[] args) 
	{
		
		try(InputStreamReader reader = new InputStreamReader(System.in);//將Sytem.in物件轉換成Reader物件	
				BufferedReader br = new BufferedReader(reader);	)//將普通Reader包裝成BufferedReader	
		//BufferedReader流具有緩衝功能,它可以一次讀取一行文字----以換行符為標誌,如果沒有讀到換行符,則程式阻塞,等到讀到換行符為止		
		{											
			String buffer = null;
			//採用迴圈方式來一行一行的讀取
			while ((buffer = br.readLine()) != null)
			{
				//如果讀取的字串為"exit",程式退出
				if (buffer.equals("exit"))
				{
					System.exit(1);
				}
				//列印讀取的內容
				System.out.println("輸入內容為:" + buffer);
			}
		}
		catch (IOException ioe)
		{
			ioe.printStackTrace();
		}		
	}
}

在這裡插入圖片描述
以上只是學習所做的筆記,不做任何用途(其實就是照著書抄啦)!!!
書籍:瘋狂Java講義