IO流常規操作
IO流
IO就是輸入輸出,IO裝置在計算機中起著舉足輕重的作用,IO流也就是輸入輸出流,用來互動資料,程式和程式互動,程式也可以和網路等媒介互動。
一、IO流的分類
要分類,肯定得站得不同角度來看這個問題。
- 根據流向劃分,分為輸入流和輸出流
- 根據資料的單位來分劃,分為位元組流和字元流
- 根據功能劃分,分為節點流和包裝流
- 還有很多
二、四大基本流
在java中IO流非常之多,但都繼承於這四大基流,分別為,位元組輸入流
(InputStream),位元組輸出流
(OutputStream),字元輸入流
(Reader)和字元輸出流
(Writer).
它們都是抽象類,不能建立物件,只能建立它們的子類物件,閱讀API發現,它們都有一個共同的方法close方法,用來關閉流,如果不關閉流的話程式會一直引用著檔案。
三、模板操作
- 建立源和目標
- 建立流物件
- 具體的IO操作
- 關閉流
在下面的例子中穿插講述
四、檔案流
檔案流操作是是檔案。分為字元流和位元組流,位元組流操作是所有的二進位制檔案包括視訊啊,圖片啊什麼的。字元流主要解決的還是中文的一些問題,因為中文多個位元組(GBK是2個位元組,UTF-8是3個位元組),所有可能出現讀一半或者寫一半就不讀了或者不寫了,這樣就出現亂碼了。具體用字元流還是位元組流得具體選擇,位元組流是可以操作字元流的,但是反過來就可能出現問題。輸入就是從檔案中讀到程式中來,而輸出就是從程式中輸出到檔案中。在這樣章節記住一句話:讀進來,寫出去。下面操作一下各個流的常用方法,
檔案位元組輸入流(FileInputStream)
int read() 從此輸入流中讀取一個數據位元組。 int read(byte[] b) 從此輸入流中將最多 b.length 個位元組的資料讀入一個 byte 陣列中 int read(byte[] b, int off, int len)從此輸入流中將最多 len 個位元組的資料讀入一個 byte 陣列中
讀取檔案中所有的資料,用到了一個緩衝陣列,也就是每次讀取5個位元組,直到讀完為止有個len返回當前讀了幾個位元組,如果後面沒有位元組了,那麼它就返回-1
//讀取檔案中所有的資料 byte[] buffer = new byte[5];//定義一個緩衝陣列長度為5,每次只能讀取5個位元組 int len = 0;//當前讀取的位元組數,沒有位元組就返回 -1 while((len = in.read(buffer))!= -1 ){ String str = new String(buffer,0,len); System.out.println(str); }
檔案位元組輸出流(FileOutputStream)
1.void write(byte[] b) 將 b.length 個位元組從指定 byte 陣列寫入此檔案輸出流中。 2.void write(byte[] b, int off, int len) 將指定 byte 陣列中從偏移量 off 開始的 len 個位元組寫入此檔案輸出流。 3.void write(int b) 將指定位元組寫入此檔案輸出流。
輸出就是想檔案中寫資料,可以一次寫一個位元組,也可以寫一個數組中的元素,這個比較簡單。可以用一下四個步驟操作一下
public class FileOutputStreamDemo { public static void main(String[] args) throws Exception { //1.建立源 File f = new File("C:/Users/15626/Desktop/test/123.txt"); //2.建立輸出流物件 FileOutputStream out = new FileOutputStream(f); //3.寫操作 out.write(97);//只能一個一個的寫 //將buffer陣列中的資料寫在檔案中 byte[] buffer = "ABCDEF".getBytes(); out.write(buffer); //從索引1開始的3個位元組寫在檔案中 out.write(buffer, 1, 3); //4.關閉資源 out.close(); } }
檔案字元輸入流(FileReader)
字元流的方法是從父類中繼承過來的,也和上面位元組流的方法大同小異
int read() 讀取單個字元。 int read(char[] cbuf, int offset, int length) 將字元讀入陣列中的某一部分。
//檔案字元輸入流 public class FileReaderDemo { public static void main(String[] args) throws IOException { //1.建立源 File src = new File("C:/Users/15626/Desktop/新建資料夾 (2)/test.txt"); //2.建立字元輸入流物件 FileReader in = new FileReader(src); //3.讀操作 char[] buffer = new char[10]; int len = -1; while((len = in.read(buffer)) != -1){ String str = new String(buffer,0,len); System.out.println(str); } //4.關閉流 in.close(); } }
檔案字元輸出流(FileWriter)
void write(char[] cbuf, int off, int len) 寫入字元陣列的某一部分。 void write(int c) 寫入單個字元。 void write(String str, int off, int len) 寫入字串的某一部分。
檔案的拷貝操作
把指定目錄的.java檔案拷貝到指定的目錄
//拷貝檔案:把指定目錄的.java檔案拷貝到指定的目錄
public class FileCopyDemo2 {
public static void main(String []args) throws Exception{
//得到指定目錄的檔案
File srcDir = new File("C:/Users/15626/Desktop/新建資料夾 (2)");
File destDir = new File("C:/Users/15626/Desktop/新建資料夾 (2)/qqq");
//篩選出字尾是.java的檔案儲存在fs這個檔案陣列中
File[] fs = srcDir.listFiles(new FilenameFilter(){
@Override
public boolean accept(File dir, String name){
return new File(dir,name).isFile() && name.endsWith(".java");
}
});
//遍歷這些檔案
for (File srcFile : fs) {
//1.建立目標
File destFile = new File(destDir,srcFile.getName());
//2.建立輸入流和輸出流物件
FileInputStream in = new FileInputStream(srcFile);
FileOutputStream out = new FileOutputStream(destFile);
//3.輸入輸出操作
byte[] buffer = new byte[10];
int len = -1;
while((len = in.read(buffer)) != -1){
out.write(buffer, 0, len);
}
//4.關閉流
in.close();
out.close();
}
}
}
資料夾的拷貝
https://www.cnblogs.com/tfper/p/9855228.html
檔案流正確關閉資源
https://www.cnblogs.com/tfper/p/9833722.html
五、緩衝流
緩衝流是一個包裝流,目的起緩衝作用.操作流的時候,習慣定義一個byte/char陣列. int read():每次都從磁碟檔案中讀取一個位元組. 直接操作磁碟檔案效能極低,為了解決這個問題,我們 定義一個數組作為緩衝區. byte[] buffer = new byte[1024]; 該陣列其實就是一個緩衝區. 一次性從磁碟檔案中讀取1024個位元組. 如此以來,操作磁碟檔案的次數少了,效能得以提升,java提供的預設快取區大小是8192(1024*8),我們一般不用修改大小.
緩衝流也有四種,分別是 BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter.操作方法都和檔案流差不多,我們來看一下緩衝流到底快了多少?
/**
* @author 15626
* 操作字元或者位元組流都應該用緩衝流包裝起來,現在比較一下用緩衝流和不用緩衝流的情況寫,複製一個檔案所花費時間
* 長度的比較
*/
public class BufferCompare {
public static void main(String[] args) throws Exception {
File srcFile = new File("C:/Users/15626/Desktop/File/test.pdf");
File destFile = new File("C:/Users/15626/Desktop/File/copy.pdf");
//test1(srcFile,destFile);//不用緩衝流包裝
test2(srcFile,destFile);//用緩衝流包裝
}
//使用緩衝流,從記憶體每次讀取1024個位元組:15ms
public static void test2(File srcFile, File destFile) throws Exception {
long begin = System.currentTimeMillis();
BufferedInputStream in = new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(destFile));
byte[] buffer = new byte[1024];
int len = -1;
while((len = in.read(buffer)) != -1){
out.write(buffer, 0, len);
}
in.close();
out.close();
System.out.print(System.currentTimeMillis() - begin);
}
//使用位元組流,從磁碟中每次讀取1024個位元組:38ms
public static void test1(File srcFile,File destFile) throws Exception {
long begin = System.currentTimeMillis();
FileInputStream in = new FileInputStream(srcFile);
FileOutputStream out = new FileOutputStream(destFile);
byte[] buffer = new byte[1024];
int len = -1;
while((len = in.read(buffer)) != -1){
out.write(buffer, 0, len);
}
in.close();
out.close();
System.out.print(System.currentTimeMillis() - begin);
}
}
緩衝流的效率很高,所有以後操作字元位元組流都可以用緩衝流包裝起來。
六、記憶體流
記憶體流就是把程式中的資料暫存在記憶體中,在這裡也就是陣列中,或者把記憶體中的資料輸出到其他媒介,記憶體流也是分為位元組流,字元流還有一個字串流(儲存在字串中)。簡單操作一下,熟悉一個方法
import java.io.*;
/**
* @author 15626
* 位元組記憶體流,或者說是位元組陣列流
* 位元組記憶體輸入流 : ByteArrayOutputStream
* 位元組記憶體輸出流 : ByteArrayInputStream
*/
public class ByteArrayStreamDemo {
public static void main(String[] args) throws Exception {
//位元組記憶體輸出流:把程式的內容寫到記憶體中的byte陣列中
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write("ABSAK".getBytes());
//把程式中的資料存在了緩衝陣列中,toByteArray()方法用來獲取緩衝區的資料
byte[] buffer = out.toByteArray();
//位元組記憶體輸入流: 把記憶體中的資料讀到程式中
ByteArrayInputStream in = new ByteArrayInputStream(buffer);
byte[] by = new byte[1024];
int len = -1;
while((len = in.read(by)) != -1){
System.out.print(new String(by,0,len));
}
//關閉流無效,因為本身就在儲存在記憶體中
in.close();
out.close();
}
}
/**
* @author 15626
* 字元記憶體流或者說是字元陣列流
* 字元記憶體輸出流:CharArrayWriter
* 字元記憶體輸入流:CharArrayReader
*/
public class CharArrayReaderWriterDemo {
public static void main(String[] args) throws Exception {
//字元記憶體輸出流 : 從程式把資料儲存到記憶體中的一個緩衝陣列中
CharArrayWriter cWriter = new CharArrayWriter();
cWriter.write("你是天邊最美的雲彩");
//獲取緩衝陣列中的內容,用toCharArray()方法獲取
char[] ch= cWriter.toCharArray();
//字元記憶體輸入流:從記憶體中被資料讀取到程式中來
CharArrayReader sReader = new CharArrayReader(ch);
char[] buffer = new char[1024];
int len = -1;
while((len = sReader.read(buffer)) != -1){
System.out.println(new String(buffer,0,len));
}
//關閉流無效,因為本身就在儲存在記憶體中
sReader.close();
cWriter.close();
}
}
/**
* @author 15626
* 字串的記憶體流,把程式中的資料儲存在一個緩衝字串中
* 字串的輸出流 :StringWriter
* 字串的輸入流:StringReader
*/
public class StringWriterReaderDemo {
public static void main(String[] args) throws Exception {
//字串的輸出流
StringWriter out = new StringWriter();
out.write("你真的想成為一隻帥帥氣氣的一隻小人嗎");
//獲取緩衝區的字串
//System.out.print(out.toString());
//字串的輸入流
StringReader in = new StringReader("海納百川,有容乃大");
char[] buffer = new char[1024];
int len = -1;
while((len = in.read(buffer)) != -1){
System.out.print(new String(buffer,0,len));
}
//關閉流無效,因為本身就在儲存在記憶體中
in.close();
out.close();
}
}
要注意的關閉流無效,但是為了追求完美的格式,防止忘記了其他流的關閉,我還還是自娛自樂的關一下吧!
七、物件流
物件流包括ObjectInputStream和ObjectOutputStream,名字起得很有範,肯定是位元組流
一般用物件流進行序列化和反序列化通過writeObject方法做序列化操作的,通過readObject方法做反序列化操作
1.序列化
指把堆記憶體中的Java物件資料,通過某種方式把物件儲存到磁碟檔案中或者傳遞給其他網路的節點(在網路上傳輸). 我們把這個過程稱之為序列化.
需要做序列化的物件的類,必須實現序列化介面:java.io.Serializable介面(標誌介面[沒有抽象方法]). 底層會判斷,如果當前物件是Serializable的例項,才允許做序列化.大多數類都已經實現了這個介面了
2.反序列化
把磁碟檔案中的物件資料或者把網路節點上的物件資料,恢復成Java物件的過程.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* @author 15626
* 物件流的序列化和反序列化操作
* ObjectInputStream 用WriterObjcet 來做序列化
* ObjectOutputStream 用ReaderObject 來做反序列化
* 需要做序列化的類必須實現Serializable介面
*/
public class ObjectStreamDemo {
public static void main(String[] args) throws Exception {
File f = new File("file/111.txt");
writeObject(f);//序列化
readObject(f);//反序列化
}
public static void readObject(File f) throws Exception{
ObjectInputStream in = new ObjectInputStream(new FileInputStream(f));
Student s = (Student)in.readObject();
System.out.println(s);
in.close();
}
public static void writeObject(File f) throws Exception {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(new Student("小夥子",19,"12345"));
out.close();
}
}
class Student implements Serializable {
/**
* 做反序列化操作必須存在序列化的位元組碼檔案,由於隨著版本升級肯定位元組碼會發生改變,java提供了序列化版本ID,
* 可以固定這一份位元組碼檔案,解決了這個問題
*/
private static final long serialVersionUID = 1L;
private String name;
private int age;
//密碼一般不會做序列化的,靜態的欄位和瞬態的欄位不能做序列化,所以給密碼加上瞬態transient修飾符
//反序列化的結果就為null
transient private String password;
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", password=" + password + "]";
}
public Student(String name, int age, String password) {
super();
this.name = name;
this.age = age;
this.password = password;
}
}
可以看到裡面有兩個重要的問題,一個是反序列化物件需要物件的位元組碼檔案,需要提供序列化ID固定這份位元組碼檔案,還有一個是transient瞬息欄位是不能序列化.
八、列印流
1.位元組列印流
import java.io.File;
import java.io.PrintStream;
/**
* @author 15626
* 位元組列印流
* 可以自動重新整理的,不想API說的那樣,必須用換行符或者println才可以重新整理緩衝區
*/
public class PrintStreamDemo {
public static void main(String[] args) throws Exception {
File f = new File("file/123.txt");
PrintStream p = new PrintStream(f);
p.print("aa");
p.println(121);
p.append("a");
p.close();
// System.out.println(); //這行程式碼等價於下面的兩行程式碼
// PrintStream ps = System.out;
// ps.println("a");
}
}
2.字元列印流
/**
* @author 15626
* 字元列印流
* 當啟用自動重新整理後,即new PrintWriter(new FileOutputStream(f),true); true
* 呼叫println方法或者printf或者\n後進行重新整理。
*
* 當不啟動自動重新整理,需要手動重新整理,呼叫flush方法,或者close方法會進行重新整理
*/
public class PrintWriterDemo {
public static void main(String[]args) throws Exception{
File f = new File("file/123.txt");
PrintWriter p = new PrintWriter(new FileOutputStream(f),true);
p.print("wqe");
p.println();
p.close();
}
}