java io 流 輸入輸出 大牛經典總結(轉載)
轉載出處:https://www.cnblogs.com/shaohz2014/p/3652548.html
java io 流 輸入輸出 大牛經典總結
在軟體開發中,資料流和資料庫操作佔據了一個很重要的位置,所以,熟悉操作資料流和資料庫,對於每一個開發者來說都是很重要的,今天就來總結一下I/O,資料庫操作
一:從資料流開始
首先先有一個結構圖看一下整個資料流中的API結構和物件繼承關係資訊:
其他常用與流有關的物件:
首先從字元流開始
1、字元流的由來:
因為檔案編碼的不同,而有了對字元進行高效操作的字元流物件。
原理:其實就是基於位元組流讀取位元組時,去查了指定的碼錶。
位元組流和字元流的區別:
1,位元組流讀取的時候,讀到一個位元組就返回一個位元組。字元流使用了位元組流讀到一個或多個位元組(中文對應的位元組數是兩個,UTF-8碼錶中是3個位元組)時。先去查指定的編碼表,將查到的字元返回。
2,位元組流可以處理所有型別資料,如圖片,mp3,avi。而字元流只能處理字元資料。
結論:只要是處理純文字資料,就要優先考慮使用字元流。除此之外都用
節流。
基本的讀寫操作方式:
因為資料通常都以檔案形式存在。
所以就要找到IO體系中可以用於操作檔案的流物件。
通過名稱可以更容易獲取該物件。
因為IO體系中的子類名字尾絕大部分是父類名稱。而字首都是體現子類功能的名字。
Reader
|--InputStreamReader
|--FileReader:專門用於處理檔案的字元讀取流物件。
Writer
|--OutputStreamWriter
|--FileWriter:專門用於處理檔案的字元寫入流物件。
Reader中的常見的方法:
1,int read():讀取一個字元。返回的是讀到的那個字元。如果讀到流的末尾,返回-1.
2,int read(char[]):將讀到的字元存入指定的陣列中,返回的是讀到的字元個數,也就是往數組裡裝的元素的個數。如果讀到流的末尾,返回-1.
3,close():讀取字元其實用的是window系統的功能,就希望使用完畢後,進行資源的釋放
Writer中的常見的方法:
1,write(ch): 將一個字元寫入到流中。
2,write(char[]): 將一個字元陣列寫入到流中。
3,write(String): 將一個字串寫入到流中。
4,flush():重新整理流,將流中的資料重新整理到目的地中,流還存在。
5,close():關閉資源:在關閉前會先呼叫flush(),重新整理流中的資料去目的地。然流關閉。
FileWriter:該類沒有特有的方法只有自己的建構函式。該類特點在於
1,用於處理文字檔案。
2,該類中有預設的編碼表,
3,該類中有臨時緩衝。
建構函式:在寫入流物件初始化時,必須要有一個儲存資料的目的地。
對於讀取或者寫入流物件的建構函式,以及讀寫方法,還有重新整理關閉功能都會丟擲IOException或其子類。所以都要進行處理。或者throws丟擲,或者try catch處理
另一個小細節:
當指定絕對路徑時,定義目錄分隔符有兩種方式:
1,反斜線但是一定要寫兩個。\\new FileWriter("c:\\demo.txt");
2,斜線/ 寫一個即可。new FileWriter("c:/demo.txt");
一個讀取文字檔案的經典例子:
[java] view plaincopyprint?
- <span style="color:#000000;">FileReader fr = null;
- try
- {
- fr = new FileReader("demo.txt");
- char[] buf = newchar[1024];//該長度通常都是1024的整數倍。
- int len = 0;
- while((len=fr.read(buf))!=-1)
- {
- System.out.println(new String(buf,0,len));
- }
- }
- catch(IOException e)
- {
- System.out.println(e.toString());
- }
- </span>
字元流的緩衝區:緩衝區的出現提高了對流的操作效率。
原理:其實就是將陣列進行封裝。
對應的物件:
BufferedWriter:特有方法:newLine():跨平臺的換行符。
BufferedReader:特有方法:readLine():一次讀一行,到行標記時,將行標記之前的字元資料作為字串返回。當讀到末尾時,返回null。
在使用緩衝區物件時,要明確,緩衝的存在是為了增強流的功能而存在,
所以在建立緩衝區物件時,要先有流物件存在。
其實緩衝內部就是在使用流物件的方法,只不過加入了陣列對資料進行了臨時儲存。為了提高操作資料的效率。
程式碼上的體現:
寫入緩衝區物件。
//建立緩衝區物件必須把流物件作為引數傳遞給緩衝區的建構函式。
BufferedWriter bufw = new BufferedWriter(new FileWriter("buf.txt"));
bufw.write("abce");//將資料寫入到了緩衝區。
bufw.flush();//對緩衝區的資料進行重新整理。將資料刷到目的地中。
bufw.close();//關閉緩衝區,其實關閉的是被包裝在內部的流物件。
讀取緩衝區物件。
BufferedReader bufr = new BufferedReader(new FileReader("buf.txt"));
String line = null;
//按照行的形式取出資料。取出的每一個行資料不包含回車符。
while((line=bufr.readLine())!=null)
{
System.out.println(line);
}
bufr.close();
readLine():方法的原理:
其實緩衝區中的該方法,用的還是與緩衝區關聯的流物件的read方法。只不過,每一次讀到一個字元,先不進行具體操作,先進行臨時儲存。當讀取到回車標記時,將臨時容器中儲存的資料一次性返回。
既然明確了原理,我們也可以實現一個類似功能的方法。
[java] view plaincopyprint?
- class MyBufferedReader
- {
- private Reader r;
- MyBufferedReader(Reader r)
- {
- this.r = r;
- }
- public String myReadLine()throws IOException
- {//1,建立臨時容器。
- StringBuilder sb = new StringBuilder();
- //2,迴圈的使用read方法不斷讀取字元。
- int ch = 0;
- while((ch=r.read())!=-1)
- {
- if(ch=='\r')
- continue;
- if(ch=='\n')
- return sb.toString();
- else
- sb.append((char)ch);
- }
- if(sb.length()!=0)
- return sb.toString();
- returnnull;
- }
- publicvoid myClose()throws IOException
- {
- r.close();
- }
- }
然後說一下位元組流:
抽象基類:InputStream,OutputStream。
位元組流可以操作任何資料。
注意:字元流使用的陣列是字元陣列。char [] chs位元組流使用的陣列是位元組陣列。byte [] bt
FileOutputStream fos = new FileOutputStream("a.txt");
fos.write("abcde");//直接將資料寫入到了目的地。
fos.close();//只關閉資源。
FileInputStream fis = new FileInputStream("a.txt");
//fis.available();//獲取關聯的檔案的位元組數。
//如果檔案體積不是很大。
//可以這樣操作。
byte[] buf = new byte[fis.available()];//建立一個剛剛好的緩衝區。
//但是這有一個弊端,就是檔案過大,大小超出jvm的內容空間時,會記憶體溢位。
fis.read(buf);
一個小問題:
位元組流的read()方法讀取一個位元組。為什麼返回的不是byte型別,而是int型別呢?
因為read方法讀到末尾時返回的是-1,而在所操作的資料中的很容易出現連續多個1的情況,而連續讀到8個1,就是-1,導致讀取會提前停止。所以將讀到的一個位元組給提升為一個int型別的數值,但是隻保留原位元組,並在剩餘二進位制位補0.
對於write方法,可以一次寫入一個位元組,但接收的是一個int型別數值。只寫入該int型別的數值的最低一個位元組(8位)。
簡單說:read方法對讀到的資料進行提升。write對操作的資料進行轉換。這是神馬意思???
轉換流:
特點:
1,是位元組流和字元流之間的橋樑。
2,該流物件中可以對讀取到的位元組資料進行指定編碼表的編碼轉換。
什麼時候使用呢?
1,當位元組和字元之間有轉換動作時。
2,流操作的資料需要進行編碼表的指定時。
具體的物件體現:
1,InputStreamReader:位元組到字元的橋樑。
2,OutputStreamWriter:字元到位元組的橋樑。
這兩個流物件是字元流體系中的成員。
那麼它們有轉換作用,而本身又是字元流。所以在構造的時候,需要傳入位元組流物件進來。
建構函式:
InputStreamReader(InputStream):通過該建構函式初始化,使用的是本系統預設的編碼表GBK。
InputStreamReader(InputStream,String charSet):通過該建構函式初始化,可以指定編碼表。
OutputStreamWriter(OutputStream):通過該建構函式初始化,使用的是本系統預設的編碼表GBK。
OutputStreamWriter(OutputStream,String charSet):通過該建構函式初始化,可以指定編碼表。
可以和流相關聯的集合物件Properties.
Map
|--Hashtable
|--Properties
Properties:該集合不需要泛型,因為該集合中的鍵值對都是String型別。
1,存入鍵值對:setProperty(key,value);
2,獲取指定鍵對應的值:value getProperty(key);
3,獲取集合中所有鍵元素:
Enumeration propertyNames();
在jdk1.6版本給該類提供一個新的方法。
Set<String> stringPropertyNames();
4,列出該集合中的所有鍵值對,可以通過引數列印流指定列出到的目的地。
list(PrintStream);
list(PrintWriter);
例:list(System.out):將集合中的鍵值對列印到控制檯。
list(new PrintStream("prop.txt")):將集合中的鍵值對儲存到prop.txt檔案中。
5,可以將流中的規則資料載入進行集合,並稱為鍵值對。
load(InputStream):
jdk1.6版本。提供了新的方法。
load(Reader):
注意:流中的資料要是"鍵=值" 的規則資料。
6,可以將集合中的資料進行指定目的的儲存。
store(OutputStram,String comment)方法。
jdk1.6版本。提供了新的方法。
store(Writer ,String comment):
使用該方法儲存時,會帶著當時儲存的時間。
File類:
該類的出現是對檔案系統的中的檔案以及資料夾進行物件的封裝。
可以通過物件的思想來操作檔案以及資料夾。
1,建構函式:
File(String filename):將一個字串路徑(相對或者絕對)封裝成File物件,該路徑是可存在的,也可以是不存在。
File(String parent,String child);
File(File parent,String child);
2,特別的欄位:separator:跨平臺的目錄分隔符。
如:File file = new File("c:"+File.separator+"abc"+File.separator+"a.txt");
3,常見方法:
1,建立:
boolean createNewFile()throws IOException:建立檔案,如果被建立的檔案已經存在,則不建立。
boolean mkdir(): 建立資料夾。
boolean mkdirs(): 建立多級資料夾。
2,刪除:
boolean delete():可用於刪除檔案或者資料夾。
注意:對於資料夾只能刪除不帶內容的空資料夾,
對於帶有內容的資料夾,不可以直接刪除,必須要從裡往外刪除。
void deleteOnExit(): 刪除動作交給系統完成。無論是否反生異常,系統在退出時執行刪除動作。
3,判斷:
boolean canExecute():
boolean canWrite():
boolean canRead();
boolean exists():判斷檔案或者資料夾是否存在。
boolean isFile(): 判斷File物件中封裝的是否是檔案。
boolean isDirectory():判斷File物件中封裝的是否是資料夾。
boolean isHidden():判斷檔案或者資料夾是否隱藏。在獲取硬碟檔案或者資料夾時,
對於系統目錄中的檔案,java是無法訪問的,所以在遍歷,可以避免遍歷隱藏檔案。
4,獲取:
getName():獲取檔案或者資料夾的名稱。
getPath():File物件中封裝的路徑是什麼,獲取的就是什麼。
getAbsolutePath():無論File物件中封裝的路徑是什麼,獲取的都是絕對路徑。
getParent(): 獲取File物件封裝檔案或者資料夾的父目錄。
注意:如果封裝的是相對路徑,那麼返回的是null.
long length():獲取檔案大小。
longlastModified():獲取檔案或者檔案最後一次修改的時間。
static File[] listRoots():獲取的是被系統中有效的碟符。
String[] list():獲取指定目錄下當前的檔案以及資料夾名稱。
String[] list(Filenamefilter): 可以根據指定的過濾器,過濾後的檔案及資料夾名稱。
File[] listFiles():獲取指定目錄下的檔案以及資料夾物件。
5,重新命名:
renameTo(File):
File f1 = new File("c:\\a.txt");
File f2 = new File("c:\\b.txt");
f1.renameTo(f2);//將c盤下的a.txt檔案改名為b.txt檔案。
物件的序列化。
ObjectInputStream
ObjectOutputStream
可以通過這兩個流物件直接操作已有物件並將物件進行本地持久化儲存。
儲存後的物件可以進行網路傳輸。
Serializable:該介面其實就是一個沒有方法的標記介面。
用於給類指定一個UID。該UID是通過類中的可序列化成員的數字簽名運算出來的一個long型的值。
只要是這些成員沒有變化,那麼該值每次運算都一樣。
該值用於判斷被序列化的物件和類檔案是否相容。
如果被序列化的物件需要被不同的類版本所相容。可以在類中自定義UID。
定義方式:static final long serialVersionUID = 42L;
注意:對應靜態的成員變數,不會被序列化。
對應非靜態也不想被序列化的成員而言,可以通過transient關鍵字修飾。
通常,這兩個物件成對使用。
————————————————————————————————————
其他的資料操作流
操作基本資料型別的流物件。
DataInputStream
DataInputStream(InputStream);
操作基本資料型別的方法:
int readInt():一次讀取四個位元組,並將其轉成int值。
boolean readBoolean():一次讀取一個位元組。
short readShort();
long readLong();
剩下的資料型別一樣。
String readUTF():按照utf-8修改版讀取字元。注意,它只能讀writeUTF()寫入的字元資料。
DataOutputStream
DataOutputStream(OutputStream):
操作基本資料型別的方法:
writeInt(int):一次寫入四個位元組。
注意和write(int)不同。write(int)只將該整數的最低一個8位寫入。剩餘三個8位丟棄。
writeBoolean(boolean);
writeShort(short);
writeLong(long);
剩下是資料型別也也一樣。
writeUTF(String):按照utf-8修改版將字元資料進行儲存。只能通過readUTF讀取。
通常只要操作基本資料型別的資料。就需要通過DataStram進行包裝。
通常成對使用。
————————————————————————————————————
運算元組的流物件。
1,操作位元組陣列
ByteArrayInputStream
ByteArrayOutputStream
toByteArray();
toString();
writeTo(OutputStream);
2,操作字元陣列。
CharArrayReader
CharArrayWriter
對於這些流,源是記憶體。目的也是記憶體。
而且這些流並未呼叫系統資源。使用的就是記憶體中的陣列。
所以這些在使用的時候不需要close。
運算元組的讀取流在構造時,必須要明確一個數據源。所以要傳入相對應的陣列。
對於運算元組的寫入流,在建構函式可以使用空引數。因為它內建了一個可變長度陣列作為緩衝區。
生活不止眼前的苟且,還有詩和遠方。。。