一胰丁、IO流是什么
慣例引用百科的回答
流是一種抽象概念,它代表了數(shù)據(jù)的無(wú)結(jié)構(gòu)化傳遞喂分。按照流的方式進(jìn)行輸入輸出锦庸,數(shù)據(jù)被當(dāng)成無(wú)結(jié)構(gòu)的字節(jié)序或字符序列。從流中取得數(shù)據(jù)的操作稱(chēng)為提取操作蒲祈,而向流中添加數(shù)據(jù)的操作稱(chēng)為插入操作甘萧。用來(lái)進(jìn)行輸入輸出操作的流就稱(chēng)為IO流。換句話說(shuō)梆掸,IO流就是以流的方式進(jìn)行輸入輸出 [1] .
我對(duì)IO流的理解就是"你的程序和系統(tǒng)之間讀寫(xiě)文件的操作就是IO操作扬卷,和系統(tǒng)之間讀寫(xiě)用的東西就是IO流"。
JAVA IO流就是JAVA程序和操作系統(tǒng)之間通信用的方法酸钦。
二怪得、JAVA IO系統(tǒng)腦圖
給大家看下JAVA IO的腦圖
自己畫(huà)的JAVA的腦圖,如果有需要原文件的去我公眾號(hào),發(fā)送:JAVA IO徒恋,就可以得到腦圖的原文件了蚕断,帶黃色文件夾標(biāo)注的是我自己寫(xiě)的注釋嗷。
什么你還不知道我公眾號(hào)入挣,微信搜索亿乳,千玨(jue),就可以關(guān)注到我了。
三径筏、JAVA IO流詳解
在這里由于篇幅的原因我只講解JAVA IO流中的字節(jié)流和字符流,別的就等以后再寫(xiě)葛假,比如:NIO AIO BIO這些,以后有時(shí)間抽出時(shí)間出來(lái)寫(xiě)一篇滋恬,要是想看的記得點(diǎn)個(gè)關(guān)注哦聊训。
下面進(jìn)入正題:
3.1 字節(jié)流和字符流的區(qū)別
字節(jié)流和字符流操作的本質(zhì)區(qū)別只有一個(gè):字節(jié)流是原生的操作,字符流是經(jīng)過(guò)處理后的操作恢氯。
畫(huà)個(gè)圖魔眨,字節(jié)流在操作時(shí)不會(huì)用到緩沖區(qū),也就是不會(huì)用到內(nèi)存酿雪,文件本身直接操作的遏暴,而字符流在操作時(shí)使用了緩沖區(qū),通過(guò)緩沖區(qū)再操作文件指黎,看下圖:
為什么要有字符流而不直接用字節(jié)流呢朋凉?
我相信有些讀者心里肯定要問(wèn)這個(gè)問(wèn)題,我剛開(kāi)始學(xué)習(xí)的時(shí)候也想過(guò)這個(gè)問(wèn)題醋安,為什么不直接用字節(jié)流解決呢杂彭,還非要搞個(gè)字符流出來(lái)呢。
我的理解就是字節(jié)流處理多個(gè)字節(jié)表示的東西的時(shí)候有可能會(huì)出現(xiàn)亂碼的問(wèn)題吓揪,比如漢字亲怠,用字節(jié)流讀取的時(shí)候有可能因?yàn)橐晃蛔止?jié)沒(méi)有讀到就變成了亂碼,字符流呢就完美解決了這個(gè)問(wèn)題柠辞,字符流你們可以這樣理解团秽,字節(jié)流和編碼表的組合就是字符流。因?yàn)橛辛司幋a表所以可以確定這個(gè)漢字有多少個(gè)字節(jié)叭首,這樣字節(jié)流就可以根據(jù)位數(shù)準(zhǔn)確的讀寫(xiě)漢字了习勤。
以上純?yōu)閭€(gè)人理解,如有不對(duì)的地方請(qǐng)?jiān)谠u(píng)論區(qū)給我留言哦焙格。
3.2 字節(jié)流
字節(jié)流顧名思義就是通過(guò)字節(jié)直接操作字符图毕,更底層一些。
字節(jié)流最基礎(chǔ)的兩個(gè)類(lèi)就是 InputStream
和 OutputStream
,根據(jù)這兩個(gè)派生而來(lái)類(lèi)都含有 read()
和 write()
的基本方法眷唉,用于讀寫(xiě)單個(gè)字節(jié)或者字節(jié)數(shù)組予颤。
3.2.1 InputStream 和 OutputStream類(lèi)
InputStream類(lèi)是一個(gè)抽象類(lèi) ,是所有字節(jié)輸入流類(lèi)的父類(lèi)囤官。
OutputStream類(lèi)是一個(gè)抽象類(lèi),是所有字節(jié)輸出流的父類(lèi)
InputStream的常見(jiàn)子類(lèi)有:
- FileInputStream:看這個(gè)名字就知道用于從文件中讀取信息蛤虐。
- ByteArrayInputStream: 字節(jié)數(shù)組輸入流,
- ObjectInputStream:序列化時(shí)使用 一般和ObjectOutputStream一起使用
- FilterInputStream: 過(guò)濾輸入流,為基礎(chǔ)的輸入流提供一些額外的操作党饮。
OutputStream的常見(jiàn)子類(lèi)有:
- FileOutPutStream: 文件輸出流對(duì)文件進(jìn)行操作
- ByteArrayOutputStream: 字節(jié)數(shù)組輸出流
- ObjectOutputStream: 序列化時(shí)使用 一般和OjbectInputStream一起使用
- FilterOutputStream:過(guò)濾輸出流,為基礎(chǔ)的輸出流提供一些額外的操作。
我們一個(gè)一個(gè)過(guò)要不然怎么能叫一文帶你看懂JAVA IO流了呢笆焰,那樣不就是標(biāo)題黨了嗎[滑稽]劫谅。
3.2.1.1 FileInputStream 和 FileOutPutStream類(lèi)
1) FileInputStream 和 FileOutPutStream概念
FileInputStream是文件字節(jié)輸入流见坑,就是對(duì)文件數(shù)據(jù)以字節(jié)的方式來(lái)處理嚷掠,如音樂(lè)、視頻荞驴、圖片等不皆。
FileOutPutStream是文件字節(jié)輸出流,
2)FileInputStream里面的方法
//通過(guò)文件的名字來(lái)創(chuàng)建一個(gè)對(duì)象
public FileInputStream(String name) throws FileNotFoundException{}
//通過(guò)File對(duì)象來(lái)創(chuàng)建一個(gè)對(duì)象
public FileInputStream(File file) throws FileNotFoundException{}
/**
* 通過(guò)FileDescriptor來(lái)創(chuàng)建一個(gè)對(duì)象
* FileDescriptor是一個(gè)文件描述符號(hào)
* 有in,out,err三種類(lèi)型
* in:標(biāo)準(zhǔn)輸入描述符熊楼,out:標(biāo)準(zhǔn)輸出的描述符霹娄,err:標(biāo)準(zhǔn)錯(cuò)誤輸出的描述號(hào)
*/
public FileInputStream(FileDescriptor fdObj){}
//打開(kāi)指定的文件進(jìn)行讀取 ,是java和c之間進(jìn)行操作的api 我們并不會(huì)用到
private native void open0(String name){}
//打開(kāi)指定的文件進(jìn)行讀取,我們并不會(huì)用到 因?yàn)樵跇?gòu)造方法里面幫我們打開(kāi)了這個(gè)文件
private void open(String name){}
//從輸入流中讀取一個(gè)字節(jié)的數(shù)據(jù)鲫骗,如果到達(dá)文件的末尾則返回-1
public int read() throws IOException{}
//讀取一個(gè)字節(jié)數(shù)組
private native int readBytes(byte b[], int off, int len) throws IOException;
private native int read0() throws IOException;
//從輸入流中讀取b.length的數(shù)據(jù)到b中
public int read(byte b[]) throws IOException{}
//從輸入流中讀取off到len之間的數(shù)據(jù)到b中
public int read(byte b[], int off, int len) throws IOException{}
//跳過(guò)并丟棄輸入流中的n個(gè)數(shù)據(jù)
public long skip(long n) throws IOException{}
private native long skip0(long n) throws IOException;
//可以從此輸入流中讀取的剩余字節(jié)數(shù)
public int available() throws IOException {}
private native int available0() throws IOException;
//關(guān)閉此文件輸入流并釋放與該流關(guān)聯(lián)的所有系統(tǒng)資源
public void close() throws IOException {}
//返回FileDescriptor對(duì)象
public final FileDescriptor getFD() throws IOException{}
//該方法返回與此文件輸入流關(guān)聯(lián)的通道 NIO中會(huì)用到 本文不會(huì)提及
public FileChannel getChannel(){}
private static native void initIDs();
private native void close0() throws IOException;
//沒(méi)有更多引用時(shí)犬耻,調(diào)用此方法來(lái)關(guān)閉輸入流 一般不使用
protected void finalize() throws IOException {}
由于篇幅起見(jiàn)FileOutputStream代碼里面的方法我就不仔細(xì)的帶你們看了(我不會(huì)說(shuō)我是因?yàn)閼胁挪粠銈兛吹模铩?/p>
一般常用的方法就幾個(gè)执泰,舉個(gè)例子枕磁,往D盤(pán)下面hello.txt里面輸入“hello world”
public class Test {
public static void main(String []args) throws IOException {
//根據(jù)文件夾的名字來(lái)創(chuàng)建對(duì)象
FileOutputStream fileOutputStream = new FileOutputStream("D:\\hello.txt");
//往文件里面一個(gè)字節(jié)一個(gè)字節(jié)的寫(xiě)入數(shù)據(jù)
fileOutputStream.write((int)'h');
fileOutputStream.write((int)'e');
fileOutputStream.write((int)'l');
fileOutputStream.write((int)'l');
fileOutputStream.write((int)'o');
String s = " world";
//入文件里面一個(gè)字節(jié)數(shù)組的寫(xiě)入文件
fileOutputStream.write(s.getBytes());
fileOutputStream.close();
//傳文件夾的名字來(lái)創(chuàng)建對(duì)象
FileInputStream fileInputStream = new FileInputStream("D:\\hello.txt");
int by = 0;
//一個(gè)字節(jié)一個(gè)字節(jié)的讀出數(shù)據(jù)
while((by = fileInputStream.read()) != -1){
System.out.println((char)by);
}
//關(guān)閉流
fileInputStream.close();
//通過(guò)File對(duì)象來(lái)創(chuàng)建對(duì)象
fileInputStream = new FileInputStream("new File("D:\\hello.txt")");
byte []bytes = new byte[10];
//一個(gè)字節(jié)數(shù)組的讀出數(shù)據(jù)
while ((by = fileInputStream.read(bytes)) != -1){
for(int i = 0; i< by ; i++){
System.out.print((char) bytes[i]);
}
}
//關(guān)閉流
fileInputStream.close();
}
}
常用的就上述代碼里面的三種方法。
3.2.1.2 ByteArrayInputStream和ByteArrayOutputStream
1)ByteArrayInputStream和ByteArrayOutputStream概念
ByteArrayInputStream是字節(jié)數(shù)組輸入流,它里面包含一個(gè)內(nèi)部的緩沖區(qū)(就是一個(gè)字節(jié)數(shù)組 )术吝,該緩沖區(qū)含有從流中讀取的字節(jié)计济。
ByteArrayOutputStream是字節(jié)數(shù)組輸出流
2)ByteArrayInputStream里面的方法
//通過(guò)byte數(shù)組來(lái)創(chuàng)建對(duì)象
public ByteArrayInputStream(byte buf[]) {}
//通過(guò)byte數(shù)組,并給定開(kāi)始下標(biāo)和結(jié)束下標(biāo)來(lái)創(chuàng)建對(duì)象
public ByteArrayInputStream(byte buf[], int offset, int length){}
//從這個(gè)輸入流讀取下一個(gè)字節(jié) 末尾會(huì)返回
public synchronized int read(){}
//從輸入流中讀取off到len之間的數(shù)據(jù)到b中
public synchronized int read(byte b[], int off, int len){}
//跳過(guò)并丟棄輸入流中的n個(gè)數(shù)據(jù)
public synchronized long skip(long n){}
//可以從此輸入流中讀取的剩余字節(jié)數(shù)
public synchronized int available(){}
//判斷這個(gè)輸入流是否支持標(biāo)記排苍,他一直返回true
public boolean markSupported(){}
//將mark的值設(shè)置為當(dāng)前讀取的下標(biāo)沦寂,readAheadLimit這個(gè)參數(shù)沒(méi)有意義,因?yàn)闆](méi)用到
public void mark(int readAheadLimit){}
//將當(dāng)前的下標(biāo)設(shè)置為mark一般和mark()方法一起使用
public synchronized void reset(){}
//關(guān)閉這個(gè)輸入流,因?yàn)锽yteArrayInputStream操作的是數(shù)組所以沒(méi)有必要關(guān)閉流
public void close() throws IOException{}
由于篇幅起見(jiàn)ByteArrayOutputStream代碼里面的方法我就不仔細(xì)的帶你們看了(我不會(huì)說(shuō)我是因?yàn)閼胁挪粠銈兛吹奶匝茫?/p>
舉個(gè)例子传藏,從一個(gè)字符串讀取數(shù)組
public class Test {
public static void main(String[] args) throws IOException {
//創(chuàng)建一個(gè)字節(jié)輸出流對(duì)象
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
//一個(gè)字節(jié)一個(gè)字節(jié)的寫(xiě)入數(shù)據(jù)
byteArrayOutputStream.write('h');
byteArrayOutputStream.write('e');
byteArrayOutputStream.write('l');
byteArrayOutputStream.write('l');
byteArrayOutputStream.write('o');
//一個(gè)字節(jié)數(shù)組的寫(xiě)入數(shù)據(jù)
byteArrayOutputStream.write(" world".getBytes());
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray();
//從這個(gè)流中讀取數(shù)據(jù)
int b = 0;
//從這個(gè)流中一個(gè)字節(jié)一個(gè)字節(jié)的讀數(shù)據(jù)
while ((b = byteArrayInputStream.read()) != -1) {
System.out.println((char) b);
}
byteArrayInputStream = new ByteArrayInputStream(bytes);
byte[] bs = new byte[10];
//從這個(gè)流中一次性讀取bs.length的數(shù)據(jù)
while ((b = byteArrayInputStream.read(bs)) != -1) {
for (int i = 0; i < b; i++) {
System.out.print((char) bs[i]);
}
System.out.println();
}
}
}
如上代碼所示,我平時(shí)常用的也就這幾個(gè)方法彤守。
3.2.1.3 ObjectInputStream 和ObjectOutpuStream
1)概念
ObjectInputStream是反序列化流漩氨,一般和ObjectOutputStream配合使用。
用ObjectOutputStream將java對(duì)象序列化然后存入文件中遗增,然后用ObjectInputStream讀取出來(lái)
這個(gè)類(lèi)的作用叫惊,我的理解是有些類(lèi)在這個(gè)程序生命周期結(jié)束后,還會(huì)被用到所以要序列化保存起來(lái)
2)ObjectInputStream 和 ObjectOutpuStream 基本方法
常用的其實(shí)就兩個(gè)方法
public final Object readObject(){}
public final void writeObject(Object obj) throws IOException{}
class Data implements Serializable {
private int n;
public Data(int n){
this.n=n;
}
@Override
public String toString(){
return Integer.toString(n);
}
}
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Data w=new Data(2);
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("worm.out"));
//序列化對(duì)象做修,把對(duì)象寫(xiě)到worm.out里面
out.writeObject("Worm storage\n");
//序列化對(duì)象霍狰,把對(duì)象寫(xiě)到worm.out里面
out.writeObject(w);
out.close();
//從worm.out里面讀取對(duì)象
ObjectInputStream in=new ObjectInputStream(new FileInputStream("worm.out"));
//讀取String對(duì)象
String s=(String)in.readObject();
//讀取Data對(duì)象
Data d=(Data)in.readObject();
System.out.println(s+"Data = "+d);
}
}
3.2.1.4 FilterInputStream 和 FilterOutputStream
1) 概念
FilterInputStream和FilteOutputStream分別是過(guò)濾輸入流和過(guò)濾輸出流,他們的作用是為基礎(chǔ)流提供一些額外的功能
2)FilterInputStream 和 FilterOutputStream的常用子類(lèi)
FilterInputStream常用子類(lèi)
- DataInputStream:可以從流中讀取基本數(shù)據(jù)類(lèi)型抡草,與DataOutpuStream配合一起使用
- BufferedInputStream:可以從緩沖區(qū)中讀取數(shù)據(jù),不用每次和文件的操作都進(jìn)行實(shí)際操作了蔗坯。
FilterOutputStream常用子類(lèi)
- DataOutputStream:可以向文件中寫(xiě)入基本類(lèi)型的數(shù)據(jù)
- PrintStream:用于產(chǎn)生格式化的輸出
- BufferedOutputStream:通過(guò)緩沖區(qū)像文件中寫(xiě)入數(shù)據(jù)康震。
DataInputStream基本類(lèi)型寫(xiě)入方法。
// 將一個(gè) byte 值寫(xiě)入流中宾濒。
void writeByte(int v)
// 將一個(gè) short 值寫(xiě)入流中
void writeShort(int v)
//將一個(gè) int 值寫(xiě)入流中
void writeInt(int v)
// 將一個(gè) long 值寫(xiě)入流中
void writeLong(long v)
//使用 Float 類(lèi)中的 floatToIntBits 方法將 float 參數(shù)轉(zhuǎn)換為一個(gè) int 值腿短,然后該int值寫(xiě)入流中
void writeFloat(float v)
//使用Double 類(lèi)中的 doubleToLongBits 方法將 double 參數(shù)轉(zhuǎn)換為一個(gè) long 值,然后將該long寫(xiě)入到流中绘梦。
void writeDouble(double v)
//寫(xiě)入一個(gè)char
void writeChar(int v)
//將一個(gè) boolean 值寫(xiě)入流橘忱。
void writeBoolean(boolean v)
//將字節(jié)數(shù)組寫(xiě)入流中
void write(byte[] b, int off, int len)
//將字符串寫(xiě)出到基礎(chǔ)輸出流中。
oid writeBytes(String s)
//采用UTF-16be方式寫(xiě)入卸奉,也就是java字符串的編碼
void writeChars(String s)
// 以u(píng)tf-8形式寫(xiě)入一個(gè)字符串
void writeUTF(String str)
//清空此數(shù)據(jù)輸出流钝诚,寫(xiě)入文件
void flush()
測(cè)試一下方法試試
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("D:\\hello.txt"));
// 寫(xiě)入byte類(lèi)型數(shù)據(jù)
dataOutputStream.writeByte(20);
// 寫(xiě)入short類(lèi)型數(shù)據(jù)
dataOutputStream.writeShort(30);
// 寫(xiě)入int類(lèi)型
dataOutputStream.writeInt(900);
// 寫(xiě)入float類(lèi)型
dataOutputStream.writeFloat(12.3f);
// 寫(xiě)入long類(lèi)型
dataOutputStream.writeLong(800L);
// 寫(xiě)入double類(lèi)型
dataOutputStream.writeDouble(14.23);
//寫(xiě)入boolean類(lèi)型
dataOutputStream.writeBoolean(true);
// 寫(xiě)入char類(lèi)型
dataOutputStream.writeChar('中');
dataOutputStream.close();
DataInputStream dataInputStream = new DataInputStream(new FileInputStream("D:\\hello.txt"));
System.out.println(dataInputStream.readByte());
System.out.println(dataInputStream.readShort());
System.out.println(dataInputStream.readInt());
System.out.println(dataInputStream.readFloat());
System.out.println(dataInputStream.readLong());
System.out.println(dataInputStream.readDouble());
System.out.println(dataInputStream.readBoolean());
System.out.println(dataInputStream.readChar());
dataInputStream.close();
//創(chuàng)建一個(gè)對(duì)象
PrintStream printStream = new PrintStream("D:\\hello.txt");
//寫(xiě)入一個(gè)字節(jié)數(shù)組
printStream.write("helloworld".getBytes());
//寫(xiě)入一個(gè)換行符號(hào)
printStream.println();
//格式化寫(xiě)入數(shù)據(jù)
printStream.format("文件名稱(chēng):%s","hello.txt");
printStream.println();
printStream.append("abcde" );
printStream.close();
}
}
BufferedInputStream和BufferedOutputStream我另開(kāi)一篇文章寫(xiě),里面要介紹的東西很多榄棵,一篇文章介紹不完凝颇。
emmm,還有字符流下篇文章寫(xiě)疹鳄,今天是完不成了拧略,覺(jué)得這篇文章還可以想看下一篇文章的關(guān)注我呀,或者可以關(guān)注我公眾號(hào):千玨呀瘪弓,后臺(tái)留言給我呀垫蛆,是千玨(jue),不是千鈺杠茬。