Java 輸入輸出流
本小節(jié)將會介紹基本輸入輸出的 Java 標(biāo)準(zhǔn)類,通過本小節(jié)的學(xué)習(xí),你將了解到什么是輸入和輸出,什么是流;輸入輸出流的應(yīng)用場景,File
類的使用,什么是文件,Java 提供的輸入輸出流相關(guān) API 等內(nèi)容。
1. 什么是輸入和輸出(I / O)
1.1 基本概念
輸入/輸出這個概念,對于計算機相關(guān)專業(yè)的同學(xué)并不陌生,在計算中,輸入/輸出(Input / Output
,縮寫為 I / O)是信息處理系統(tǒng)(例如計算機))與外界(可能是人類或其他信息處理系統(tǒng))之間的通信。輸入是系統(tǒng)接收的信號或數(shù)據(jù),輸出是從系統(tǒng)發(fā)送的信號或數(shù)據(jù)。
那么在 Java 中,什么是輸入和輸出呢?要理解這個概念,可將 Java 平臺視作一個系統(tǒng)。Java 平臺是一個孤立的系統(tǒng),系統(tǒng)之外的所有東西都是它的環(huán)境。系統(tǒng)與其環(huán)境之間的交互是一種雙向?qū)υ?。系統(tǒng)要么從其環(huán)境接收消息,要么將其消息傳遞給環(huán)境。當(dāng)系統(tǒng)接收到消息時,將其稱為輸入,與之相反的是輸出。
本小節(jié)將介紹 Java 的基本輸入和輸出,包括從鍵盤讀取文本輸入,將文本輸出到屏幕以及從文件系統(tǒng)讀取/寫入文件。
Java 提供了兩個用于 I / O 的包:較舊的java.io
包(不支持符號鏈接)和較新的java.nio
(“new io”)包,它對java.nio.file
的異常處理進行了改進。
1.2 簡單的 Java 輸出——打印內(nèi)容到屏幕
一直以來,我們都在向屏幕輸出內(nèi)容以驗證我們編寫的代碼邏輯。向屏幕輸出內(nèi)容非常簡單,可以由以下兩種方式來完成:
// 打印 Hello World,不換行
System.out.print("Hello World");
// 打印 Hello Java,并換行
System.out.println("Hello Java");
1.3 簡單的 Java 輸入——從鍵盤輸入
java.util
包下的Scanner
類可用于獲取用戶從鍵盤輸入的內(nèi)容,我們在Java Scanner 類這一小節(jié)已經(jīng)介紹過具體使用,實例如下:
import java.util.Scanner;
/**
* @author colorful@TaleLin
*/
public class ScannerDemo {
public static void main(String[] args) {
// 創(chuàng)建掃描器對象
Scanner scanner = new Scanner(System.in);
System.out.println("請輸入您的姓名:");
// 可以將用戶輸入的內(nèi)容掃描為字符串
String name = scanner.nextLine();
// 打印輸出
System.out.println("你好 ".concat(name).concat(" ,歡迎來到慕課網(wǎng)!"));
// 關(guān)閉掃描器
scanner.close();
}
}
運行結(jié)果:
請輸入您的姓名:
Colorful
你好 Colorful ,歡迎來到慕課網(wǎng)!
2. 什么是流(Stream)
Java 中最基本的輸入/輸入是使用流來完成的。
流是代表數(shù)據(jù)源和數(shù)據(jù)目標(biāo)的對象,怎么理解這句話呢?簡單來說,可以讀取作為數(shù)據(jù)源的流,也可以寫入作為數(shù)據(jù)目標(biāo)的流。Java中的流是長度不確定的有序字節(jié)序列,它是一連串流動的字符,是以先進先出的方式發(fā)送信息的通道。
3. 輸入輸出流的應(yīng)用場景
上面我們已經(jīng)了解了輸入輸出流的基本概念,那么它具體是做什么用的呢?
在web
產(chǎn)品的開發(fā)中,最常開發(fā)的功能就是上傳文件到服務(wù)器了,這個文件的讀寫過程就要用到輸入輸出流。對于計算機中文件的讀寫、復(fù)制和刪除等操作也都要用到輸入輸出流。輸入輸出流可以說是無處不在,下面我們將會介紹 Java 中輸入輸出流相關(guān)的 API
。
4. File 類
在 Java 中,提供了java.io.File
類對文件和目錄進行操作。
File 意思為文件,文件在計算機中非常重要,我們編寫的 word 文檔、PPT 演示文稿、運行游戲的.exe
可執(zhí)行文件以及我們編寫的 Java 源代碼等等都是文件。
4.1 實例化
要實例化File
對象,需要傳入一個文件或目錄的路徑。
File 類提供了如下 4 個構(gòu)造方法:
File(File parent, String child)
:從父抽象路徑名和子路徑名字符串創(chuàng)建新的文件實例;File(String pathName)
:通過將給定的路徑名字符串轉(zhuǎn)換為抽象路徑名,創(chuàng)建一個新的文件實例(最常用);File(String parent, String child)
:從父路徑名字符串和子路徑名字符串創(chuàng)建新的文件實例;File(URI uri)
:通過將給定的文件: URI轉(zhuǎn)換為抽象路徑名,創(chuàng)建一個新的文件實例。
以Windows
系統(tǒng)為例,在桌面下有一個imooc
目錄,該目錄下有一個Hello.java
文件和一個空的images
目錄,截圖如下:

我們可以單擊Windows
的路徑欄,來獲取imooc
目錄的絕對路徑:

有了目錄和文件以及路徑。我們分別實例化兩個File
對象,實例如下:
import java.io.File;
public class FileDemo1 {
public static void main(String[] args) {
// 傳入目錄絕對路徑
File dir = new File("C:\\Users\\Colorful\\Desktop\\imooc\\images");
// 傳入文件絕對路徑
File file = new File("C:\\Users\\Colorful\\Desktop\\imooc\\Hello.java");
// 打印兩個File對象
System.out.println(dir);
System.out.println(file);
}
}
我們可以直接打印File
對象,File
類重寫了toString()
方法,查看 Java 源碼,toString()
方法直接返回了getPath()
實例方法,此方法返回構(gòu)造方法傳入的路徑字符串:

運行結(jié)果:
C:\Users\Colorful\Desktop\imooc\images
C:\Users\Colorful\Desktop\imooc\Hello.java
上面代碼中,使用\\
表示Windows
下的路徑分隔符\
,Linux
和MacOS
下使用正斜杠/
作為路徑分隔符。假設(shè)是同樣的目錄結(jié)構(gòu),在MacOS
和Linux
下是這樣表示的:
File dir = new File("/Users/Colorful/Desktop/imooc/images");
因為Windows
平臺和其他平臺路徑分隔符不同,使用不同平臺的開發(fā)者就難以保證路徑分隔符的統(tǒng)一。
為了保證代碼更好的兼容性,File
類下提供了一個靜態(tài)變量separator
,用于表示當(dāng)前平臺的系統(tǒng)分隔符:
// 根據(jù)當(dāng)前平臺輸出 / 獲取 \
System.out.println(File.separator);
4.2 絕對路徑和相對路徑
在實例化File
對象時,既可以傳入絕對路徑,也可以傳入相對路徑。
絕對路徑是以根目錄開頭的完整的全路徑,上面代碼實例中傳入的是絕對路徑,我們再來看看什么是相對路徑,以及如何傳入相對路徑。
相對路徑指的是當(dāng)前文件所在的路徑引起的跟其它文件(或文件夾)的路徑關(guān)系。聽起來有點繞,我們舉例來說明一下,在imooc
目錄下新建一個FileDemo2.java
文件,此時imooc
目錄的文件目錄樹結(jié)構(gòu)如下:
└── imoooc
├── FileDemo2.java
├── Hello.java
└── images
內(nèi)容如下:
import java.io.File;
import java.io.IOException;
public class FileDemo2 {
public static void main(String[] args) throws IOException {
// 傳入目錄相對路徑
File dir = new File(".\\images");
File imoocDir = new File("..\\imooc");
// 傳入文件相對路徑
File file = new File(".\\Hello.java");
}
}
上面代碼的File
構(gòu)造方法中傳入的就是相對路徑,代碼中的.
表示當(dāng)前目錄,..
表示上級目錄。
Tips: 我們在實例化 File 對象時,不會產(chǎn)生對磁盤的操作,因此即使傳入的文件或目錄不存在,代碼也不會拋出異常。只有當(dāng)調(diào)用 File 對象下的一些方法時,才會對磁盤進行操作。
File 對象下有 3 個表示路徑的實例方法:
String getPath()
:將抽象路徑名轉(zhuǎn)換為路徑名字符串;String getAbsolute()
:返回此抽象路徑名的絕對路徑名字符串;String getCanonicalPath()
:返回此抽象路徑名的規(guī)范路徑名字符串。
我們可以調(diào)用這 3 個方法并打印結(jié)果,實例如下:
import java.io.File;
import java.io.IOException;
public class FileDemo2 {
public static void main(String[] args) throws IOException {
// 傳入目錄相對路徑
File imagesDir = new File(".\\images");
File imoocDir = new File("..\\imooc");
// 傳入文件相對路徑
File file = new File(".\\Hello.java");
System.out.println("-- imagesDir ---");
System.out.println(imagesDir.getPath());
System.out.println(imagesDir.getAbsolutePath());
System.out.println(imagesDir.getCanonicalPath());
System.out.println("-- imoocDir ---");
System.out.println(imoocDir.getPath());
System.out.println(imoocDir.getAbsolutePath());
System.out.println(imoocDir.getCanonicalPath());
System.out.println("-- file ---");
System.out.println(file.getPath());
System.out.println(file.getAbsolutePath());
System.out.println(file.getCanonicalPath());
}
}
運行結(jié)果:
-- imagesDir ---
.\images
C:\Users\Colorful\Desktop\imooc\.\images
C:\Users\Colorful\Desktop\imooc\images
-- imoocDir ---
..\imooc
C:\Users\Colorful\Desktop\imooc\..\imooc
C:\Users\Colorful\Desktop\imooc
-- file ---
.\Hello.java
C:\Users\Colorful\Desktop\imooc\.\Hello.java
C:\Users\Colorful\Desktop\imooc\Hello.java
通過運行結(jié)果可以看出,規(guī)范路徑名就是把.
和..
轉(zhuǎn)換為標(biāo)準(zhǔn)的絕對路徑。
4.3 判斷對象是文件還是目錄
我們可以通過如下兩個方法判斷 File 對象是文件還是目錄:
boolean isFile()
:測試此抽象路徑名表示的文件是否為普通文件;boolean isDirectory()
:測試此抽象路徑名表示的文件是否為目錄。
實例如下:
import java.io.File;
public class FileDemo3 {
public static void printResult(File file) {
// 調(diào)用isFile()方法并接收布爾類型結(jié)果
boolean isFile = file.isFile();
String result1 = isFile ? "是已存在文件" : "不是已存在文件";
// 掉用isDirectory()方法并接收布爾類型而己過
boolean directory = file.isDirectory();
String result2 = directory ? "是已存在目錄" : "不是已存在目錄";
// 打印該file對象是否是已存在文件/目錄的字符串結(jié)果
System.out.print(file);
System.out.print('\t' + result1 + '\t');
System.out.println(result2);
}
public static void main(String[] args) {
// 傳入目錄絕對路徑
File dir = new File("C:\\Users\\Colorful\\Desktop\\imooc\\images");
// 傳入文件絕對路徑
File file = new File("C:\\Users\\Colorful\\Desktop\\imooc\\test.java");
FileDemo3.printResult(dir);
FileDemo3.printResult(file);
}
}
運行結(jié)果:
C:\Users\Colorful\Desktop\imooc\images 不是已存在文件 是已存在目錄
C:\Users\Colorful\Desktop\imooc\test.java 不是已存在文件 不是已存在目錄
代碼中我們封裝了一個靜態(tài)方法printResult()
,此方法打印 File 對象是否是文件/目錄。值得注意的是,我們的磁盤中不存在C:\Users\Colorful\Desktop\imooc\test.java
,因此無論調(diào)用isFile()
方法還是isDirectory()
方法,其返回結(jié)果都為false
。
4.4 創(chuàng)建和刪除目錄
4.4.1 創(chuàng)建目錄
對于一個不存在的目錄,我們可以使用boolean mkdir()
方法創(chuàng)建一個目錄。例如,我們想在imooc
目錄下創(chuàng)建一個codes
目錄,就可以使用該方法編寫一段創(chuàng)建目錄的代碼。
實例如下:
import java.io.File;
public class FileDemo4 {
public static void main(String[] args) {
// 傳入目錄絕對路徑
File dir = new File("C:\\Users\\Colorful\\Desktop\\imooc\\codes");
if (!dir.exists()) {
// 調(diào)用 mkdir() 方法
boolean result = dir.mkdir();
if (result) {
System.out.println("目錄創(chuàng)建成功");
}
}
}
}
代碼中我們調(diào)用了File
對象的boolean exists()
方法,此方法用于測試由此抽象路徑名表示的文件或目錄是否存在。當(dāng)不存在時,我們才去創(chuàng)建目錄。
運行代碼前,imooc
文件目錄樹結(jié)構(gòu)如下:
└── imoooc
├── FileDemo2.java
├── Hello.java
└── images
運行結(jié)果:
目錄創(chuàng)建成功
運行代碼后,imooc
目錄下多了一個codes
目錄,樹結(jié)構(gòu)如下:
└── imoooc
├── FileDemo2.java
├── Hello.java
├── images
└── codes
另外,F(xiàn)ile 類也提供了一個boolean mkdirs()
方法,用來創(chuàng)建由這個抽象路徑名命名的目錄,包括任何必要但不存在的父目錄。實際上是在遞歸執(zhí)行mkdir()
方法。
4.4.2 刪除目錄
如果我們想要刪除剛剛創(chuàng)建的codes
目錄,可以調(diào)用boolean delete()
方法,實例如下:
import java.io.File;
public class FileDemo5 {
public static void main(String[] args) {
// 傳入目錄絕對路徑
File dir = new File("C:\\Users\\Colorful\\Desktop\\imooc\\codes");
if (dir.exists()) {
// 調(diào)用 delete() 方法
boolean deleted = dir.delete();
if (deleted) {
System.out.println("刪除目錄成功");
}
}
}
}
運行代碼前,imooc
文件目錄樹結(jié)構(gòu)如下:
└── imoooc
├── FileDemo2.java
├── Hello.java
├── images
└── codes
運行結(jié)果:
刪除目錄成功
運行代碼后,樹結(jié)構(gòu)如下:
└── imoooc
├── FileDemo2.java
├── Hello.java
└── images
4.5 創(chuàng)建和刪除文件
對于文件類型的File
對象,可以通過boolean createNewFile()
方法創(chuàng)建一個新文件,使用boolean delete()
方法刪除文件。其調(diào)用方法和創(chuàng)建/刪除目錄相同,此處不再贅述。
關(guān)于更多File
對象的操作,可翻閱官方文檔。
5. InputStream 抽象類
5.1 概述
java.io.InputStream
抽象類是 Java 提供的最基本的輸入流,它是所有輸入流的父類。其最常用的抽象方法int read()
簽名如下:
public abstract int read() throws IOException;
這個方法用于讀取輸入流的下一個字節(jié),返回的int
如果為-1
,則表示已經(jīng)讀取到文件末尾。
InputStream
與其子類的 UML 圖如下所示:

5.2 FileInputStream 實現(xiàn)類
我們將以最常用的FileInputStream
實現(xiàn)類為例進行學(xué)習(xí)。其他實現(xiàn)類大同小異,如有需要可翻閱官方文檔。
FileInputStream
就是從文件流中讀取數(shù)據(jù),我們在imooc
目錄下新建一個文本文檔Hello.txt
,并輸入如下內(nèi)容:

讀取Hello.txt
文件中數(shù)據(jù)的實例代碼如下:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamDemo1 {
public static void main(String[] args) throws IOException {
// 實例化文件流
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Colorful\\Desktop\\imooc\\Hello.txt");
for (;;) {
int n = fileInputStream.read();
if (n == -1) {
// read() 方法返回-1 則跳出循環(huán)
break;
}
// 將n強制轉(zhuǎn)換為 char 類型
System.out.print((char) n);
}
// 關(guān)閉文件流
fileInputStream.close();
}
}
運行結(jié)果:
Hello imooc!
如果我們打開了一個文件并進行操作,不要忘記使用close()
方法來及時關(guān)閉。這樣可以讓系統(tǒng)釋放資源。
6. OutputStream 抽象類
6.1 概述
OutPutStream
抽象類是與InputStream
對應(yīng)的最基本的輸出流,它是所有輸出流的父類。其最常用的抽象方法void write(int b)
簽名如下:
public abstract void write(int b) throws IOException;
這個方法用于寫入一個字節(jié)到輸出流。
OutputStream
與其子類的 UML 圖如下所示:

6.2 FileOutputStream 實現(xiàn)類
我們同樣以最常用的FileOutputStream
實現(xiàn)類為例進行學(xué)習(xí)。其他實現(xiàn)類大同小異,如有需要可翻閱官方文檔。
FileOutputStream
就是向文件流中寫入數(shù)據(jù),下面我們向imooc
目錄下的文本文檔Hello.txt
輸入一段字符串HHH
。完整實例如下:
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamDemo1 {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Colorful\\Desktop\\imooc\\Hello.txt");
// 寫入 3 個H字符
fileOutputStream.write(72);
fileOutputStream.write(72);
fileOutputStream.write(72);
fileOutputStream.close();
}
}
運行代碼后,Hello.txt
后面成功寫入了 3 個字符H
。
7. 小結(jié)
通過本小節(jié)的學(xué)習(xí),我們知道了什么是輸入輸出流的概念,輸入輸出流經(jīng)常用于上傳文件到服務(wù)器的場景。想要通過 Java 操作文件和目錄,要學(xué)會使用java.io.File
類,InputStream
和OutputStream
分別是所有輸入流和所有輸出流的父類,FileInputStream
實現(xiàn)了文件流的輸入,FileOutputStream
實現(xiàn)了文件流的輸出。還有很多其它實現(xiàn)類我們沒有介紹到,但使用方法大同小異,希望同學(xué)可以在用到時自行查閱文檔來學(xué)習(xí)。