"萬字" Java I/O 詳解

来源:https://www.cnblogs.com/TheMagicalRainbowSea/archive/2023/02/11/17112236.html
-Advertisement-
Play Games

Java 平臺的基礎 I/O 類。它首先關註 I/O Streams,這是一個強大的概念, 可以大大簡化 I/O 操作。該課程還可以看到序列化,這使得程式可以將整個對象寫入流並再次讀取它們。 然後,該課程將查看 文件 I/O 和文件系統操作,包括隨機訪問文件。 I/O Streams 大多數都是講... ...


Java I/O流講解

每博一文案

誰讓你讀了這麼多書,又知道了雙水村以外還有一個大世界,如果從小你就在這個天地里,日出而作,日落而息。
那你現在就會和眾鄉親抱同一理想:經過幾年的辛勞,像大哥一樣娶個滿意的媳婦,生個胖兒子,加上你的體魄,
會成為一名出色的莊稼人。不幸的是,你知道的太多了,思考的太多了,因此才有了,這種不能為周圍人所理解的苦惱。
                                          —————— 《平凡的世界》
人生是這樣的不可預測,沒有永恆的痛苦,也沒有永恆的幸福,生活就像流水一般,
有時是那麼平展,有時又是那麼曲折。
世界上有些人因為忙而感到生活的沉重,也有些人因為閑而活得壓抑,人啊,都有自己一本難念的經;
可是不同處境的人又很難理解別人的苦處。
細想過來,每個人的生活也同樣是一個世界,即使是最平方的人,也要為他那個世界的存在而戰鬥。
                                          ——————  《平凡的世界》

@

目錄

1. File 類

java.io.File 類:文件和文件目錄路徑的抽象表示形式,與平臺無關

File 能新建,刪除,重命名文件和目錄,但File 不能訪問文件內容本身。如果需要訪問文件內容本身,則需要使用 輸入/輸出 流。

想要在Java 程式中表示一個真實存在的文件或目錄,那麼必須有一個 File 對象,但是 Java 程式中的一個 File 對象,可能沒有一個真實存在的文件或目錄。

File 對象可以作為參數傳遞給流的構造器。

1.1 File 類中:構造器

public File(String pathname);  // 過將給定路徑名字元串轉換為抽象路徑名來創建一個新 File 實例。如果給定字元串是空字元串,那麼結果是空抽象路徑名
public File(String parent,String child);  // 根據 parent 路徑名字元串和 child 路徑名字元串創建一個新 File 實例。以parent為父路徑,child為子路徑創建File對象
public File(File parent, String child); // 根據 parent 抽象路徑名和 child 路徑名字元串創建一個新 File 實例。根據一個父File對象和子文件路徑創建File對象
// 路徑可以是絕對路徑,也可以是相對路徑
  • 絕對路徑: 是一個固定的路徑,從盤符開始。
  • 相對路徑: 是相對於某個位置開始。IDEA中預設相對路徑是從**Project**項目(路徑)下和同級的 **src** 的路徑開始的,註意不是模塊開始的**Module** 。如下圖所示:src 和 Module 模塊是同級的。

1.2 File 類中:路徑分隔符

路徑中的每級目錄之間用一個路徑分隔符隔開。

路徑分隔符和系統有關:

  • windows和DOS系統預設使用“\”來表示,需要註意的是在 java 中 **"\"** 具有轉義的意思,所以想要表示真正的 “\” 需要兩個 **"\\"** 來轉義回來表示一個斜桿。
  • UNIX和URL使用“/”來表示。

Java程式支持跨平臺運行,因此路徑分隔符要慎用。

為瞭解決這個隱患,File類提供了一個常量:

public static final String separator。// 根據操作系統,動態的提供分隔符。
File file1 = new File("E:\\Test\\info.txt");

File file2 = new File("E:" + File.separator + "Test" + File.separator + "info.txt");

File file3 = new File("E:/Test");

// 這三者表示的路徑是一樣的。只是表示方式不同而已。

舉例:

package blogs.blog9;


import java.io.File;

public class FileTest {

    /**
     * File 構造器的使用
     */
    public static void main(String[] args) {
        // 構造器一:
        // 絕對路徑: 帶盤符
        File file = new File("E:\\Java\\JavaRebuilt\\src\\blogs\\blog9"); // 雙右斜桿表示一個 \ (轉義)
        // 相對路徑: IDEA預設是Project的根目錄,不是模塊Module的根目錄,
        // 也可以使用:左斜桿表示路徑分隔符
        System.out.println(file);
        File file2 = new File("src/blogs/blog9"); // 這裡是在src的包下(src 和 Module 模塊是同級的)
        System.out.println(file2);

        // 構造器二:
        // 第一個參數是第二個參數的父路徑,第二個參數是子路徑
        File file3 = new File("E:\\Java\\JavaRebuilt\\src\\blogs","blog9");
        System.out.println(file3);

        // 構造器三:
        // 第一個參數是 File 類對象(這裡是第二個參數的父路徑的File對象),第二個參數是子路徑
        File file4 = new File(file3,"blog9");
        System.out.println(file4);

    }
}

1.3 File 類中:常用方法

獲取文件屬性的信息的方法:

  • getAbsoluteFile() : 返回此File對象中路徑的絕對路徑。返回的是 File 對象
public File getAbsoluteFile(); // 返回此抽象路徑名的絕對路徑名形式。
  • getAbsolutePath() : 返回此抽象路徑名的絕對路徑名字元串
public String getAbsolutePath(); // 返回此抽象路徑名的絕對路徑名字元串
  • getPath() : 返回此抽象路徑名以字元串的形式。
public String getPath(); // 返回此抽象路徑名
  • getName() : 獲取名稱
public String getName(); // 返回由此抽象路徑名錶示的文件或目錄的名稱。該名稱是路徑名名稱序列中的最後一個名稱。如果路徑名名稱序列為空,則返回空字元串
  • getParent() : 獲取上層文件目錄路徑(也就是父路徑)。若無,返回null
public String getParent();  // 返回此抽象路徑名父目錄的路徑名字元串;如果此路徑名沒有指定父目錄,則返回 null。
  • length() : 返迴文件長度即(位元組數)
public long length();  // 獲取文件長度(即:位元組數)。不能獲取目錄的長度
  • lastModified() : 獲取最後一次的修改時間,毫秒值。
public long lastModified();  // 獲取最後一次的修改時間,毫秒值
  • list() : 獲取指定目錄下的所有文件或者文件目錄的名稱數組
public String[] list();  // 返回一個字元串數組,這些字元串指定此抽象路徑名錶示的目錄中的文件和目錄。
  • listFiles() : 獲取指定目錄下的所有文件或者文件目錄的 File 數組
public File[] listFiles();  // 返回一個抽象路徑名數組,這些路徑名錶示此抽象路徑名錶示的目錄中的文件。

舉例:

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;

public class FileTest {
    public static void main(String[] args) {
        File file = new File("src\\blog9\\hello.txt"); // 註意轉義以及文件尾碼
        File file2 = new File("src/blog9/hello3.txt"); // 左斜桿也是可以的

        String absolutePath = file.getAbsolutePath();  // 返回絕對路徑,以String的形式返回
        System.out.println(absolutePath);
        File absoluteFile = file.getAbsoluteFile();  // 返回絕對路徑,以File 對象的形式返回
        System.out.println();
        System.out.println(file.getPath());  // 返回此路徑名/目錄名
        System.out.println(file.getName()); // 返回該文件名/目錄名
        System.out.println(file.getParent()); // 返回該上層文件/目錄名稱
        System.out.println(file.length());  // 返迴文件長度即文件的大小(位元組)
        long l = file.lastModified();  // 返回該文件的最後一次修改的時間值(毫秒值)時間戳
        // 將時間戳轉換為Date,再轉換為 指定格式的字元串
        Date date = new Date(l);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:ss:mm SSS");
        String format = simpleDateFormat.format(date);
        System.out.println(format);

    }
}

舉例:

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;

public class FileTest {

    /**
     * File 文件目錄
     */
    public static void main(String[] args) {
        File file = new File("src/blogs/blog9"); // 也可以使用 左斜桿
        String[] list = file.list();  // 返回獲取指定目錄下的所有文件或者文件目錄的名稱數組
        for (String s : list) {
            System.out.println(s);
        }

        File[] files = file.listFiles();
        for(File f : files) {
            System.out.println(f);
        }
    }
}


File類的重命名功能

  • renameTo(File dest) : 把文件重命名為指定的文件路徑。換句話說:就是剪切附加對文件的重命名 的意思。
public boolean renameTo(File dest);  // 重新命名此抽象路徑名錶示的文件。

註意: 這裡的剪切效果,有一定的要求:就是比如:file.renameTo(dest) 想要將 file 剪切到 dest 位置路徑上。要保證 file 剪切的文件實際在硬碟中存在,並且 dest 不能在硬碟文件中存在(僅僅當一個路徑)。如果不滿足會失敗,返回 false

舉例:

import java.io.File;

public class FileTest {
    public static void main(String[] args) {
        File file = new File("src\\blogs\\blog9\\hello.txt");
        File dest = new File("E:\\臨時文件\\temp\\test.txt");
        boolean b = file.renameTo(dest);  // 將file文件剪切到 dest 中並重命名
        System.out.println(b);

    }
}

失敗:原因是:dest 中的 test.txt 是在硬碟中實際存在的。

將test.txt去了就沒事了 ,再重新剪切,就可以了。

File 類的判斷功能

  • isDirectory() : 判斷是否是文件目錄
public boolean isDirectory(); // 測試此抽象路徑名錶示的文件是否是一個目錄。
  • isFile()  :判斷是否是文件
public boolean isFile();  // 當且僅當此抽象路徑名錶示的文件存在且 是一個標準文件時,返回 true;否則返回 false
  • exists() : 判斷該文件/目錄是否存在
public boolean exists();  // 測試此抽象路徑名錶示的文件或目錄是否存在
  • canRead() : 判斷該文件是否可讀的
public boolean canRead(); // 當且僅當此抽象路徑名指定的文件存在且 可被應用程式讀取時,返回 true;否則返回 false
  • canWrite() : 判斷該文件是否可寫的
public boolean canWrite(); // 當且僅當文件系統實際包含此抽象路徑名錶示的文件且 允許應用程式對該文件進行寫入時,返回 true;否則返回 false.
  • isHidden() :  判斷該文件是否隱藏的
public boolean isHidden(); // 當且僅當此抽象路徑名錶示的文件根據底層平臺約定是隱藏文件時,返回 true

舉例:

import java.io.File;

public class FileTest {
    public static void main(String[] args) {
        File file = new File("src\\blogs\\blog9\\hello.txt"); // 註意轉義以及文件尾碼(該文件實際存在的)
        File file2 = new File("src/blogs/blog9/hello3.txt"); // 左斜桿也是可以的 (該文件不存在的)

        System.out.println(file.isDirectory()); // 判斷是否是文件目錄
        System.out.println(file.isFile());  // 判斷是否為文件
        System.out.println(file.exists()); // 判斷該文件/目錄是否實際存在
        System.out.println(file.canRead());  // 判斷該我呢見是否可讀的
        System.out.println(file.canWrite()); // 判斷該文件是否是可寫的
        System.out.println(file.isHidden()); // 判斷該文件是否隱藏的

        System.out.println("*************************** file2 *************************");
        System.out.println(file2.isDirectory()); // 判斷是否是文件目錄
        System.out.println(file2.isFile());  // 判斷是否為文件
        System.out.println(file2.exists()); // 判斷該文件/目錄是否實際存在
        System.out.println(file2.canRead());  // 判斷該我呢見是否可讀的
        System.out.println(file2.canWrite()); // 判斷該文件是否是可寫的
        System.out.println(file2.isHidden()); // 判斷該文件是否隱藏的
    }
}

File 類的創建功能

  • **createNewFile() ** : 創建文件。若文件存在,則不創建,返回false
public boolean createNewFile() throws IOException // 如果指定的文件不存在併成功地創建,則返回 true;如果指定的文件已經存在,則返回 false
  • mkdirs() : 創建文件目錄。創建文件目錄。如果上層文件目錄不存在,一併創建
public boolean mkdirs(); // 創建此抽象路徑名指定的目錄,包括所有必需但不存在的父目錄。註意,此操作失敗時也可能已經成功地創建了一部分必需的父目錄。
  • mkdir() : 創建文件目錄。如果此文件目錄存在,就不創建了。如果此文件目錄的上層目錄不存在,也不創建。
public boolean mkdir(); // 創建此抽象路徑名指定的目錄。

註意事項:如果你創建文件或者文件目錄沒有寫盤符路徑,那麼,預設在項目路徑下。

舉例:

import java.io.File;
import java.io.IOException;

public class FileTest {
    public static void main(String[] args) {
        File file = new File("src/blogs/blog9/hello.txt");

        boolean b = false;
        try {
            b = file.createNewFile();  // 文件存在不創建,不存在文件創建
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(b);
    }
}

創建目錄: 使用 mkdir

import java.io.File;

public class FileTest {
    public static void main(String[] args) {
        File file = new File("src/blogs/blog9/test/test2/");
        boolean b = file.mkdir();  // 如果對應的 test2目錄的上級目錄test不存在,則目錄都不創建
        System.out.println(b);
    }
}

使用 mkdirs()

import java.io.File;
import java.io.IOException;

public class FileTest {
    public static void main(String[] args) {
        File file = new File("src/blogs/blog9/test/test2/");
        boolean b = file.mkdirs();  // 如果對應的 test2目錄的上級目錄test不存在,則目錄一併都創建
        System.out.println(b);
    }
}

File類的刪除功能:

  • delete()  : 刪除文件或者文件夾
public boolean delete(); // 刪除此抽象路徑名錶示的文件或目錄。如果此路徑名錶示一個目錄,則該目錄必須為空才能刪除。

刪除註意事項:Java中的刪除不走回收站。要刪除一個文件目錄,請註意該文件目錄內不能包含文件或者文件目錄。如果含有無法刪除的。

舉例:

import java.io.File;

public class FileTest {
    public static void main(String[] args) {
        File file = new File("src/blogs/blog9/test");
        boolean b = file.delete();  // test 目錄下不能有文件/目錄,有的話無法刪除
        System.out.println(b);
    }
}

小結

1.4 實用案例:

判斷指定目錄下是否有尾碼名為.jpg的文件,如果有,就輸出該文件名稱

package com.atguigu.exer2;

import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;

import org.junit.Test;
/**
判斷指定目錄下是否有尾碼名為.jpg的文件,如果有,就輸出該文件名稱
 */
public class FindJPGFileTest {

	@Test
	public void test1(){
		File srcFile = new File("d:\\code");
		
		String[] fileNames = srcFile.list();
		for(String fileName : fileNames){
			if(fileName.endsWith(".jpg")){
				System.out.println(fileName);
			}
		}
	}
    
	@Test
	public void test2(){
		File srcFile = new File("d:\\code");
		
		File[] listFiles = srcFile.listFiles();
		for(File file : listFiles){
			if(file.getName().endsWith(".jpg")){
				System.out.println(file.getAbsolutePath());
			}
		}
	}
    
	/*
	 * File類提供了兩個文件過濾器方法
	 * public String[] list(FilenameFilter filter)
	 * public File[] listFiles(FileFilter filter)
	 */
	@Test
	public void test3(){
		File srcFile = new File("d:\\code");
		
		File[] subFiles = srcFile.listFiles(new FilenameFilter() {
			
			@Override
			public boolean accept(File dir, String name) {
				return name.endsWith(".jpg");
			}
		});
		
		for(File file : subFiles){
			System.out.println(file.getAbsolutePath());
		}
	}
	
}

遍歷指定目錄所有文件名稱,包括子文件目錄中的文件。
拓展1:並計算指定目錄占用空間的大小
拓展2:刪除指定文件目錄及其下的所有文件

package com.atguigu.exer2;

import java.io.File;
/**
 * 3. 遍歷指定目錄所有文件名稱,包括子文件目錄中的文件。
	拓展1:並計算指定目錄占用空間的大小
	拓展2:刪除指定文件目錄及其下的所有文件

 * @author shkstart 郵箱:[email protected]
 * @version  創建時間:2019年2月23日  上午1:55:31
 *
 */
public class ListFilesTest {

	public static void main(String[] args) {
		// 遞歸:文件目錄
		/** 列印出指定目錄所有文件名稱,包括子文件目錄中的文件 */

		// 1.創建目錄對象
		File dir = new File("E:\\teach\\01_javaSE\\_尚矽谷Java編程語言\\3_軟體");

		// 2.列印目錄的子文件
		printSubFile(dir);
	}

	public static void printSubFile(File dir) {
		// 列印目錄的子文件
		File[] subfiles = dir.listFiles();

		for (File f : subfiles) {
			if (f.isDirectory()) {// 文件目錄
				printSubFile(f);
			} else {// 文件
				System.out.println(f.getAbsolutePath());
			}

		}
	}

	// 方式二:迴圈實現
	// 列出file目錄的下級內容,僅列出一級的話
	// 使用File類的String[] list()比較簡單
	public void listSubFiles(File file) {
		if (file.isDirectory()) {
			String[] all = file.list();
			for (String s : all) {
				System.out.println(s);
			}
		} else {
			System.out.println(file + "是文件!");
		}
	}

	// 列出file目錄的下級,如果它的下級還是目錄,接著列出下級的下級,依次類推
	// 建議使用File類的File[] listFiles()
	public void listAllSubFiles(File file) {
		if (file.isFile()) {
			System.out.println(file);
		} else {
			File[] all = file.listFiles();
			// 如果all[i]是文件,直接列印
			// 如果all[i]是目錄,接著再獲取它的下一級
			for (File f : all) {
				listAllSubFiles(f);// 遞歸調用:自己調用自己就叫遞歸
			}
		}
	}

	// 拓展1:求指定目錄所在空間的大小
	// 求任意一個目錄的總大小
	public long getDirectorySize(File file) {
		// file是文件,那麼直接返回file.length()
		// file是目錄,把它的下一級的所有大小加起來就是它的總大小
		long size = 0;
		if (file.isFile()) {
			size += file.length();
		} else {
			File[] all = file.listFiles();// 獲取file的下一級
			// 累加all[i]的大小
			for (File f : all) {
				size += getDirectorySize(f);// f的大小;
			}
		}
		return size;
	}

	// 拓展2:刪除指定的目錄
	public void deleteDirectory(File file) {
		// 如果file是文件,直接delete
		// 如果file是目錄,先把它的下一級幹掉,然後刪除自己
		if (file.isDirectory()) {
			File[] all = file.listFiles();
			// 迴圈刪除的是file的下一級
			for (File f : all) {// f代表file的每一個下級
				deleteDirectory(f);
			}
		}
		// 刪除自己
		file.delete();
	}

}

2. I/O 流的概述

一個 I / O流 代表輸入源或輸出目的地。流可以表示許多不同種類的源和目的地,包括磁碟文件,設備,其他程式和存儲器陣列。

流支持許多不同類型的數據,包括簡單位元組,原始數據類型,本地化字元和對象。一些流簡單地傳遞數據; 其他人以有用的方式操縱和轉換數據。

I/O  其中的 IInput 的縮寫,OOutput 的縮寫。I/O 技術是非常實用的技術,用於處理設備之間的數據傳輸。如讀/寫文件,網路通訊等。

Java 程式中,對於數據的輸入/輸出操作以 流(stream) 的方式進行。

java.io 包下提供了各種 “流”類和介面,用以獲取不同種類的數據,並通過方法輸入或輸出數據。

無論內部工作如何,所有流都會使用與使用它們的程式相同的簡單模型:

流是一系列數據。程式使用 輸入流 從源中讀取數據:**input** 輸入流以記憶體為參考對象(將文件中的數據內容寫入到記憶體當中) 以及 **Read** (以文件為參考對象,將讀取文件中的數據到記憶體當中)。這兩個都是將意思都是一樣的將文件中的數據讀取出來寫入到記憶體當中。

程式使用 輸出流 將數據寫入目的地。**Output** 輸出流以記憶體為參考對象(將記憶體中的數據內容輸出到硬碟文件當中) 以及 **Write** (以文件為參考對象,將記憶體中的數據到寫入到硬碟文件當中)。這兩個都是將意思都是一樣的:將記憶體中的數據輸出到硬碟文件當中。

2.1 I/O的分類和體繫結構

按照不同的分類方式, 可以將流分為不同的類型。

2.1.1 輸入流 和 輸出流

按照流的流向來分, 可以分為輸入流和輸出流:

輸入流: 只能從中讀取數據, 而不能向其寫入數據。
輸出流: 只能向其寫入數據, 而不能從中讀取數據。
此處的輸入、 輸出涉及一個方向問題, 對於如圖 1 所示的數據流向, 數據從記憶體到硬碟, 通常稱為輸出流——也就是說, 這裡的輸入、 輸出都是從程式運行所在記憶體的角度來劃分的。

對於如圖 2 所示的數據流向, 數據從伺服器通過網路流向客戶端, 在這種情況下, Server 端的記憶體負責將數據輸出到網路里, 因此 Server 端的程式使用輸出流; Client 端的記憶體負責從網路里讀取數據, 因此 Client 端的程式應該使用輸入流。

2.1.2 位元組流 和 字元流

按操作數據單位不同分為:位元組流(8 bit),字元流(16 bit)。

位元組流和字元流的用法幾乎完全一樣, 區別在於位元組流和字元流操作的數據單元的不同:位元組流是 8 位的位元組, 而字元流操作的數據單元是 16 位的字元。其中還有一點不同的就是:

  • 字元流:只能讀取操作文本文件,因為字元流讀取的是文件中的 char 字元信息。 .c,.java,.c++,.txt 等等這些都是文本文件不僅僅只是 txt文件,而特別註意的是 :.wrod 不是文本文件,wrod中的文字是經過特殊處理的存在一定的規範格式,不是純文本文件。
  • 位元組流:可以操作任何的文件,因為位元組流讀取的是二進位信息,讀取1個位元組byte,等同於一次讀取8個二進位,這種流是萬能的,什麼類型的文件都可以讀取到,因為文件都是有二進位組成的。包括: 文本文件,圖片,聲音文件。

2.1.3 節點流 和 處理流(包裝流)

按流的角色的不同分為:節點流,處理流。

  • 節點流: 所謂的節點流:就是最基本的一個流到底目的的流向,其中的流沒有被其它的流所包含住。直接從數據源或目的地讀寫數據。如下圖所示

  • 處理流: 處理流又稱為包裝流 ,處理流對一個己存在的流進行連接或封裝/包裝, 通過封裝後的流來實現數據讀/寫功能。不直接連接到數據源或目的地,而是“連接”在已存在的流(節點流或處理流)之上,通過對數據的處理為程式提 供更為強大的讀寫功能。
  • 如下圖所示:

註意: 節點流和包裝流是相對的,有時候,相對於不同的流的,一個節點流變成了是另一個流的包裝流。一個包裝流變成了另一個流的節點流 。如下圖所示:

區分一個流是節點流還是包裝流:大家可以:以誰包裝誰作為參考,被包裝的流就是節點流,包裝了其它的流的就是包裝流,當然註意這是相對的。

2.1.4 流的概念模型

Java 把所有設備里的有序數據抽象成流模型, 簡化了輸入/輸出處理, 理解了流的概念模型也就瞭解了Java IO。

通過使用處理流, Java 程式無須理會輸入/輸出節點是磁碟、 網路還是其他的輸入/輸出設備, 程式只要將這些節點流包裝成處理流, 就可以使用相同的輸入/輸出代碼來讀寫不同的輸入/輸出設備的數據。

2.1.5 I/O 的體繫結構

  • Java的IO流共涉及40多個類,實際上非常規則,都是從如下 Java Io 流四大家族  個 抽象基類派生的。
  • 由以下這四個類派生出來的子類。其名稱都是以其父類名作為子類名尾碼。這樣用於我們辨認。
  • Java IO流四大家族:
    • java.io.InputStream  位元組輸入流,類名是以  "stream" 結尾的。
public abstract class InputStream implements Closeable {}
  • java.io.OutputStream 位元組輸出流,類名是以  "stream" 結尾的。
public abstract class OutputStream implements Closeable, Flushable {}
  • java.io.Reader 字元輸入流,類名是以 "Reader/Writer"結尾的。
public abstract class Reader implements Readable, Closeable {}
  • java.io.Writer 字元輸出流,類名是以以 "Reader/Writer"結尾的。
public abstract class Writer implements Appendable, Closeable, Flushable {}
  • 上述四大家族的首領都是抽象類 abstract class。這四個類都實現了 java.io.Closeable 介面,都是可以關閉的,都有 close() 方法。流畢竟是一個管道,這個記憶體和硬碟之間的通道,用完之後一定要關閉。不然會耗費很多資源(因為Java打開的資源是有限的,當你打開過多的資源超過限制時就無法打開其它的資源了)。養成好習慣,用完之後一定要關閉(必須關閉)。

  • 這四個類都實現了java.io.Flushable 介面,都是可刷新 的,都是有 flush() 方法的,養成一個好習慣,輸出流(將記憶體當中的數據輸出到硬碟文件當中)在最終(輸出完)之後,一定要記得 flush() ,刷新一下,這個刷新的作用就是清空管道(強制將記憶體當中的數據輸出到文件中)。為什麼要清空管道呢:因為:如果沒有 flush() 可以會導致記憶體中一部分的數據並沒有全部輸出到硬碟當中,從而導致一部分的數據丟失。
  • 註意:在Java中只要 類名是以 **"stream"** 結尾的都是位元組流,以 **"Reader/Writer"**結尾的都是字元流。
java.io包下需要掌握的流有 16個

文件專屬:
java.io.FileInputStream
java.io.FileOutputStream    位元組流無法讀取到: 文件中的空格的
java.io.FileReader
java.io.FileWriter          字元流可以讀取到:文件中的空格的

轉換流: (將位元組流轉換字元流)
java.io.InputStreamReader
java.io.OutputStreamWriter

緩衝流專屬:
java.io.BufferedReader
java.io.BufferedWriter
java.io.BufferedInputStream
java.io.BufferedOutputStream
java.io.BufferedOutputStream

數據流專屬:
java.io.DataInputStream
java.io.DataOutputStream

標準輸出流:
java.io.PrintWtiter
java.io.PrinStream

對象專屬流:
java.io.ObjectInputStream
java.io.ObjectOutputStream

2.2 字元流

2.2.1 java.io.FileReader 字元輸入流

關於字元輸入流的類都是繼承了:java.io.Writer(字元輸出流)/ java.io.Reader (字元輸入流) 來使用的,但是這個兩個類是抽象類,是無法 new 對象來使用的。所以我們就需要使用其實現的子類:對於文件字元輸入流比較常用的就是: java.io.FileReader 這個子類了。

字元流: 只能讀取文本文件,不能讀取其它格式的文件,文本文件不僅僅是 .txt 尾碼的文件,.c,.java,.c++ 都是文本文件,註意 : word 不是文本文件,因為 word 中的文本字元是有一個規範格式設置的。不是純的文本文件。字元流操作字元,只能操作普通文本文件。最常見的文本文件:.txt,.java,.c,.cpp 等語言的源代碼。尤其註意.doc,excel,ppt這些不是文本文件。

在讀取文件時,必須保證該文件已存在,否則報異常。

其中繼承的 InputStreamReader 是個轉換流,繼承了  Reader 抽象類的。

FileReader的構造器

public FileReader(File file) throws FileNotFoundException; // 在給定從中讀取數據的 File 的情況下創建一個新 FileReader對象
public FileReader(String fileName) throws FileNotFoundException; // 根據文件的相對路徑名/絕對路徑創建一個新 FileReader對象

舉例:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;

public class FileReaderTest {
    public static void main(String[] args) {
        File file = new File("E:\\Java\\JavaRebuilt\\src\\blogs\\blog9\\hello.txt");  // 絕對路徑
        try {
            FileReader fileReader = new FileReader(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        try {
            FileReader fileReader2 = new FileReader("src/blogs/blog9/hello.txt"); // 相對路徑
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

如下是 FileReader  繼承 java.io.InputStreamReader** 繼承的方法**

  • read() : 讀取單個字元。作為整數讀取的字元,範圍在 0 到 65535 之間 (0x00-0xffff)(2個位元組的Unicode碼),如果已到達流的末尾,則返回 -1。
public int read() throws IOException;  // 讀取單個字元。
  • int read(char[] cbuf) : 將字元讀入數組。如果已到達流的末尾,則返回 -1。否則返回本次讀取的字元數。
public int read(char[] cbuf) throws IOException; // 將文件中的數據讀取到char[] cbuf的字元數組當中,返回讀取到的個數。
  • int read(char[] cbuf,int off,int len) : 將字元讀入數組的某一部分。存到數組cbuf中,從off處開始存儲,最多讀len個字 符。如果已到達流的末尾,則返回 -1。否則返回本次讀取的字元數。
public int read(char[] cbuf,int offset, int length) throws IOException; // 將字元讀入數組中的某一部分.
  • public void close() throws IOException :關閉此輸入流並釋放與該流關聯的所有系統資源。
public void close() throws IOException;  // 關閉此輸入流並釋放與該流關聯的所有系統資源。

需要明白:對於 read() 讀取文件內容的方式是,一個一個字元的讀取的,每調用一次 read()對於的文件中的游標就會往後移動一下。對於特殊的 read(char[ ] cbuf) 讀取的個數是 char[] 數組的長度,往後移動游標的位置也是  char[] 數組的長度。以及返回的是對於字元的編碼值。

 設文件 file1.txt ,採用字元流的話是這樣讀的:
  a中國bo張三
  第一次讀: ‘a’字元
  第二次讀: ‘中’字元

舉例:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class FileReaderTest {
    public static void main(String[] args) {
        FileReader fileReader = null; // 相對路徑
        try {
            fileReader = new FileReader("src/blogs/blog9/hello.txt");
            int read = fileReader.read();  // 返回的是編碼值
            System.out.println(read);

            read = fileReader.read();
            System.out.println(read);

            read = fileReader.read();
            System.out.println(read);

            read = fileReader.read();
            System.out.println(read);

            read = fileReader.read();
            System.out.println(read);

            read = fileReader.read();
            System.out.println(read);

            read = fileReader.read();
            System.out.println(read);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // fileReader 防止 null引用
            if(fileReader != null) {
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

舉例: 使用 while() 迴圈處理

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class FileReaderTest {
    public static void main(String[] args) {
        FileReader fileReader = null; // 相對路徑
        try {
            fileReader = new FileReader("src/blogs/blog9/hello.txt");
            int len = 0;

            // 當read()讀取到 文件末尾返回 -1,跳出迴圈
            while((len = fileReader.read()) != -1) {
                System.out.println(len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // fileReader 防止 null引用
            if(fileReader != null) {
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

舉例: 使用 int read(char[] cbuf) : 將字元讀入數組。如果已到達流的末尾,則返回 -1。否則返回本次讀取的字元數

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class FileReaderTest {
    public static void main(String[] args) {
        FileReader fileReader = null; // 相對路徑
        try {
            fileReader = new FileReader("src/blogs/blog9/hello.txt");
            int len = 0;
            char [] chars = new char[4];
            // read(chars) 一次性讀取數組長度個字元,返回讀取到的字元個數。到達文件末尾返回-1
            while((len = fileReader.read(chars)) != -1) {
                // 將char[] 數組轉換為字元串
                System.out.println(new String(chars));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // fileReader 防止 null引用
            if(fileReader != null) {
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

read(char[]) 讀取數據時的覆蓋效果的講解

舉例: 去除 read(char[] cduf) 的覆蓋效果,我們讀取多到了多少個字元,就 new String 轉換多少個字元

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class FileReaderTest {
    public static void main(String[] args) {
        FileReader fileReader = null; // 相對路徑
        try {
            fileReader = new FileReader("src/blogs/blog9/hello.txt");
            int len = 0;
            char [] chars = new char[4];
            // read(chars) 一次性讀取數組長度個字元,返回讀取到的字元個數。到達文件末尾返回-1
            while((len = fileReader.read(chars)) != -1) {
                // 將char[] 數組轉換為字元串
                // 這裡我們 讀取到了多少個字元,就將 chars數組中的前多少個轉換為字元串
                System.out.println(new String(chars,0,len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // fileReader 防止 null引用
            if(fileReader != null) {
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

2.2.2 java.io.FileWriter 字元輸出流

於字元流輸出的類都是繼承了:java.io.Writer(字元輸出流)/ java.io.Reader (字元輸入流) 來使用的,但是這個兩個類是抽象類,是無法 new 對象來使用的。所以我們就需要使用其實現的子類:對於文件字元輸出流比較常用的就是: java.io.FileWriter 這個子類了。

其中繼承的 OutputStreamWriter 是個轉換流,繼承了  Writer 抽象類的。

OutputStreamWriter 的構造器:

public FileWriter(File file) throws IOException;  // 根據給定的 File 對象構造一個 FileWriter 對象
public FileWriter(String fileName) throws IOException; // 根據給定的文件名構造一個 FileWriter 對象。
public FileWriter(File file,boolean append) throws IOException; // 根據給定的 File 對象構造一個 FileWriter 對象。如果第二個參數為 true,則將位元組寫入文件末尾處,而不是寫入文件開始處。
//  file - 要寫入數據的 File 對象
// append - 如果為 true,則將位元組寫入文件末尾處,而不是寫入文件開始處 ,預設是 false,不寫的話也是 false

舉例:

package blogs.blog9;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class FileWriterTest {
    public static void main(String[] args) {
        File file = new File("E:\\Java\\JavaRebuilt\\src\\blogs\\blog9\\hello.txt"); // 絕對路徑
        try {
            FileWriter fileWriter = new FileWriter(file);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            FileWriter fileWriter2 = new FileWriter("src/blogs/blog9/hello.txt"); // 相對路徑
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            FileWriter fileWriter3 = new FileWriter("src/blogs/blog9/hello.txt",true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

如下是 FileWriter  繼承 java.io.OutputStreamWriter繼承的方法

  • void write(int c) : 寫入單個字元。要寫入的字元包含在給定整數值的 16 個低位中,16 高位被忽略。 即寫入0 到 65535 之間的Unicode碼。
public void write(int c) throws IOException;  // 寫入單個字元。
  • void write(char[] cbuf) : 將字元數組的內容,寫入到對應的硬碟文件中去
public void write(char[] cbuf) throws IOException; // 將字元數組的內容,寫到文件中
  • void write(char[] cbuf,int off,int len) : 寫入字元數組的某一部分。從off開始,寫入len個字元。
public void write(char[] cbuf,int off,int len) throws IOException // 寫入字元數組的某一部分。
  • void write(String str) :將字元串的內容,寫入到對應的硬碟文件中去
public void write(Stirng str) throws IOException
  • void write(String str,int off,int len) :寫入字元串的某一部分。從off開始,寫入len個結束
public void write(String str,int off,int len) throws IOException
  • void flush() :刷新該流的緩衝,立即記憶體中的數據寫入預期目標硬碟文件中去。
public void flush() throws IOException;  // 刷新該流的緩衝。
  • public void close() :關閉此輸出流並釋放與該流關聯的所有系統資源
public void close() throws IOException; // 關閉此流,但要先刷新它。在關閉該流之後,再調用 write() 或 flush() 將導致拋出 IOException。關閉以前關閉的流無效。

註意: 如果寫入到的文件不存在,是會自動創建的。如果文件已經存在了,在創建FileWriter 對象時沒有設置為 true 的話,是會將原本文件中已經存在的內容覆蓋的,寫入新的內容。

舉例: 文件不存在,自動創建。

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class FileReaderTest {
    public static void main(String[] args) {
        FileReader fileReader = null; // 相對路徑
        try {
            fileReader = new FileReader("src/blogs/blog9/hello.txt");
            int len = 0;
            char [] chars = new char[4];
            // read(chars) 一次性讀取數組長度個字元,返回讀取到的字元個數。到達文件末尾返回-1
            while((len = fileReader.read(chars)) != -1) {
                // 將char[] 數組轉換為字元串
                // 這裡我們 讀取到了多少個字元,就將 chars數組中的前多少個轉換為字元串
                System.out.println(new String(chars,0,len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // fileReader 防止 null引用
            if(fileReader != null) {
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

舉例: 文件已經存在,寫入的信息覆蓋原本文件的全部內容

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class FileWriterTest {
    public static void main(String[] args) {
        FileWriter fileWriter = null; // 相對路徑
        try {
            // 1. 創建輸出流對象: FileWriter
            fileWriter = new FileWriter("src/blogs/blog9/hello2.txt");

            // 2. 將記憶體當中的內容寫入到文件中
            fileWriter.write("你好世界");
            // 3. 刷新:將記憶體中沒有輸出到文件中的內容,強制全部寫入到文件中
            fileWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 防止 null 引用
            if(fileWriter != null) {
                // 4. 關閉IO資源
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

舉例: 創建 **FileWriter ** 對象時,設置 true ,將記憶體當中的信息寫入到文件的末尾去,不會覆蓋原本文件中的內容

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class FileWriterTest {
    public static void main(String[] args) {
        FileWriter fileWriter = null; // 相對路徑
        try {
            // 1. 創建輸出流對象: FileWriter,並設置 true 將寫入的內容追加到文件的末尾中去
            fileWriter = new FileWriter("src/blogs/blog9/hello2.txt",true);

            // 2. 將記憶體當中的內容寫入到文件中
            fileWriter.write("\n");  // 換行
            fileWriter.write("Hello World");
            // 3. 刷新:將記憶體中沒有輸出到文件中的內容,強制全部寫入到文件中
            fileWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 防止 null 引用
            if(fileWriter != null) {
                // 4. 關閉IO資源
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

2.2.3 實例文本文件的拷貝

將同目錄中的 hello.txt 文件的內容拷貝到 同目錄中的 hello2.txt 中去

思路:

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class FileWriterTest {
    public static void main(String[] args) {
        FileWriter descFile = null;
        FileReader srcFile = null; // 註意文件尾碼
        try {
            // 1. 創建hello.txt 文件的字元輸入流對象,以及 hello2.txt文件的字元輸出流對象
            descFile = new FileWriter("src/blogs/blog9/hello2.txt");
            srcFile = new FileReader("src/blogs/blog9/hello.txt");

            // 2. 一邊讀,一邊寫
            int len = 0;
            char[] chars = new char[10];
            // 讀取hello.txt的數據信息
            while((len = srcFile.read(chars)) != -1) {
                // 將讀取到的內容寫入到hello2.txt文件中
                descFile.write(chars,0,len);
            }

            // 3. 刷新:將記憶體中遺留沒有寫入到文件中的信息,全部強制寫入到文件中去
            descFile.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 5. 關閉IO資源
            // 分開 try,如果兩個一起try的話其中一個出現異常了,後面的一個IO就無法關閉了
            if(srcFile != null) {  // 防止 null引用
                try {
                    srcFile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if(descFile != null) {  // 防止 null引用
                try {
                    descFile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }
}

2.3 位元組流

2.3.1 FileInputStream 位元組輸入流

關於位元組輸入流的類都是繼承了:InputStream(位元組輸入流) 來使用的,但是個類是抽象類,是無法 new 對象來使用的。所以我們就需要使用其實現的子類:對於文件字元輸入流比較常用的就是: **java.io.FileInputStream ** 這個子類了。

位元組流: 可以操作任何的文件,因為位元組流讀取的是二進位信息,讀取1個位元組byte,等同於一次讀取8個二進位,這種流是萬能的,什麼類型的文件都可以讀取到,因為文件都是有二進位組成的。包括: 文本文件,圖片,聲音文件。再比如:比如:.mp3,.avi,.rmvb,mp4,.jpg,.doc,.ppt等文件。

<

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 為什麼會選擇Pop!_os 剛決定使用Linux系統的時候想的肯定是用Arch Linux(不得不說ArchWiki太NB了),但是遇到兩個麻煩沒有解決:1.連上wifi後發現沒有分配IPv4地址,google了一圈都沒找到解決方法。於是放棄了。2.第二次嘗試安裝Arch(主要是第一次安裝失敗後,心 ...
  • 使用JS的DOM(文檔對象模型)獲取前端迴圈的參數 使用Go語言渲染html,但是想讓網頁動起來,顯示一些彈窗還是比較麻煩的,於是乎,想到使用js獲取頁面的數據進行顯示,但是js無法載入go的一些變數。想了很久,突然在網頁調試的時候使用了js的DOM進行元素查找獲得了些許靈感最後實現了這個功能。 1 ...
  • 其他章節請看: react 高效高質量搭建後臺系統 系列 表格 有一種頁面在後臺系統中比較常見:頁面分上下兩部分,上部分是 input、select、時間等查詢項,下部分是查詢項對應的表格數據。包含增刪改查,例如點擊新建進行新增操作。就像這樣: 本篇將對 ant 的表格進行封裝。效果如下: spug ...
  • 問題:對於超大的 string V8不能支持 問題背景 在 Nodejs 計算服務中,對端上上報的記憶體信息二進位數據進行預處理+緩存時,遇到了一個奇怪的報錯:RangeError: Invalid string length 。根據該報錯信息,查找得知是字元串長度超過了 node.js 的限制,即 ...
  • 1基礎知識 機器語言是機器指令的集合,由0和1組成,但是很長很複雜,彙編語言因此產生。 彙編語言的主體是彙編指令。彙編指令是機器指令的便於記憶的書寫格式。 程式員寫完彙編指令通過編譯器轉換為機器碼,機器碼再傳到電腦執行。 彙編語言有以下三類: 1彙編指令:助記符,有對應機器碼 2.偽指令:沒有對應 ...
  • Java線程里:“中斷”就是指“終止”,與操作系統里的"中斷"、“異常”是完全不同的概念; 由於stop()方法過於暴力,可能導致資源回收無法風險、開銷過大等問題,此方法已過期,故Java中沒有強制中斷線程的手段;但可以調用interupt()、interupted()方法來向進程提出中斷請求,待進 ...
  • 聲明:文章來源於網路,如有侵權,請聯繫刪除 網頁即將載入 --網頁即將載入 if(網頁鏈接:find"url/.")then 停止載入() 進入子頁面("游覽",{鏈接=網頁鏈接}) end 載入本地網頁 載入本地網頁("file:///android_asset/drawable/index.ht ...
  • 這篇文章主要介紹分散式系統中的集中式結構,以及我們經常使用的三種基於集中式結構的解決方案:Google Borg、Kubernetes和Mesos。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...