目錄
一、IO流
1.1 IO流概述及分類
1.2 IO流常用父類
1.3程式書寫
二、位元組流
2.1FileInputStream
2.2FileOutputStream
2.3FileOutputStream追加
2.4拷貝檔案
2.4.1按位元組逐個拷貝
2.4.2位元組數組整體拷貝
2.4.3小數組拷貝
2.4.4緩沖位元組流拷貝
2.5flush和close方法
2.6位元組流操作中文
2.7流的異常處理
2.7.1老版本(1.6版本及以前)
2.7.2新版本(1.7版本)
2.8給圖檔加密
2.9拷貝檔案
2.10錄入資料拷貝到檔案
一、IO流
1.1 IO流概述及分類
IO流是用來處理裝置之間的資料傳輸,Java對資料的操作是通過流的方式,
Java用于操作流的類都在IO包中,流按照流向可以分為輸入流(InputStream)和輸出流(OutputStream),
按照操作類型可以分為:位元組流和字元流,
- 位元組流:位元組流可以操作任何資料,因為在計算機中任何資料都是以位元組的形式存儲的
- 字元流:字元流隻能操作純字元資料,比較友善
1.2 IO流常用父類
- 位元組流抽象父類
- InputStream
- OutputStream
- 字元流抽象父類
- Reader
- Writer
1.3程式書寫
- 使用前,導入IO包中的類
- 使用時,進行IO異常處理
- 使用後,釋放資源
二、位元組流
位元組流主要分為InputStream和OutputStream,因為這兩個是抽象類,我們在使用的時候隻能使用它的子類,
JAVA中針對檔案的讀寫操作設定了一系列的流,
其中主要有FileInputStream、FileOutputStream、FileReader、FileWriter四種最為常用的流,
我們首先學習一下位元組流FileInputStream和FileOutputStream。
2.1FileInputStream
FileInputStream是從檔案系統中的某個檔案中獲得輸入位元組,用于讀取諸如圖像資料之類的原始資料流,
我們一般使用read方法來讀取位元組,read()可以從輸入流中讀取一個資料位元組,并且以int類型傳回,當檔案到末尾時會傳回-1,
我們利用這個特點來讀取一個txt檔案測試一下,
import java.io.FileInputStream;
import java.io.IOException;
public class IOTest {
public static void main(String[] args) throws IOException {
FileInputStream f=new FileInputStream("data.txt");
int ch;
while((ch=f.read())!=-1){
System.out.println(ch);
}
f.close();
}
}
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPB5UeVR0TzEFVPpHOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLzYzN4ITMzkTMwITMwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
這裡有一個小問題,既然read方法每次讀取的是一個位元組,為什麼不用byte類型接收而用int類型,
因為位元組輸入流可以操作任意類型的檔案,比如圖檔音頻等,這些檔案底層都是以二進制形式存儲的,
如果每次讀取傳回的類型都是byte,有可能讀到11111111,這個二進制補碼代表的數字為-1,而程式碰到-1後就會停止讀檔案,
後面的資料就沒有辦法讀到了,是以在讀取的時候用int類型接收,如果是11111111,那麼前面也會補足24個0湊夠4個位元組,然後傳回
這樣就可以保證正常讀取後面的資料了。
2.2FileOutputStream
FileOutputStream是檔案輸出流,是用于将資料寫入File或者FileDescriptor的輸出流。
FileOutputStream用于寫入諸如圖像資料之類的原始位元組的流,我們一般用的是write方法,
void write(byte b[])//将b.length個位元組從指定的byte數組寫入檔案輸出流中
void write(byte b[],int off,int len)//将指定byte數組從偏移量off開始的len個位元組寫入檔案輸出流
void write(int b)//将指定位元組寫入檔案輸出流
我們往txt檔案寫入字元串"java"試一下,
FileOutputStream f=new FileOutputStream("dataOutput.txt");
f.write(106);//字元j(雖然寫入的是一個int數,但是會自動去除前3個八位,變為byte一個位元組)
f.write(97);//字元a
f.write(118);//字元v
f.write(97);//字元a
f.close();
2.3FileOutputStream追加
當我們想保留原來檔案的内容,并在原來内容的基礎上添加一些字元,這時就不能隻用檔案名構造FileOutputStream()了,
因為用檔案名構造FileOutputStream對象,在建立對象的時候如果沒有檔案會幫我們建立檔案,如果有則會将檔案清空再重新寫入,
我們需要添加一個額外的布爾參數append,append如果為true ,則位元組将被寫入檔案的末尾而不是開頭,也就是不會覆寫前面的内容
FileOutputStream(File file, boolean append)//建立檔案輸出流以寫入由指定的 File對象表示的檔案
FileOutputStream(String name, boolean append)//建立檔案輸出流以指定的名稱寫入檔案
我們來測試一下,接着上面的内容"java",繼續再寫入一個"java"。
FileOutputStream f=new FileOutputStream("dataOutput.txt",true);
f.write(106);
f.write(97);
f.write(118);
f.write(97);
f.close();
2.4拷貝檔案
在java檔案流中,拷貝檔案一般有四種方式。
2.4.1按位元組逐個拷貝
FileInputStream fis=new FileInputStream("luffy.jpg");
FileOutputStream fos=new FileOutputStream("luffyCopy.jpg");
int b;
while((b=fis.read())!=-1){
fos.write(b);//逐個位元組拷貝,複制圖檔
}
fis.close();
fos.close();
可以看到圖檔已經拷貝成功了。
但是這種速度非常慢,是逐個位元組進行拷貝的,如果碰到比較大的檔案效率就會很低。
2.4.2位元組數組整體拷貝
如果遇到位元組數比較多的檔案,拷貝的時候時間消耗就會比較大,這裡我們可以用到available方法,
int availabe()//傳回檔案可以讀取的位元組個數
int read(byte[] b)//從輸入流中将最多b.length個位元組的資料讀取到byte數組中,并傳回讀取的有效位元組個數
int read(byte[] b,int off, int len)//從輸出流中将最多len個位元組的資料讀取到byte數組中,開始位置為off,并傳回讀取的有效位元組個數
int write(byte[] b)//将b.length個位元組從指定byte數組寫入檔案輸出流,并傳回寫入的有效位元組個數
int write(byte[] b,int off, int len)//将指定byte數組從偏移量off開始的len個位元組寫入檔案輸出流,并傳回寫入的有效位元組個數
那麼我們看看如何使用available方法拷貝位元組數組,
FileInputStream fis=new FileInputStream("testVideo.avi");
FileOutputStream fos=new FileOutputStream("testVideoCopy.avi");
byte arr[]=new byte[fis.available()];
fis.read(arr);
fos.write(arr);
fis.close();
fos.close();
這種方式相比于一個位元組一個位元組讀取,效率快了很多,但是在開發中這種方式還是不常用的,
因為如果檔案大小過大的話,一次将檔案讀取到程式中,JVM可能會出現記憶體溢出的錯誤,是以在開發中這兩種方法都不常用。
2.4.3小數組拷貝
小數組拷貝就是定義一個容量相對小的數組,讓數組一次無法接收完檔案流中的資料,
然後不斷讀取寫入,讀取寫入直到檔案流中沒有資料了,我們來看看代碼如何實作,
FileInputStream fis=new FileInputStream("testVideo.avi");
FileOutputStream fos=new FileOutputStream("testVideoCopy.avi");
byte arr[]=new byte[1024];//這裡一般定義為1024,也可以自己設定為其他較小的數字(最好為1024的整數倍)
int len;
while((len=fis.read(arr))!=-1){//當檔案流中還有資料時
fos.write(arr,0,len);//寫入讀取進來的有效資料
}
fis.close();
fos.close();
2.4.4緩沖位元組流拷貝
緩沖位元組流就是用到了BufferedInputStream和BufferedOutputStream,其構造方法如下,
BufferedInputStream(InputStream in)//使用輸入流in建立一個BufferedInputStream對象,供以後使用
BufferedInputStream(InputStream in, int size)//使用輸入流in建立BufferedInputStream,具有指定緩沖區大小size
BufferedOutputStream(OutputStream out)//建立一個新的緩沖輸出流,以将資料寫入指定的底層輸出流
BufferedOutputStream(OutputStream out, int size)//建立一個新的緩沖輸出流,以便以指定的緩沖區大小将資料寫入指定的底層輸出流
當建立BufferedInputStream時,将建立一個内部緩沖區數組,大小一般為1024*8個位元組,裝滿後一次重新整理到檔案上,
當從流中讀取或跳過位元組時,内部緩沖區将根據需要從所包含的輸入流中重新填充,一次有多個位元組,
然後我們使用緩沖位元組流對象進行讀寫操作,其功能與前面的讀寫相同,我們看看如何具體使用的,
FileInputStream fis=new FileInputStream("data.txt");
FileOutputStream fos=new FileOutputStream("dataCopy.txt");
BufferedInputStream bis=new BufferedInputStream(fis);
BufferedOutputStream bos=new BufferedOutputStream(fos);
int len;
while((len=bis.read())!=-1){//當檔案流中還有資料時
bos.write(len);//寫入讀取進來的有效資料
}
bis.close();
bos.close();
fis.close();
fos.close();
2.5flush和close方法
- flush()方法
- 用來重新整理緩沖區的,将緩沖區的位元組全部都重新整理到檔案上,重新整理之後可以再次寫出資料
- close()方法
- 用于關閉流釋放資源的,如果是帶緩沖區的流對象使用close方法,會先重新整理緩沖區再關閉流,将緩沖區的位元組全部都重新整理到檔案上,關閉之後不可以寫出資料
2.6位元組流操作中文
位元組流在讀取中文的時候,因為一個中文用兩個位元組表示,位元組流一個位元組一個位元組讀取時有可能會讀到半個中文,造成亂碼,
如果我們用小數組來讀取,那麼怎麼設定小數組的大小也是一個問題,如果大小設定為兩個,那麼兩個中文之間要是有一個标點符号一樣還是會亂碼,
是以我們一般使用字元流讀取中文,在用位元組流讀取中文時可能會出現亂碼的情況,
在位元組流寫出中文時,位元組流直接操作的是位元組,是以寫出中文也必須将字元串轉換為位元組數組再寫出,
比如write("你好".getBytes()),再比如寫回車換行為write("\r\n".getBytes())
2.7流的異常處理
2.7.1老版本(1.6版本及以前)
import java.io.*;
public class IOTest {
public static void main(String[] args) throws IOException {
FileInputStream fis=null;
FileOutputStream fos=null;
try{//try finally語句保證了建立輸出流有異常時,輸入流可以正常關閉
fis=new FileInputStream("data.txt");
fos=new FileOutputStream("dataCopy.txt");
int b;
while((b=fis.read())!=-1){
fos.write(b);
}
}finally {//裡面的try finally嵌套保證fis關閉異常時可以正常關閉fos
try{
if(fis!=null)
fis.close();
}finally {
if(fos!=null)
fos.close();
}
}
}
}
2.7.2新版本(1.7版本)
import java.io.*;
public class IOTest {
public static void main(String[] args) throws IOException {
try(
FileInputStream fis=new FileInputStream("data.txt");
FileOutputStream fos=new FileOutputStream("dataCopy.txt");
){
int b;
while((b=fis.read())!=-1) {
fos.write(b);
}
}
//不用顯示寫close關閉流,在try後的小括号中建立流對象,
//執行完大括号的代碼後,流對象會自動關閉
}
}
注意,小括号中不能建立不具備自動關閉功能的對象(需要實作AutoCloseable接口)。
2.8給圖檔加密
給圖檔加密實作起來比較簡單,将讀出來的位元組通過自己設計的編碼規則對應到另外的一個數,再将結果儲存輸出,
解碼的時候按照編碼規計算出原來的位元組碼,即可得到原來的照片了,我們看看具體如何實作,
import java.io.*;
public class IOTest {
public static void main(String[] args) throws IOException {
BufferedInputStream bis=new BufferedInputStream(new FileInputStream("luffy.jpg"));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("luffyEncrypt.jpg"));
int b;
while((b=bis.read())!=-1){
//異或操作使用兩次就會得到數本身,解碼的時候再異或一次即可得到原位元組碼
bos.write(b^123);//将得到的位元組碼異或123
}
bis.close();
bos.close();
Decode();
}
public static void Decode() throws IOException {
BufferedInputStream bis=new BufferedInputStream(new FileInputStream("luffyEncrypt.jpg"));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("luffyDecode.jpg"));
int b;
while((b=bis.read())!=-1){
bos.write(b^123);//異或123進行解碼
}
bis.close();
bos.close();
}
}
我們看看原圖檔,加密後圖檔和解密後的圖檔(順序為依次從左至右),
2.9拷貝檔案
要求在控制台錄入檔案的路徑,将檔案拷貝到目前項目下,
我們定義一個方法,擷取鍵盤錄入的檔案路徑,并且封裝成File對象傳回,
import java.io.*;
import java.util.Scanner;
public class IOTest {
public static void main(String[] args) throws IOException {
File file = getFile();//擷取檔案
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file.getName()));
int b;
while ((b = bis.read()) != -1) {
bos.write(b);
}
bis.close();
bos.close();
System.out.println("拷貝成功");
}
public static File getFile() {
Scanner scanner = new Scanner(System.in);
System.out.println("請輸入檔案路徑名:");
while (true) {
String fileDir = scanner.nextLine();
File file = new File(fileDir);
if (!file.exists()) {
System.out.println("路徑下不存在該檔案,請重新輸入檔案路徑名:");
} else if (file.isDirectory()) {
System.out.println("輸入的路徑為檔案夾路徑,請重新輸入檔案路徑名:");
} else {
return file;//傳回檔案
}
}
}
}
我們檢查一下目前目錄下有沒有剛剛的ico.ico檔案,可以看到正确拷貝過來了。
2.10錄入資料拷貝到檔案
要求将鍵盤錄入的資料拷貝到目前項目下的data.txt檔案中,鍵盤錄入的資料當遇到quit時退出,
import java.io.*;
import java.util.Scanner;
public class IOTest{
public static void main(String[] args) throws IOException {
Scanner sc=new Scanner(System.in);
FileOutputStream fos=new FileOutputStream("data.txt");
System.out.println("請鍵盤輸入資料,輸入quit結束:");
while(true){
String line=sc.nextLine();
if("quit".equals(line)){
break;
}else{
fos.write(line.getBytes());//字元串寫出必須轉換為位元組數組
fos.write("\r\n".getBytes());//寫入換行
}
}
fos.close();
System.out.println("程式已退出");
}
}
我們檢查一下文本檔案的内容,發現正确寫入了内容,