本篇博客主要介紹Java中基礎IO的基本使用。
什麼是文件?
在介紹基礎IO之前,我們先來理解一下什麼是文件?
- 文件可以簡單的理解成,在外設硬盤上保存數據的一種方式;
- 文件一共可以由兩部分組成:屬性(文件大小、文件名、文件類型等)和內容(文件中放了什麼);
File文件操作類
在java.io
包中,用File類可以對文件進行操作(創建、刪除、獲取屬性信息等)。
常用構造方法
方法 | 說明 |
public File(String pathname) | 創建指定路徑文件對象 |
public File(String parent, String child) | 同上,但可指明父路徑和子路徑 |
基本文件操作
方法 | 說明 |
public boolean exists() | 測試指定路徑文件或目錄是否存在 |
public boolean isDirectory() | 判斷一個文件是目錄 |
public boolean isFile() | 判斷是否是文件 |
public boolean delete() | 刪除文件 |
public boolean createNewFile() throws IOException | 創建一個新文件 |
下面看代碼演示:
import org.junit.Assert;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
public class FileTest {
private String path = "D:\\test.txt";
@Test
public void existsTest() {
File file = new File(path);
Assert.assertTrue(file.exists());
}
@Test
public void isDirectoryTest() {
File file = new File(path);
Assert.assertFalse(file.isDirectory());
}
@Test
public void isFileTest() {
File file = new File(path);
Assert.assertTrue(file.isFile());
}
@Test
public void deleteTest() {
File file = new File(path);
Assert.assertTrue(file.delete());
}
@Test
public void createNewFileTest() throws IOException {
File file = new File(path);
Assert.assertTrue(file.createNewFile());
}
}
以上代碼實現了最基本的文件操作,但是代碼存在兩個問題:
- 實際項目部署環境可能與開發環境不同。不同的環境下路徑分隔符是不同的,Windows下使用\作爲路徑分隔符。而Unix/Linux系統下使用的是/。所以使用路徑分隔符時都會採用File類的一個常量
public static final String separator
來描述。
- 在Java中要進行文件的處理操作是要通過本地操作系統支持的,在這之中如果操作的是同名文件,就可能出現延遲的問題(開發之中儘可能避免文件重名問題)。
目錄操作
方法 | 說明 |
public boolean mkdir() | 創建一個空目錄 |
public boolean mkdirs() | 創建目錄(無論有多少級父目錄,都會創建) |
public String getParent() | 取得父目錄 |
public File getParentFile() | 取得父File對象 |
代碼演示:
import org.junit.Assert;
import org.junit.Test;
import java.io.File;
public class FileTest {
@Test
public void mkdirTest() {
File file = new File("D:\\test.txt");
Assert.assertTrue(file.mkdir());
}
@Test
public void mkdirsTest() {
File file = new File("D:\\a\\b\\c\\test.txt");
Assert.assertTrue(file.mkdirs());
}
@Test
public void getParentTest() {
File file = new File("D:\\test.txt");
Assert.assertEquals(file.getParent(), "D:\\");
}
@Test
public void getParentFileTest() {
File file = new File("D:\\test.txt");
Assert.assertEquals(file.getParentFile(), new File("D:\\"));
}
}
文件屬性操作
方法 | 說明 |
public long length() | 獲取文件大小(字節) |
public long lastModified() | 最後一個修改日期 |
代碼演示:
import java.io.File;
import java.util.Date;
public class FileTest {
public static void main(String[] args) {
File file = new File("D:\\test.txt");
System.out.println("file size: " + file.length());
System.out.println("file last modified date: " + new Date(file.lastModified()));
}
}
其他操作
方法 | 說明 |
public File[] listFiles() | 列出一個目錄下的文件 |
藉助這個方法,我們來實現一個小功能,列舉出一個目錄下的所有文件,包含子目錄下的文件:
import java.io.File;
import java.util.LinkedList;
import java.util.Queue;
public class FileTest {
public static void main(String[] args) {
File file = new File("C:\\Users\\叄拾叄畫生\\Desktop\\test");
Queue<File> queue = new LinkedList<>();
for (File child : file.listFiles()) {
queue.offer(child);
}
while (!queue.isEmpty()) {
File curFile = queue.poll();
System.out.println(curFile);
if (curFile.isDirectory()) {
for (File child : curFile.listFiles()) {
queue.offer(child);
}
}
}
}
}
流
什麼是流?
在Java中所有數據都是使用流讀寫的。流是一組有順序的,有起點和終點的字節集合,是對數據傳輸的總稱或抽象。即數據在兩設備間的傳輸稱爲流。即數據在兩設備間的傳輸稱爲流,流的本質是數據傳輸,根據數據傳輸特性將流抽象爲各種類,方便更直觀的進行數據操作。
- 按照流向分:輸入流、輸出流;
- 按照處理數據的單位分:字節流(8位的字節)、字符流(16位的字節)。
什麼是輸入輸出流?
- 輸入流:輸入就是將數據從各種輸入設備(包括文件、鍵盤等)中讀取到內存中,如鍵盤就是一個標準的輸入設備;
- 輸出流:正好相反,將數據寫入到各種輸出設備(比如文件、顯示器、磁盤等),顯示器就是一個標準的輸出設備。
文件既可以是輸入設備,又可以是輸出設備。
什麼是字節流、字符流?
File類不支持文件內容處理,如果要處理文件內容,必須要通過流的操作模式來完成。
在java.io
包中,流分爲兩種:字節流和字符流。
- 字節流:數據流中最小的數據單元是字節。InputStream、OutputStream;
- 字符流:數據流中最小的數據單元是字符,Java中的字符是Unicode編碼,一個字符佔用兩個字節。Reader、Writer。
Java中IO流的體系結構如圖:
Java IO類圖:
字節流
FileInputStream和FileOutputStream
public class FileInputStream extends InputStream {}
- FileInputStream:從文件系統中的某個文件中獲得輸入字節;
- FileInputStream:用於讀取諸如圖像數據之類的原始字節流。
方法 | 說明 |
FileInputStream(File file) | 通過打開與實際文件的連接創建一個FileInputStream,該文件由文件系統中的File對象file命名 |
FileInputStream(String name) | 通過打開與實際文件的連接來創建一個FileInputStream,該文件由文件系統中的路徑名name命名 |
public class FileOutputStream extends OutputStream {}
- 文件輸出流是用於將數據寫入到輸出流File或一個FileDescriptor。文件是否可用或可能被創建取決於底層平臺;
- 特別是某些平臺允許一次只能打開一個文件來寫入一個FileOutputStream(或其他文件寫入對象)。
方法 | 解釋 |
FileOutputStream(File file) | 創建文件輸出流以寫入由指定的File對象表示的文件 |
FileOutputStream(String name) | 創建文件輸出流以指定的名稱寫入文件 |
代碼演示:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileInputOutputStreamTest {
public static void main(String[] args) throws IOException {
FileInputStream fin = new FileInputStream("C:\\Users\\叄拾叄畫生\\Desktop\\test\\IO流.png");
FileOutputStream fout = new FileOutputStream("C:\\Users\\叄拾叄畫生\\Desktop\\test\\IO流Copy.png");
int len = 0;
byte[] buf = new byte[1024];
while ((len = fin.read(buf)) != -1) {
fout.write(buf, 0, len);
}
fin.close();
fout.close();
}
}
字節緩衝流BufferedInputStream和BufferedOutputStream
爲什麼需要緩衝流?
當我們用read()
讀取文件時,每讀一個字節,訪問一次硬盤,效率很低。文件過大時,操作起來也不是很方便。因此我們需要用到buffer緩存流,當創建buffer對象時,會創建一個緩衝區數組。當我們讀一個文件時,先從硬盤中讀到緩衝區,然後直接從緩衝區輸出即可,效率會更高。
public class BufferedInputStream extends FilterInputStream {}
- BufferedInputStream爲另一個輸入流添加了功能,即緩衝輸入和支持mark和reset方法的功能。當創建BufferedInputStream時,將創建一個內部緩衝區數組;
- 當從流中讀取或跳過字節時,內部緩衝區將根據需要從所包含的輸入流中重新填充,一次有多個字節。mark操作會記住輸入流中的一點,並且reset操作會導致從最近的mark操作之後讀取的所有字節在從包含的輸入流中取出新的字節之前重新讀取。
方法 | 說明 |
BufferedInputStream(InputStream in) | 創建一個BufferedInputStream並保存其參數,輸入流in,供以後使用 |
BufferedInputStream(InputStream in, int size) | 創建BufferedInputStream具有指定緩衝區大小,並保存其參數,輸入流in,供以後使用 |
public class BufferedOutputStream extends FilterOutputStream {}
- 該類實現緩衝輸出流。通過設置這樣的輸出流,應用程序可以向底層輸出流寫入字節,而不必爲寫入的每個字節導致底層系統的調用。
方法 | 說明 |
BufferedOutputStream(OutputStream out) | 創建一個新的緩衝輸出流,以將數據寫入指定的底層輸出流 |
BufferedOutputStream(OutputStream out, int size) | 創建一個新的緩衝輸出流,以便以指定的緩衝區大小將數據寫入指定的底層輸出流 |
代碼演示:
import java.io.*;
public class BufferStreamTest {
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("C:\\Users\\叄拾叄畫生\\Desktop\\test\\Java\\集合類.rar")
);
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("C:\\Users\\叄拾叄畫生\\Desktop\\test\\Java\\集合類Copy.rar")
);
byte[] bytes = new byte[1024];
int len = 0;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
bos.flush();
bos.close();
bis.close();
}
}
字符流
FileReader和FileWriter
public class FileReader extends InputStreamReader {}
- 如果要從文件中讀取內容,可以直接使用FileReader子類;
- FileReader是用於讀取字符流。要讀取原始字節流,請考慮使用FileInputStream。
方法 | 說明 |
FileReader(File file) | 創建一個新的FileReader,給出File讀取 |
FileReader(String fileName) | 創建一個新的FileReader,給定要讀取的文件的名稱 |
public class FileWriter extends OutputStreamWriter {}
- 如果是向文件中寫入內容,應該使用FileWriter子類;
FileWriter
是用於寫入字符流。要編寫原始字節流,請考慮使用FileOutputStream
。
方法 | 說明 |
FileWriter(File file) | 給一個File對象構造一個FileWriter對象 |
FileWriter(String fileName) | 構造一個給定文件名的FileWriter對象 |
代碼演示:
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileReaderWriterTest {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader(
"C:\\Users\\叄拾叄畫生\\Desktop\\test\\Java\\Java從入門到放棄.txt"
);
FileWriter fw = new FileWriter(
"C:\\Users\\叄拾叄畫生\\Desktop\\test\\Java\\Java從入門到放棄copy.txt"
);
int tmp;
while ((tmp = fr.read()) != -1) {
fw.write(tmp);
}
fw.close();
fr.close();
}
}
字符緩衝流BufferedReader和BufferedWriter
爲了提高字符流讀寫的效率,引入了緩衝機制,進行字符批量的讀寫,提高了單個字符讀寫的效率。BufferedReader
用於加快讀取字符的速度,BufferedWriter
用於加快寫入的速度。
BufferedReader
和BufferedWriter
類各擁有8192個字符大小的緩衝區。
- 當
BufferedReader
在讀取文本文件時,會先儘量從文件中讀入字符數據並放滿緩衝區,而之後若使用read()方法,會先從緩衝區中進行讀取。如果緩衝區數據不足,纔會再從文件中讀取; - 使用
BufferedWriter
時,寫入的數據並不會先輸出到目的地,而是先存儲至緩衝區中。如果緩衝區中的數據滿了,纔會一次對目的地進行寫出。
public class BufferedReader extends Reader {}
從字符輸入流讀取文本,緩衝字符,以提供字符,數組和行的高效讀取。
方法 | 說明 |
BufferedReader(Reader in) | 創建使用默認大小的輸入緩衝區的緩衝字符輸入流 |
BufferedReader(Reader in, int sz) | 創建使用指定大小的輸入緩衝區的緩衝字符輸入流 |
public class BufferedWriter extends Writer {}
將文本寫入字符輸出流,緩衝字符,以提供單個字符,數組和字符串的高效寫入。
方法 | 說明 |
BufferedWriter(Writer out) | 創建使用默認大小的輸出緩衝區的緩衝字符輸出流 |
BufferedWriter(Writer out, int sz) | 創建一個新的緩衝字符輸出流,使用給定大小的輸出緩衝區 |
代碼演示:
import java.io.*;
public class BufferedReaderWriterTest {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader(
"C:\\Users\\叄拾叄畫生\\Desktop\\test\\Java\\Java從入門到放棄.txt"
);
BufferedReader br = new BufferedReader(fr);
FileWriter fw = new FileWriter(
"C:\\Users\\叄拾叄畫生\\Desktop\\test\\Java\\Java從入門到放棄copy.txt"
);
BufferedWriter bw = new BufferedWriter(fw);
String content = "";
while ((content = br.readLine()) != null) {
bw.write(content + "\r\n");
}
fr.close();
br.close();
fw.close();
bw.close();
}
}
字節流對比字符流
- 字節流操作的基本單元是字節;字符流操作的基本單元爲Unicode碼元;
- 字節流在操作的時候本身不會用到緩衝區的,是與文件本身直接操作的;而字符流在操作的時候使用到緩衝區的;
- 所有文件的存儲都是字節(byte)的存儲,在磁盤上保留的是字節;
- 在使用字節流操作中,即使沒有關閉資源(close方法),也能輸出;而字符流不使用close方法的話,不會輸出任何內容。
字符字節轉換流
有時候我們需要進行字節流與字符流二者之間的轉換,因爲這是兩種不同的流,所以,在進行轉換的時候我們需要用到OutputStreamWriter
和InputStreamReader
。
InputStreamReader
是Reader的子類,將輸入的字節流轉換爲字符流。
public class InputStreamReader extends Reader {}
InputStreamReader
是從字節流到字符流的橋;它讀取字節,並使用指定的charset將其解碼爲字符;- 它使用的字符集可以由名稱指定,也可以被明確指定,或者可以接受平臺的默認字符集。
方法 | 說明 |
InputStreamReader(InputStream in) | 創建一個使用默認字符集的InputStreamReader |
InputStreamReader(InputStream in, Charset cs) | 創建一個使用指定字符集的InputStreamReader |
代碼演示:
import java.io.*;
public class InputStreamReaderTest {
public static void main(String[] args) {
InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null;
try {
try {
is = new FileInputStream("C:\\Users\\叄拾叄畫生\\Desktop\\test\\Java\\test.txt");
isr = new InputStreamReader(is, "GBK");
br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} finally {
if (br != null) br.close();
if (isr != null) isr.close();
if (is != null) is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
OutputStreamWriter
是Writer的子類,將輸出的字符流轉換爲字節流。
public class OutputStreamWriter extends Writer {}
OutputStreamWriter
是字符流轉換爲字節流的橋樑,將其寫入的字符編碼成使用指定的字節charset;- 它使用的字符集可以由名稱指定,也可以被明確指定,或者可以接受平臺的默認字符集。
方法 | 說明 |
OutputStreamWriter(OutputStream out) | 創建一個使用默認字符編碼的OutputStreamWriter |
OutputStreamWriter(OutputStream out, Charset cs) | 創建一個使用給定字符集的OutputStreamWriter |
代碼演示:
import java.io.*;
public class OutputStreamWriterTest {
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\叄拾叄畫生\\Desktop\\test\\Java\\test.txt");
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(file), "UTF-8")
);
bw.write("Hello, World!");
bw.newLine();
bw.flush();
bw.close();
}
}