IO流總結
IO(Input Output)流
l IO流用來處理設備之間的數據傳輸
l Java對數據的操作是通過流的方式
l Java用於操作流的對象都在IO包中
l 流按操作數據分爲兩種:字節流與字符流。
l 流按流向分爲:輸入流,輸出流。
輸入流和輸出流的流向的理解?
流就是處理數據的一種方式或者一種手段,或者理解爲一種數據流。
從硬盤已有的數據讀取出來放內存裏面的這個過程就是輸入流。
把內存中的數據存儲到硬盤中的這個過程就是輸出流。
簡單理解就是:以內存爲中心。
什麼時候使用流對象?
操作設備上的數據或操作文件的時候可以使用。
字符流
字符流的抽象基類:Reader , Writer。
字符流的理解,由來和作用?
由於很多國家的文字融入進來,比如說中文在編碼表中默認佔2個字節。而爲了按照文字的單位來處理,所以出現了字符流。
由來:早期的字節流+編碼表,爲了更便於操作文字數據。
作用:爲了處理文字數據。
複製文件的原理和代碼。
原理:
首先用一個讀取流對象和一個文件進行關聯,然後用一個寫入流對象作爲目地的,
爲了把讀取流中的文件傳輸到目的地流對象中,我們就提供了一個字符數組,
爲了關聯這個數組,所以讀取流對象有一個read()方法與這個字符數組進行關聯,
同理,寫入流對象也有一個write()方法與這個字符數組進行關聯,
這樣2個流對象就相連接了,而這個字符數組就相當於一箇中轉站。
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/*
* 對文本文件進行復制。將c盤的文件複製到d盤中。
* 原理:其實就是一個最簡單的讀寫過程。
* 從c盤源,讀取數據,並將讀到的數據,寫入到目的d盤中。
*/
public class CopyTextFileTest {
public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
try {
//1,創建一個字符讀取流讀取與源數據相關聯。
fr = new FileReader("demo.txt");
//2,創建一個存儲數據的目的地。
fw = new FileWriter("copyDemo.txt");
//3,創建一個字符數組將讀取流對象和寫入流對象相連接。
char[] buf = new char[1024];
//4,每次讀取的長度不一樣,所以定義一個變量.
int len = 0;
//5,用循環讀取文件中的數據
while((len= fr.read(buf)) != -1) //判斷是否讀取完沒
fw.write(buf,0,len); //爲了只讀取有效的數據
} catch (Exception e) {
}finally{
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
讀取字符流對象的兩種方式
第一種讀取方式 一次讀一個字符
//1,創建一個文件讀取流對象,和指定名稱的文件相關聯
//2,要保證該文件時已經存在的。如果不存在,會發生異常。FileNotFoundException
FileReader fr = new FileReader("Demo.txt");
//3,調用讀取流的方法,read方法一次讀一個字符,而且會自動往下讀。
int line=0; //read返回時int型的數,即返回的是字符的ascII表對應的數字
while ((line=fr.read())!=-1)
{
sop((char)ch);
}
第二種讀取方式
//1,創建一個文件讀取流對象,和指定名稱的文件相關聯
//2,要保證該文件時已經存在的。如果不存在,會發生異常。FileNotFoundException
FileReader fr = new FileReader("Demo.txt"); //把Demo.txt中的數據讀到控制檯
//3,定義一個字符數組,用於存儲獨到的字符該read(char[]) //返回的是讀到字符的個數
char[] buf = new char[1024];
int len=0;
while((len=fr.read(buf))!=-1) //把讀到的字符暫時存到buf數組中
{
sop("num="+num+"...."+new String(buf,0,len));
}
緩衝區的基本思想?提高效率的原理。
緩衝區的基本思想就是對要處理的數據進行臨時存儲。譬如購物車以及籃子。
原理:
減少頻繁的操作。
給讀取流對象和寫入流對象提供中轉站。
相對於來回跑的麻煩,利用緩衝區的容量,可以一邊先存儲,滿了後再寫入的方式,這樣就提高了效率。
BufferedReader ,BufferedWriter.高效的體現?
內部將數組進行封裝。
變成對象後,方便於對緩衝區的操作。提高效率。
並提供了對文本便捷操作的方法。
readLine
newLine。-----只有BufferedWriter裏有
自定義緩衝區,MyBufferedReader
import java.io.IOException;
import java.io.Reader;
/*
* 模擬一個緩衝區
* 基於已有的緩衝區思想,我們可以從源讀取用read方法。
* 我們的緩衝區,應該是一個更高效的read讀取方法。
*/
public class MyBufferTest extends Reader{
private Reader r;
private char[] buf = new char[1024];
private int count = 0,pos = 0;
public MyBufferTest(Reader r){
this.r = r;
}
/**
* 一次從緩衝區中取一個
* @return 返回一個緩衝區中的字符
* @throws IOException
*/
public int myRead() throws IOException {
//1,首先判斷緩衝區中是否有數據,如果沒有就從源中去拿。
if(count == 0){
count = r.read(buf);
pos = 0;
}
//2,當緩衝區中沒數據了且源中也沒有數據時,count自減1小於0時就返回-1結束.
if(count < 0)
return -1;
//3,如果以上都不滿足,那麼從緩衝區中寫入一個字符到新的文件中。
char ch = buf[pos];
pos++;
count--;
return ch;
}
/**
* 按照文本特點,提供一個特有的操作文本的方法。
* 一次讀取一行文本,只要是到行結束符之前的文本即可。
* @return 返回讀取到的一行文本
* @throws IOException
* 原理:
* 就是從緩衝區中取出數據,並存儲到一個臨時容器中。
* 如果取到了回車符,就將臨時容器中的數據轉成字符串返回。
*/
public String myReadLine() throws IOException{
//1,定義一個臨時容器,進行臨時存儲
StringBuilder sb = new StringBuilder();
//2,定義一個變量,接收讀取到的字符,也就是轉成ask碼錶後的一個int型數字
int ch = 0;
while((ch = myRead()) != -1){
//3,當讀取到\r時,直接跳出本次循環
if(ch == '\r')
continue;
//4,當讀取到\n時,直接跳出當前循環
if(ch == '\n')
return sb.toString();
//5,當都沒有讀取到時,就將這些數據存儲到臨時容器中。
sb.append((char)ch);
}
//6,當臨時容器中的長度不等於0時,就輸出字符。
if(sb.length() != 0)
return sb.toString();
return null;
}
@Override
public void close() throws IOException {
}
@Override
public int read(char[] arg0, int arg1, int arg2) throws IOException {
return 0;
}
}
readLine方法的原理。
就是從緩衝區中獲取數據,並進行臨時存儲,知道讀取到了換行符,
將臨時存儲的數據轉成字符串返回。
它對於操作文本是畢竟方便,可以完成一行一行的讀取文本。
裝飾設計模式,以及和繼承的區別?
對原有類進行了功能的改變,增強。
區別:
1,
繼承在對對象進行增強時,採用的是子類覆蓋父類中的寫方法,
且這些子類使用的功能的原理都一樣,這樣就顯得很臃腫。
而BufferWriter的出現避免了繼承體系關係的臃腫,比繼承更爲靈活。
2,
在爲了增強功能的情況下,相較於繼承,BufferWriter這種方式解決起來更爲方便。
使用字符流可以複製圖片嗎?爲什麼?
不能,因爲字符流就是字節流+編碼表,而用字符流去複製圖片時,字符流會默認將圖片的字節碼格式進行編碼,
所以會導致複製後的圖片與原圖片可能不一致。
字符流只能複製文本
字符流繼承體系簡圖
字節流的抽象基類:InputStream ,OutputStream。
字節流繼承體系簡圖
轉換流InputStreamReader,OutputStreamWriter
轉換流的由來?
爲了方便於字符流與字節流進行轉換,也就是建立一個橋樑。
建立橋樑後,它就將字節流和編碼表進行了封裝,實現了對字符的便捷操作。
另外也爲了方便字符流與字節流之間的操作。
轉換流的應用?
字節流中的數據都是字符時,轉成字符流操作更高效。
轉換流的兩個橋樑都是從哪裏到哪裏?
首先將文件通過InputStreamReader的方式將字節數據轉成字符,爲了高效,將其先存儲到緩衝區中。
然後通過OutputStreamWriter將緩衝區中的字符數據轉成字節,最後輸出。
轉換流的另一個功能,編碼的體現?
對操作的文本文件使用指定編碼表進行編碼解碼的操作。
轉換流的子類和轉換流的區別?
區別:
1,
轉換流:字節流+編碼表
轉換流的子類:FileReader&FileWriter:字節流+本地默認碼錶(GBK)。
2,
轉換流可以指定任意碼錶。
而轉換流子類需要構造一個其父類的對象。
標準輸入輸出流
l System類中的字段:in,out。
l 它們各代表了系統標準的輸入和輸出設備。
l 默認輸入設備是鍵盤,輸出設備是顯示器。
l System.in的類型是InputStream.
l System.out的類型是PrintStream是OutputStream的子類FilterOutputStream 的子類.
什麼是標準輸入輸出流?
例:獲取鍵盤錄入數據,然後將數據流向顯示器,那麼顯示器就是目的地。
通過System類的setIn,setOut方法對默認設備進行改變。
• System.setIn(new FileInputStream(“1.txt”));//將源改成文件1.txt。
• System.setOut(new PrintStream(“2.txt”));//將目的改成文件2.txt
因爲是字節流處理的是文本數據,可以轉換成字符流,操作更方便。
BfferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw =
new BufferedWriter(new OutputStreamWriter(System.out));
流的基本應用小節
流是用來處理數據的。
處理數據時,一定要先明確數據源,與數據目的地(數據匯)。
數據源可以是文件,可以是鍵盤。
數據目的地可以是文件、顯示器或者其他設備。
而流只是在幫助數據進行傳輸,並對傳輸的數據進行處理,比如過濾處理.轉換處理等
IO流的操作規律總結:
1,明確體系:
數據源:InputStream ,Reader
數據匯:OutputStream,Writer
2,明確數據:因爲數據分兩種:字節,字符。
數據源:是否是純文本數據呢?
是:Reader
否:InputStream
數據匯:
是:Writer
否:OutputStream
到這裏就可以明確具體要使用哪一個體系了。
剩下的就是要明確使用這個體系中的哪個對象。
3,明確設備:
數據源:
鍵盤:System.in
硬盤:FileXXX
內存:數組。
網絡:socket
數據匯:
控制檯:System.out
硬盤:FileXXX
內存:數組
網絡:socket
4,明確額外功能:
1,需要轉換?是,使用轉換流。InputStreamReader OutputStreamWriter
2,需要高效?是,使用緩衝區。Buffered
3,需要其他?
1, 複製一個文本文件。
1,明確體系:
源:InputStream ,Reader
目的:OutputStream ,Writer
2,明確數據:
源:是純文本嗎?是 Reader
目的;是純文本嗎?是 Writer
3,明確設備:
源:硬盤上的一個文件。 FileReader
目的:硬盤上的一個文件。FileWriter
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
4,需要額外功能嗎?
需要,高效,使用buffer
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
2, 讀取鍵盤錄入,將數據存儲到一個文件中。
1,明確體系:
源:InputStream ,Reader
目的:OutputStream ,Writer
2,明確數據:
源:是純文本嗎?是 Reader
目的;是純文本嗎?是 Writer
3,明確設備:
源:鍵盤,System.in
目的:硬盤,FileWriter
InputStream in = System.in;
FileWriter fw = new FileWriter("a.txt");
4,需要額外功能嗎?
需要,因爲源明確的體系時Reader。可是源的設備是System.in。
所以爲了方便於操作文本數據,將源轉成字符流。需要轉換流。InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
FileWriter fw = new FileWriter("a.txt");
需要高效不?需要。Buffer
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new FileWriter("a.txt"));
3, 讀取一個文本文件,將數據展現在控制檯上。
1,明確體系:
源:InputStream ,Reader
目的:OutputStream ,Writer
2,明確數據:
源:是純文本嗎?是 Reader
目的;是純文本嗎?是 Writer
3,明確設備:
源:硬盤文件,FileReader。
目的:控制檯:System.out。
FileReader fr = new FileReader("a.txt");
OutputStream out = System.out;
4,需要額外功能?
因爲源是文本數據,確定是Writer體系。所以爲了方便操作字符數據,
需要使用字符流,但是目的又是一個字節輸出流。
需要一個轉換流,OutputStreamWriter
FileReader fr = new FileReader("a.txt");
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要高效嗎?需要。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
4, 讀取鍵盤錄入,將數據展現在控制檯上。
1,明確體系:
源:InputStream ,Reader
目的:OutputStream ,Writer
2,明確數據:
源:是純文本嗎?是 Reader
目的;是純文本嗎?是 Writer
3,明確設備:
源:鍵盤:System.in
目的:控制檯:System.out
InputStream in = System.in;
OutputStream out = System.out;
4,需要額外功能嗎?
因爲處理的數據是文本數據,同時確定是字符流體系。
爲方便操作字符數據的可以將源和目的都轉成字符流。使用轉換流。
爲了提高效率,使用Buffer
BufferedReader bufr =new BufferedReader(new InputStreamReader(Systme.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
5, 讀取一個文本文件,將文件按照指定的編碼表UTF-8進行存儲,保存到另一個文件中。
1,明確體系:
源:InputStream ,Reader
目的:OutputStream ,Writer
2,明確數據:
源:是純文本嗎?是 Reader
目的;是純文本嗎?是 Writer
3,明確設備:
源:硬盤:FileReader.
目的:硬盤:FileWriter
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
4,額外功能:
注意:目的中雖然是一個文件,但是需要指定編碼表。
而直接操作文本文件的FileWriter本身內置的是本地默認碼錶。無法明確具體指定碼錶。
這時就需要轉換功能。OutputStreamWriter,而這個轉換流需要接受一個字節輸出流,而且
對應的目的是一個文件。這時就使用字節輸出流中的操作文件的流對象。FileOutputStream.
FileReader fr = new FileReader("a.txt");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("b.txt"),"UTF-8");
需要高效嗎?
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw =
new BufferedWriter(new OutputStreamWriter(new FileOutputStream("b.txt"),"UTF-8"));
File類
File類的作用?
1,用來將文件或者文件夾封裝成對象
2,方便對文件與文件夾的屬性信息進行操作。
3,File對象可以作爲參數傳遞給流的構造函數。
File對象基本使用。瞭解創建,刪除,獲取,判斷等。
創建:
boolean createNewFile():創建一個新的空文件,不過該文件已存在,就不會創建
boolean mkdir():創建指定的目錄
boolean mkdirs():創建指定的目錄已經父目錄
刪除:
boolean delete():刪除文件或目錄
void deleteOnExit():虛擬機結束時,會自動刪除指定的文件或目錄
獲取:
String getAbsolutePath():絕對路徑
String getPath():相對路徑
String getParent():返回此路徑名父目錄的路徑,如果沒有則返回null
String getName():返回此路徑名錶示的文件或目錄的名稱
long length():返回此路徑名錶示的文件的長度
long lastModified():返回此文件最後一次被修改的時間
判斷:
boolean exists():判斷此路徑名錶示的文件或目錄是否存在
boolean isFile():判斷此路徑名錶示的文件或目錄是否是一個標準的文件
boolean isDirectory():判斷此路徑名錶示的文件或目錄是否是一個目錄
File對象的過濾器。
實現FilenameFilter接口,然後覆蓋其accept方法並指定需要過濾的擴展名就哦了。實現此接口的類實例可用於過濾器文件名。
boolean accpet(File dir, String name):當且僅當該名稱應該包含在文件列表中時返回 true;否則返回 false。
遞歸
什麼是遞歸?
函數自己調用自己。
遞歸的使用和注意事項,並舉例。
遞歸就是函數自身直接或間接調用自身。
當一個功能被重複使用,而且使用過程中,參數運算的數據不斷的在發生着變化。
這時可以使用遞歸這種手法來解決問題。
注意事項:
1,必須要定義條件,否則會出現棧內存溢出。
2,要控制遞歸的次數。
例如:當我們傳遞一個參數超出了棧內存的範圍,且這些數據並沒有出棧,所以就會出問題。
遞歸詳解圖
Properties類
Properties類的作用?
主要用於存儲鍵值的map集合中Hashtable的子類。
操作配置文件最方便的對象。
Properties的特點?
1,它沒有泛型。
2,它裏面的鍵和值都是固定的類型,字符串。
3,它自己有特有的存儲和取出的動作。
4,它有和IO流相關聯的方法。
5,它經常用於簡單配置文件的解析。
Properties常見方法的使用?
void list(PrintWriter out):列出屬性值
void store(OutputStream out, String comments):將集合中的鍵值信息進行持久化存儲
void load(Reader reader) :從持久化設備上將指定格式的鍵值信息加載到Properties集合中
Properties&XML應用實例:
IO中的其他功能流對象:
1,打印流:
PrintStream:
特點:
1,構造函數可接收File對象,字符串路徑,字節輸出流。意味着打印目的可以有很多。
2,該對象具備特有的方法 打印方法print println,可以任何類型的數據。
3,特有的print方法可以保持任意類型的數據表現形式的原樣性,將數據輸出到目的地。
而對於OutputStream父類中的write方法,是將數據的最低字節寫出去。
PrintWriter:
特點:
1,當操作的數據是字符時,可以選擇PrintWriter,比PrintStream要方便。
2,它的構造函數可以接收File對象,字符串路徑,字節輸出流,字符輸出流。
3,構造函數中,如果參數是輸出流,那麼可以通過指定另一個參數true完成自動刷新,該true對println方法有效。
什麼時候用?
當需要保證數據表現的原樣性時,就可以使用打印流的打印方法來完成,這樣更爲方便。
保證數據表現形式的原樣性的原理:其實就是將數據變成字符串,再進行寫入操作。
print(int)和 write(int)的區別?
print(int)方法可以保持任意類型的數據表現形式的原樣性,將數據輸出到目的地。
write(int)方法只是將數據的最低字節寫出去。
PrintWriter可以打印的目的地有什麼?
有字節和字符。
PrintWriter在什麼情況下可以自動刷新?
構造函數中,如果參數是輸出流,那麼可以通過指定另一個參數true完成自動刷新,該true對println方法有效。
PrintWriter對象的println自動刷新圖
注意,自動刷新功能只對字節流對象和字符流對象有效。
2,序列流:
SequenceInputStream:
特點:
1,將多個字節讀取流合併成一個讀取流,將多個源合併成一個源,操作起來更方便。
2,需要的枚舉接口可以通過Collections.enumeration(collection);
可以完成什麼功能?
可以將多個字節讀取流合併成一個讀取流,將多個源合併成一個源的功能。
文件切割的原理?
通過讀取流關聯源文件,然後讀取流將一個臨時的數據進行了一個臨時的緩衝,然後通過這個臨時的緩衝區將數據分散到不同的文件當中。
也就意味着一個讀取流對應着多個輸出流。
ArrayList集合如何獲取其對應的枚舉對象?
實例化一個Enumeration對象,實現其內部方法,並且其內部實現過程和迭代器的功能一樣,所以可以使用迭代的方法完美枚舉的功能。
3,對象序列化
ObjectInputStream 和 ObjectOutputStream :對象的序列化和反序列化
實現Serializable接口。
啓用其序列化功能的。
序列化是驗證序列化對象的。
必須添加默認ID號。
ID號的作用:可以對對象的聲明週期進行延長。
關鍵字transient。
對象的序列化和反序列化,怎麼理解的?
理解:序列化是驗證序列化對象的。對象的持久化過程。
也就是將對象寫入設備的方式,當然,也可以完成多個對象才存儲,那麼每個對象都需要有一個順序。
Serializable接口有什麼用?
啓用其序列化功能的。
關於序列化中數字簽名的小細節
數字簽名不一致,也就是Seriable接口相關的類發生了改變,ID號發生了改變
非靜態數據不需要序列化,如何解決?
使用關鍵字transient。
4,隨機訪問文件
RandomAccessFile:
• 隨機訪問文件,自身具備讀寫的方法。
• 通過skipBytes(int x),seek(int x)來達到隨機訪問。
特點:
1,既可以讀取,也可以寫入。
2,內部維護了一個大型的byte數組,通過對數組的操作完成讀取和寫入。
3,可以通過getFilePointer方法獲取指針的位置,還可以通過seek方法設置指針的位置。
4,該對象的內部應該封裝了字節輸入流和字節輸出流。
5,該對象只能操作文件。(侷限性)
完成隨機訪問的原理?
對數組不斷的操作。
通過seek方法操作指針,可以從這個數組中的任意位置上進行讀和寫。
可以完成對數據的修改,但是數據必須有規律。
5,管道流:
需要和多線程技術相結合的流對象。
特點:
1,可以將管道輸出流連接到管道輸入流通信管道。
2,需要和多線程技術相結合的流對象。
6,操作基本數據類型(額外功能的對象)
DataInputStream 和 DataOutputStream
特點:用於操作基本數據類型值的對象。
7,操作字節數組
ByteArrayInputStream 和 ByteArrayOutputStream
8,設備是內存的流對象。
操作字符數組
• CharArrayReader與CharArrayWrite
操作字符串
• StringReader 與 StringWriter
IO中的這些功能流對象在用流操作規律分析時,都在第四步,是否需要額外功能呢?
1,轉換嗎?
一是需要橋樑嗎?
2,高效嗎?
隨機訪問文件 RandomAccessFile
3,序列化嗎?
ObjectInputStream 和 ObjectOutputStream :對象的序列化和反序列化 Serializable接口
4,操作基本數據類型嗎?
DataInputStream 和 DataOutputStream
5, 需要保證數據原樣性?
printWriter
編碼表
編碼表的由來
l 計算機只能識別二進制數據,早期由來是電信號。
l 爲了方便應用計算機,讓它可以識別各個國家的文字。
l 就將各個國家的文字用數字來表示,並一一對應,形成一張表。
l 這就是編碼表。
字符編碼
字符流的出現爲了方便操作字符。
更重要是的加入了編碼轉換。
通過子類轉換流來完成。
• InputStreamReader
• OutputStreamWriter
在兩個對象進行構造的時候可以加入字符集。
OutputStreamWriter osw = new OutputStreamWriter(new FileWriter(“test.txt”));
常見的碼錶有哪些,都有什麼特點?
ASCll:美國標準信息交換碼
用一個字節的7位可以表示
ISO8859-1:拉丁碼錶。歐洲碼錶
用一個字節的8位表示
GB2312:中國的中文編碼表
GBK:中國的中文編碼表升級,融合了更多的中文文字字符號。
Unicode:國際標準碼,融合了多種文字。
所有文字都用兩個字節來表示,Java語言使用的就是unicode
UTF-8:最多用三個字節來表示一個字符
編解碼的原則:編對解錯,編錯。
編碼:字符串-->字節數組 可以理解爲將認識的變成不認識的就是編碼
解碼:字節數組-->字符串 可以理解爲將不認識的變成認識的就是解碼
編碼錯了,就解不出來了。
編碼對了,解錯了,有可能有救。
當涉及向服務端提交中文數據時,服務器是iso8859-1的碼錶,如何獲取正確的中文?
對亂碼進行iso8859-1編碼,在用gbk解碼即可。
聯通是怎麼回事?
聯通的GBK的編碼正好符合了UTF-8的編碼,所以就用了UTF-8的碼錶進行解碼,因此出現了亂碼。
也就是聯通的GBK編碼表和UTF-8重複了。