1. 程式人生 > >JavaSE 學習 —— 淺談 IO

JavaSE 學習 —— 淺談 IO

一、IO流概述

  IO 流簡單來說就是 Input 和 Output 流,IO 流主要是用來處理裝置之間的資料傳輸,Java 對於資料的操作都是通過流實現的,而Java用於操作流的物件都在 IO 包中。

分類

  • 按操作資料分為:位元組流和字元流。如:Reader 和 InputStream
  • 按流向分為:輸入流和輸出流。如:InputStream 和 OutputStream

IO 流常用的基類: InputStream , OutputStream

字元流的抽象基類:Reader,Writer

二、流的概念

  程式通過流來完成輸入/輸出。流是生產或消費資訊的抽象,流通過輸入輸出與物理裝置連結,儘管與它們連結的物理裝置不盡相同,所有流的行為具有相同的方式。這樣就意味一個輸入流能夠抽象多種不同型別的輸入:從磁碟檔案、從鍵盤或從網路套接字;同樣,一個輸出流可以輸出到控制檯、磁碟檔案或相連的網路。

三、字元流

簡介

  1) 字元流中的物件融合了編碼表,也就是系統預設的編碼表,我們的系統一般是GBK編碼。
  2) 字元流只用來處理文字資料,位元組流用來處理媒體資料。
  3) 資料最常見的表現方式是檔案,字元流用於操作檔案的子類一般是 FileReader 和 FileWriter 。

注意事項:
  • 1.寫入檔案後必須要用 flush() 重新整理。

  • 2.用完流後記得要關閉流

  • 4.使用流物件要丟擲 IO 異常

  • 3.定義檔案路徑時,可以用 File.separator 、 “/” 或者 “\”。

  • 5.在建立一個檔案時,如果目錄下有同名檔案將被覆蓋。

  • 6.在讀取檔案時,必須保證該檔案已存在,否則出異常

不關閉流的後果

  如果不關閉的話,那麼這個 IO 資源就會被他一直佔用,這樣別人想用就沒有辦法用了,所以這回造成資源浪費。但是在 Java7 中,資源的連線如果是在 try()中,凡是實現介面 AutoCloseable 的類都可以使用這種方式進行資源訪問,並且會自動釋放。

Java7 中新的語法其實就是一種編譯器優化,資源總會關閉,不管是否發生異常。人通常會犯錯,但是編譯器卻不會,所以使用新的方式進行資源訪問,能夠避免隱藏的bug,而在java7中絕大多數的資源訪問都已經重新實現了 AutoCloseable 介面,包括網路訪問 socket 等,所以基本上可以放心的使用,就算沒實現,編譯器也會快速報錯。

如何關閉流

關閉流需要按照從外到內的順序關閉。關閉流只需要關閉最外層的包裝流,其他流會自動呼叫關閉,這樣可以保證不會拋異常。

從內到外關閉報錯資訊:

	Exception in thread "main" java.io.IOException: Stream closed
	    at sun.nio.cs.StreamEncoder.ensureOpen(StreamEncoder.java:45)
	    at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:118)
	    at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
	    at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
	    at java.io.BufferedWriter.close(BufferedWriter.java:264)
	    at IOTest.main(IOTest.java:18)

四、JDK1.7新特性–自動關閉類

JDK1.7之後出現了一個重要的介面,以及改造了一個重要的方法結構:

  • AutoCloseable自動關閉介面
  • try(){ } ----- catch{ } ----- finally{ }

相應的一些資源也實現了該介面,如 preparedStatement 、 Connection 、 InputStream 、outputStream 等等資源介面。

介面的實現類要重寫 close() 方法,將要關閉的資源定義在 try() 中,這樣當程式執行完畢之後,資源將會自動關閉。

	/** Java7 之前 **/
	public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                if(fis != null) 
                    fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

	/** Java7 之後 **/
	public static void main(String[] args) {
	        try (FileInputStream fis = new FileInputStream("");) {
	            fis.read();
	        } catch (FileNotFoundException e) {
	            e.printStackTrace();
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	}

五、位元組流

簡介

  在 Java 中,位元組流一般適用於處理位元組資料(諸如圖片、視訊),字元流適用於處理字元資料(諸如文字檔案),但二者並沒有嚴格的功能劃分,因為有轉換流的存在,使得對於資料的處理變得更加靈活。
InputStream 和 OutputStream 分別是位元組輸入流與位元組輸出流的基類,它們的子類都是位元組流,主要用在按位元組來處理二進位制資料。

一個簡單的樣例

	/**
	 * 使用IO技術,建立一個目錄,然後複製一個檔案到該目錄!
	 */
	public class CreatDemo {
	    public static void main(String[] args) throws IOException {
			File file = new File("." + File.separator + "test" + File.separator + "demonstration.txt");
			FileInputStream fis = new FileInputStream(file.getCanonicalPath());
			byte[] b = new byte[(int) file.length()];
			fis.read(b);
			
			File copyfile = new File("." + File.separator + "test" + File.separator + "copy.txt");
			copyfile.createNewFile();
			
			FileOutputStream fos = new FileOutputStream(copyfile);
			fos.write(b);
			
			fos.close();
			fis.close();
			
			Runtime.getRuntime().exec("notepad " + copyfile.getCanonicalPath());
		}
	}
額外補充
	/**
	 * 使用IO技術,開發出一個控制檯的資源管理器!
	 *		要求:
	 *		從命令列輸入一個路徑!
	 *		如果存在將該目錄下所有的檔案和資料夾列舉出來,
	 *		如果不存在則輸出不存在該路徑。
	 */
	public class ResourceAdmin {
		public static void listAllFiles(File dir) throws IOException{
			File[] files = dir.listFiles();
			if(files == null || files.length == 0) {
				System.out.println("此目錄為空!!");
				return;
			}
			for(File f : files) {
				if(f.isDirectory()) {
					System.out.println("資料夾:" + f.getCanonicalPath());
					listAllFiles(f);
				}else {
					System.out.println("檔案:" + f.getCanonicalPath());
				}
			}
		}
	    public static void main(String[] args) throws IOException {
			Scanner sc = new Scanner(System.in);
			System.out.println("請輸入一個資料夾路徑");
			String src = sc.nextLine();
			File dir = new File(src);
			listAllFiles(dir);
		}
	}

六、字元流與位元組流的區別

  在 java.io 包中操作檔案內容的主要有兩大類:位元組流、字元流,兩類都分為輸入和輸出操作。在位元組流中輸出資料主要是使用 OutputStream 完成,輸入使的是 InputStream ,在字元流中輸出主要是使用 Writer 類完成,輸入流主要使用 Reader 類完成。(這四個都是抽象類)
  字元流處理的單元為2個位元組的 Unicode 字元,分別操作字元、字元陣列或字串,而位元組流處理單元為1個位元組,操作位元組和位元組陣列。所以字元流是由Java虛擬機器將位元組轉化為2個位元組的Unicode 字元為單位的字元而成的,所以它對多國語言支援性比較好!如果是音訊檔案、圖片、歌曲,就用位元組流好點,如果是關係到中文(文字)的,用字元流好點。
  所有檔案的儲存是都是位元組(byte)的儲存,在磁碟上保留的並不是檔案的字元而是先把字元編碼成位元組,再儲存這些位元組到磁碟。在讀取檔案(特別是文字檔案)時,也是一個位元組一個位元組地讀取以形成位元組序列。
  位元組流提供了處理任何型別的IO操作的功能,但它不能直接處理 Unicode 字元,而字元流就可以
  位元組流是最基本的,所有的 InputStrem 和 OutputStream 的子類都是,主要用在處理二進位制資料,它是按位元組來處理的 但實際中很多的資料是文字,又提出了字元流的概念,它是按虛擬機器的 encode 來處理,也就是要進行字符集的轉化 這兩個之間通過 InputStreamReader , OutputStreamWriter來關聯,實際上是通過 byte[] 和 String 來關聯 在實際開發中出現的漢字問題實際上都是在字元流和位元組流之間轉化不統一而造成的。

一個簡單的網路爬蟲

	public class FoundUrl {
		public static String Connect(String address) throws IOException {
			URL url = new URL(address);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setDoInput(true);
			conn.connect();
			InputStream in = conn.getInputStream();
			// 要爬網頁的編碼
			BufferedReader reader = new BufferedReader(new InputStreamReader(in,"utf-8"));
			StringBuffer sb = new StringBuffer();
			String line = null;
			while((line = reader.readLine()) != null) {
				sb.append(line);
			}
			return sb.toString();
		}
		
		@SuppressWarnings("static-access")
		public static void main(String[] args) throws IOException {
			FoundUrl fu = new FoundUrl();
			// 要爬的網頁
			String url = "https://www.baidu.com/";
			String found = fu.Connect(url);
			System.out.println(found);
			
			String information = found;
			
			// 輸出路徑
			File file = new File("C:\\Users\\Administrator\\Desktop\\Demo.html");
			FileWriter fw = new FileWriter(file);
			
			fw.write(information);
			fw.close();
			
			Runtime.getRuntime().exec("notepad " + file.getCanonicalPath());
		}
	}

總結圖示

IO總結


本文內容部分取自百度內容,如有雷同部分請見諒。