java基礎知識---IO流
IO流:★★★★★,用於處理裝置上資料。
流:可以理解資料的流動,就是一個數據流。IO流最終要以物件來體現,物件都存在IO包中。
流也進行分類:
1:輸入流(讀)和輸出流(寫)。
2:因為處理的資料不同,分為位元組流和字元流。
位元組流:處理位元組資料的流物件。裝置上的資料無論是圖片或者dvd,文字,它們都以二進位制儲存的。二進位制的最終都是以一個8位為資料單元進行體現,所以計算機中的最小資料單元就是位元組。意味著,位元組流可以處理裝置上的所有資料,所以位元組流一樣可以處理字元資料。
那麼為什麼要有字元流呢?因為字元每個國家都不一樣,所以涉及到了字元編碼問題,那麼GBK編碼的中文用unicode編碼解析是有問題的,所以需要獲取中文位元組資料的同時+ 指定的編碼表才可以解析正確資料。為了方便於文字的解析,所以將位元組流和編碼表封裝成物件,這個物件就是字元流
注意:流的操作只有兩種:讀和寫。
流的體系因為功能不同,但是有共性內容,不斷抽取,形成繼承體系。該體系一共有四個基類,而且都是抽象類。
位元組流:InputStream OutputStream
字元流:Reader Writer
在這四個系統中,它們的子類,都有一個共性特點:子類名字尾都是父類名,字首名都是這個子類的功能名稱。
--------------------------------------------------------------------------------------------------------------------
public static void main(String[] args)throws IOException { //讀、寫都會發生IO異常
/*
1:建立一個字元輸出流物件,用於操作檔案。該物件一建立,就必須明確資料儲存位置,是一個檔案。
2:物件產生後,會在堆記憶體中有一個實體,同時也呼叫了系統底層資源,在指定的位置建立了一個儲存資料的檔案。
3:如果指定位置,出現了同名檔案,檔案會被覆蓋。
*/
FileWriterfw = new FileWriter("demo.txt"); // FileNotFoundException
/*
呼叫Writer類中的write方法寫入字串。字串並未直接寫入到目的地中,而是寫入到了流中,(其實是寫入到記憶體緩衝區中)。怎麼把資料弄到檔案中?
*/
fw.write("abcde");
fw.flush(); // 重新整理緩衝區,將緩衝區中的資料刷到目的地檔案中。
fw.close();// 關閉流,其實關閉的就是java呼叫的系統底層資源。在關閉前,會先重新整理該流。
}
close()和flush()的區別:
flush():將緩衝區的資料刷到目的地中後,流可以使用。
close():將緩衝區的資料刷到目的地中後,流就關閉了,該方法主要用於結束呼叫的底層資源。這個動作一定做。
--------------------------------------------------------------------------------------------------------------------
io異常的處理方式:io一定要寫finally;
FileWriter寫入資料的細節:
1:window中的換行符:\r\n兩個符號組成。 linux:\n。
2:續寫資料,只要在建構函式中傳入新的引數true。
3:目錄分割符:window \\ /
public static void main(String[] args) {
FileWriterfw = null;
try{
fw= new FileWriter("demo.txt",true);
fw.write("abcde");
}
catch(IOException e ){
System.out.println(e.toString()+"....");
}
finally{
if(fw!=null)
try{
fw.close();
}
catch(IOException e){
System.out.println("close:"+e.toString());
}
}
}
--------------------------------------------------------------------------------------------------------------------
FileReader:使用Reader體系,讀取一個文字檔案中的資料。返回 -1 ,標誌讀到結尾。
import java.io.*;
class FileReaderDemo {
publicstatic void main(String[] args) throws IOException {
/*
建立可以讀取文字檔案的流物件,FileReader讓建立好的流物件和指定的檔案相關聯。
*/
FileReader fr = new FileReader("demo.txt");
intch = 0;
while((ch =fr.read())!= -1) { //條件是沒有讀到結尾
System.out.println((char)ch); //呼叫讀取流的read方法,讀取一個字元。
}
fr.close();
}
}
--------------------------------------------------------------------------------------------------------------------
讀取資料的第二種方式:第二種方式較為高效,自定義緩衝區。
import java.io.*;
class FileReaderDemo2 {
publicstatic void main(String[] args) throws IOException {
FileReaderfr = new FileReader("demo.txt"); //建立讀取流物件和指定檔案關聯。
//因為要使用read(char[])方法,將讀取到字元存入陣列。所以要建立一個字元陣列,一般陣列的長度都是1024的整數倍。
char[]buf = new char[1024];
intlen = 0;
while((len=fr.read(buf)) != -1) {
System.out.println(newString(buf,0,len));
}
fr.close();
}
}
--------------------------------------------------------------------------------------------------------------------
IO中的使用到了一個設計模式:裝飾設計模式。
裝飾設計模式解決:對一組類進行功能的增強。
包裝:寫一個類(包裝類)對被包裝物件進行包裝;
* 1、包裝類和被包裝物件要實現同樣的介面;
* 2、包裝類要持有一個被包裝物件;
* 3、包裝類在實現介面時,大部分方法是靠呼叫被包裝物件來實現的,對於需要修改的方法我們自己實現;
--------------------------------------------------------------------------------------------------------------------
字元流:
Reader:用於讀取字元流的抽象類。子類必須實現的方法只有 read(char[],int, int) 和 close()。
|---BufferedReader:從字元輸入流中讀取文字,緩衝各個字元,從而實現字元、陣列和行的高效讀取。 可以指定緩衝區的大小,或者可使用預設的大小。大多數情況下,預設值就足夠大了。
|---LineNumberReader:跟蹤行號的緩衝字元輸入流。此類定義了方法 setLineNumber(int)和 getLineNumber(),它們可分別用於設定和獲取當前行號。
|---InputStreamReader:是位元組流通向字元流的橋樑:它使用指定的 charset 讀取位元組並將其解碼為字元。它使用的字符集可以由名稱指定或顯式給定,或者可以接受平臺預設的字符集。
|---FileReader:用來讀取字元檔案的便捷類。此類的構造方法假定預設字元編碼和預設位元組緩衝區大小都是適當的。要自己指定這些值,可以先在 FileInputStream 上構造一個 InputStreamReader。
|---CharArrayReader:
|---StringReader:
-------------------------------------------------
Writer:寫入字元流的抽象類。子類必須實現的方法僅有 write(char[],int, int)、flush() 和 close()。
|---BufferedWriter:將文字寫入字元輸出流,緩衝各個字元,從而提供單個字元、陣列和字串的高效寫入。
|---OutputStreamWriter:是字元流通向位元組流的橋樑:可使用指定的 charset 將要寫入流中的字元編碼成位元組。它使用的字符集可以由名稱指定或顯式給定,否則將接受平臺預設的字符集。
|---FileWriter:用來寫入字元檔案的便捷類。此類的構造方法假定預設字元編碼和預設位元組緩衝區大小都是可接受的。要自己指定這些值,可以先在 FileOutputStream 上構造一個 OutputStreamWriter。
|---PrintWriter:
|---CharArrayWriter:
|---StringWriter:
---------------------------------
位元組流:
InputStream:是表示位元組輸入流的所有類的超類。
|--- FileInputStream:從檔案系統中的某個檔案中獲得輸入位元組。哪些檔案可用取決於主機環境。FileInputStream用於讀取諸如影象資料之類的原始位元組流。要讀取字元流,請考慮使用 FileReader。
|--- FilterInputStream:包含其他一些輸入流,它將這些流用作其基本資料來源,它可以直接傳輸資料或提供一些額外的功能。
|--- BufferedInputStream:該類實現緩衝的輸入流。
|--- Stream:
|--- ObjectInputStream:
|--- PipedInputStream:
-----------------------------------------------
OutputStream:此抽象類是表示輸出位元組流的所有類的超類。
|--- FileOutputStream:檔案輸出流是用於將資料寫入
File
或 FileDescriptor
的輸出流。
|--- FilterOutputStream:此類是過濾輸出流的所有類的超類。
|--- BufferedOutputStream:該類實現緩衝的輸出流。
|--- PrintStream:
|--- DataOutputStream:
|--- ObjectOutputStream:
|--- PipedOutputStream:
--------------------------------
緩衝區是提高效率用的,給誰提高呢?
BufferedWriter:是給字元輸出流提高效率用的,那就意味著,緩衝區物件建立時,必須要先有流物件。明確要提高具體的流物件的效率。
FileWriter fw = newFileWriter("bufdemo.txt");
BufferedWriter bufw =new BufferedWriter(fw);//讓緩衝區和指定流相關聯。
for(int x=0; x<4;x++){
bufw.write(x+"abc");
bufw.newLine(); //寫入一個換行符,這個換行符可以依據平臺的不同寫入不同的換行符。
bufw.flush();//對緩衝區進行重新整理,可以讓資料到目的地中。
}
bufw.close();//關閉緩衝區,其實就是在關閉具體的流。
-----------------------------
BufferedReader:
FileReader fr = newFileReader("bufdemo.txt");
BufferedReaderbufr = new BufferedReader(fr);
String line = null;
while((line=bufr.readLine())!=null){ //readLine方法返回的時候是不帶換行符的。
System.out.println(line);
}
bufr.close();
-----------------------------
//記住,只要一讀取鍵盤錄入,就用這句話。
BufferedReader bufr = new BufferedReader(newInputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(newOutputStreamWriter(System.out));//輸出到控制檯
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
bufw.write(line.toUpperCase());//將輸入的字元轉成大寫字元輸出
bufw.newLine();
bufw.flush();
}
bufw.close();
bufr.close();
------------------------------
流物件:其實很簡單,就是讀取和寫入。但是因為功能的不同,流的體系中提供N多的物件。那麼開始時,到底該用哪個物件更為合適呢?這就需要明確流的操作規律。
流的操作規律:
1,明確源和目的。
資料來源:就是需要讀取,可以使用兩個體系:InputStream、Reader;
資料匯:就是需要寫入,可以使用兩個體系:OutputStream、Writer;
2,操作的資料是否是純文字資料?
如果是:資料來源:Reader
資料匯:Writer
如果不是:資料來源:InputStream
資料匯:OutputStream
3,雖然確定了一個體系,但是該體系中有太多的物件,到底用哪個呢?
明確操作的資料裝置。
資料來源對應的裝置:硬碟(File),記憶體(陣列),鍵盤(System.in)
資料匯對應的裝置:硬碟(File),記憶體(陣列),控制檯(System.out)。
4,需要在基本操作上附加其他功能嗎?比如緩衝。
如果需要就進行裝飾。
轉換流特有功能:轉換流可以將位元組轉成字元,原因在於,將獲取到的位元組通過查編碼表獲取到指定對應字元。
轉換流的最強功能就是基於 位元組流 + 編碼表 。沒有轉換,沒有字元流。
發現轉換流有一個子類就是操作檔案的字元流物件:
InputStreamReader
|--FileReader
OutputStreamWriter
|--FileWrier
想要操作文字檔案,必須要進行編碼轉換,而編碼轉換動作轉換流都完成了。所以操作檔案的流物件只要繼承自轉換流就可以讀取一個字元了。
但是子類有一個侷限性,就是子類中使用的編碼是固定的,是本機預設的編碼表,對於簡體中文版的系統預設碼錶是GBK。
FileReader fr = newFileReader("a.txt");
InputStreamReader isr = newInputStreamReader(new FileInputStream("a.txt"),"gbk");
以上兩句程式碼功能一致,
如果僅僅使用平臺預設碼錶,就使用FileReader fr = new FileReader("a.txt"); //因為簡化。
如果需要制定碼錶,必須用轉換流。
轉換流 = 位元組流+編碼表。
轉換流的子類File = 位元組流 + 預設編碼表。
凡是操作裝置上的文字資料,涉及編碼轉換,必須使用轉換流。
-----------------------------------------------------------------------------------------------
File類:將檔案系統中的檔案和資料夾封裝成了物件。提供了更多的屬性和行為可以對這些檔案和資料夾進行操作。這些是流物件辦不到的,因為流只操作資料。
File類常見方法:
1:建立。
boolean createNewFile():在指定目錄下建立檔案,如果該檔案已存在,則不建立。而對操作檔案的輸出流而言,輸出流物件已建立,就會建立檔案,如果檔案已存在,會覆蓋。除非續寫。
boolean mkdir():建立此抽象路徑名指定的目錄。
boolean mkdirs():建立多級目錄。
2:刪除。
boolean delete():刪除此抽象路徑名錶示的檔案或目錄。
void deleteOnExit():在虛擬機器退出時刪除。
注意:在刪除資料夾時,必須保證這個資料夾中沒有任何內容,才可以將該資料夾用delete刪除。
window的刪除動作,是從裡往外刪。注意:java刪除檔案不走回收站。要慎用。
3:獲取.
long length():獲取檔案大小。
String getName():返回由此抽象路徑名錶示的檔案或目錄的名稱。
String getPath():將此抽象路徑名轉換為一個路徑名字串。
String getAbsolutePath():返回此抽象路徑名的絕對路徑名字串。
String getParent():返回此抽象路徑名父目錄的抽象路徑名,如果此路徑名沒有指定父目錄,則返回
null
。
long lastModified():返回此抽象路徑名錶示的檔案最後一次被修改的時間。
File.pathSeparator:返回當前系統預設的路徑分隔符,windows預設為 “;”。
File.Separator:返回當前系統預設的目錄分隔符,windows預設為 “\”。
4:判斷:
boolean exists():判斷檔案或者資料夾是否存在。
boolean isDirectory():測試此抽象路徑名錶示的檔案是否是一個目錄。
boolean isFile():測試此抽象路徑名錶示的檔案是否是一個標準檔案。
boolean isHidden():測試此抽象路徑名指定的檔案是否是一個隱藏檔案。
boolean isAbsolute():測試此抽象路徑名是否為絕對路徑名。
5:重新命名。
boolean renameTo(Filedest):可以實現移動的效果。剪下+重新命名。
String[] list():列出指定目錄下的當前的檔案和資料夾的名稱。包含隱藏檔案。
如果呼叫list方法的File 物件中封裝的是一個檔案,那麼list方法返回陣列為null。如果封裝的物件不存在也會返回null。只有封裝的物件存在並且是資料夾時,這個方法才有效。
------------------------------------------------------------------------------------------------
遞迴:就是函式自身呼叫自身。
什麼時候用遞迴呢?
當一個功能被重複使用,而每一次使用該功能時的引數不確定,都由上次的功能元素結果來確定。
簡單說:功能內部又用到該功能,但是傳遞的引數值不確定。(每次功能參與運算的未知內容不確定)。
遞迴的注意事項:
1:一定要定義遞迴的條件。
2:遞迴的次數不要過多。容易出現 StackOverflowError 棧記憶體溢位錯誤。
其實遞迴就是在棧記憶體中不斷的載入同一個函式。
------------------------------------------------------------------------------------------------
Java.util.Properties:一個可以將鍵值進行持久化儲存的物件。Map--Hashtable的子類。
Map
|--Hashtable
|--Properties:用於屬性配置檔案,鍵和值都是字串型別。
特點:1:可以持久化儲存資料。2:鍵值都是字串。3:一般用於配置檔案。
|-- load():將流中的資料載入進集合。
原理:其實就是將讀取流和指定檔案相關聯,並讀取一行資料,因為資料是規則的key=value,所以獲取一行後,通過= 對該行資料進行切割,左邊就是鍵,右邊就是值,將鍵、值儲存到properties集合中。
|-- store():寫入各個項後,重新整理輸出流。
|-- list():將集合的鍵值資料列出到指定的目的地。
-------------------------------------------------------------------------------------------------
以下介紹IO包中擴充套件功能的流物件:基本都是裝飾設計模式。
Java.io.outputstream.PrintStream:列印流
1:提供了更多的功能,比如列印方法。可以直接列印任意型別的資料。
2:它有一個自動重新整理機制,建立該物件,指定引數,對於指定方法可以自動重新整理。
3:它使用的本機預設的字元編碼.
4:該流的print方法不丟擲IOException。
該物件的建構函式。
PrintStream(File file) :建立具有指定檔案且不帶自動行重新整理的新列印流。
PrintStream(File file, String csn) :建立具有指定檔名稱和字符集且不帶自動行重新整理的新列印流。
PrintStream(OutputStream out) :建立新的列印流。
PrintStream(OutputStream out, boolean autoFlush) :建立新的列印流。
PrintStream(OutputStream out, boolean autoFlush, Stringencoding) :建立新的列印流。
PrintStream(String fileName) :建立具有指定檔名稱且不帶自動行重新整理的新列印流。
PrintStream(String fileName, String csn)
PrintStream可以操作目的:1:File物件。2:字串路徑。3:位元組輸出流。
前兩個都JDK1.5版本才出現。而且在操作文字檔案時,可指定字元編碼了。
當目的是一個位元組輸出流時,如果使用的println方法,可以在printStream物件上加入一個true引數。這樣對於println方法可以進行自動的重新整理,而不是等待緩衝區滿了再重新整理。最終print方法都將具體的資料轉成字串,而且都對IO異常進行了內部處理。
既然操作的資料都轉成了字串,那麼使用PrintWriter更好一些。因為PrintWrite是字元流的子類,可以直接操作字元資料,同時也可以指定具體的編碼。
--------------------------------------------------------
PrintWriter:具備了PrintStream的特點同時,還有自身特點:
該物件的目的地有四個:1:File物件。2:字串路徑。3:位元組輸出流。4:字元輸出流。
開發時儘量使用PrintWriter。
方法中直接操作檔案的第二引數是編碼表。
直接操作輸出流的,第二引數是自動重新整理。
//讀取鍵盤錄入將資料轉成大寫顯示在控制檯.
BufferedReader bufr = new BufferedReader(newInputStreamReader(System.in));//源:鍵盤輸入
//目的:把資料寫到檔案中,還想自動重新整理。
PrintWriter out = new PrintWriter(new FileWriter("out.txt"),true);//設定true後自動重新整理
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
out.println(line.toUpperCase());//轉大寫輸出
}
//注意:System.in,System.out這兩個標準的輸入輸出流,在jvm啟動時已經存在了。隨時可以使用。當jvm結束了,這兩個流就結束了。但是,當使用了顯示的close方法關閉時,這兩個流在提前結束了。
out.close();
bufr.close();
------------------------------------------------------------------------------------------------
SequenceInputStream:序列流,作用就是將多個讀取流合併成一個讀取流。實現資料合併。
表示其他輸入流的邏輯串聯。它從輸入流的有序集合開始,並從第一個輸入流開始讀取,直到到達檔案末尾,接著從第二個輸入流讀取,依次類推,直到到達包含的最後一個輸入流的檔案末尾為止。
這樣做,可以更方便的操作多個讀取流,其實這個序列流內部會有一個有序的集合容器,用於儲存多個讀取流物件。
該物件的建構函式引數是列舉,想要獲取列舉,需要有Vector集合,但不高效。需用ArrayList,但ArrayList中沒有列舉,只有自己去建立列舉物件。
但是方法怎麼實現呢?因為列舉操作的是具體集合中的元素,所以無法具體實現,但是列舉和迭代器是功能一樣的,所以,可以用迭代替代列舉。
合併原理:多個讀取流對應一個輸出流。
切割原理:一個讀取流對應多個輸出流。
import java.io.*;
import java.util.*;
class SplitFileDemo{
private static final String CFG = ".properties";
private static final String SP =".part";
public static void main(String[] args)throws IOException{
File file = newFile("c:\\0.bmp");
File dir = newFile("c:\\partfiles");
meger(dir);
}
//資料的合併。
public static void meger(File dir)throwsIOException{
if(!(dir.exists() &&dir.isDirectory()))
throw new RuntimeException("指定的目錄不存在,或者不是正確的目錄");
File[] files = dir.listFiles(newSuffixFilter(CFG));
if(files.length==0)
throw new RuntimeException("副檔名.proerpties的檔案不存在");
//獲取到配置檔案
File config = files[0];
//獲取配置檔案的資訊。
Properties prop = new Properties();
FileInputStream fis = newFileInputStream(config);
prop.load(fis);
String fileName =prop.getProperty("filename");
int partcount =Integer.parseInt(prop.getProperty("partcount"));
//--------------------------
File[] partFiles = dir.listFiles(newSuffixFilter(SP));
if(partFiles.length!=partcount)
throw new RuntimeException("缺少碎片檔案");
//---------------------
ArrayList<FileInputStream> al =new ArrayList<FileInputStream>();
for(int x=0; x<partcount; x++){
al.add(new FileInputStream(newFile(dir,x+SP)));
}
Enumeration<FileInputStream> en =Collections.enumeration(al);
SequenceInputStream sis = newSequenceInputStream(en);
File file = new File(dir,fileName);
FileOutputStream fos = newFileOutputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while((len=sis.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
//帶有配置資訊的資料切割。
public static void splitFile(Filefile)throws IOException{
//用一個讀取流和檔案關聯。
FileInputStream fis = newFileInputStream(file);
//建立目的地。因為有多個。所以先建立引用。
FileOutputStream fos = null;
//指定碎片的位置。
File dir = newFile("c:\\partfiles");
if(!dir.exists())
dir.mkdir();
//碎片檔案大小引用。
File f = null;
byte[] buf = new byte[1024*1024];
//因為切割完的檔案通常都有規律的。為了簡單標記規律使用計數器。
int count = 0;
int len = 0;
while((len=fis.read(buf))!=-1){
f = newFile(dir,(count++)+".part");
fos = new FileOutputStream(f);
fos.write(buf,0,len);
fos.close();
}
//碎片檔案生成後,還需要定義配置檔案記錄生成的碎片檔案個數。以及被切割檔案的名稱。
//定義簡單的鍵值資訊,可是用Properties。
String filename = file.getName();
Properties prop = new Properties();
prop.setProperty("filename",filename);
prop.setProperty("partcount",count+"");
File config = newFile(dir,count+".properties");
fos = new FileOutputStream(config);
prop.store(fos,"");
fos.close();
fis.close();
}
}
class SuffixFilterimplements FileFilter{
private String suffix;
SuffixFilter(String suffix){
this.suffix = suffix;
}
public boolean accept(File file){
return file.getName().endsWith(suffix);
}
}
-----------------------------------------------------------------------------------------------
RandomAccessFile:
特點:
1:該物件即可讀取,又可寫入。
2:該物件中的定義了一個大型的byte陣列,通過定義指標來操作這個陣列。
3:可以通過該物件的getFilePointer()獲取指標的位置,通過seek()方法設定指標的位置。
4:該物件操作的源和目的必須是檔案。
5:其實該物件內部封裝了位元組讀取流和位元組寫入流。
注意:實現隨機訪問,最好是資料有規律。
class RandomAccessFileDemo{
public static voidmain(String[] args) throws IOException{
write();
read();
randomWrite();
}
//隨機寫入資料,可以實現已有資料的修改。
public static voidrandomWrite()throws IOException{
RandomAccessFileraf = new RandomAccessFile("random.txt","rw");
raf.seek(8*4);
System.out.println("pos:"+raf.getFilePointer());
raf.write("王武".getBytes());
raf.writeInt(102);
raf.close();
}
public static voidread()throws IOException{
RandomAccessFileraf = new RandomAccessFile("random.txt","r");//只讀模式。
//指定指標的位置。
raf.seek(8*1);//實現隨機讀取檔案中的資料。注意:資料最好有規律。
System.out.println("pos1:"+raf.getFilePointer());
byte[] buf = newbyte[4];
raf.read(buf);
String name = newString(buf);
int age =raf.readInt();
System.out.println(name+"::"+age);
System.out.println("pos2:"+raf.getFilePointer());
raf.close();
}
public static voidwrite()throws IOException{
//rw:當這個檔案不存在,會建立該檔案。當檔案已存在,不會建立。所以不會像輸出流一樣覆蓋。
RandomAccessFileraf = new RandomAccessFile("random.txt","rw");//rw讀寫模式
//往檔案中寫入人的基本資訊,姓名,年齡。
raf.write("張三".getBytes());
raf.writeInt(97);
raf.close();
}
}
------------------------------------------------------------------------------------------------
管道流:管道讀取流和管道寫入流可以像管道一樣對接上,管道讀取流就可以讀取管道寫入流寫入的資料。
注意:需要加入多執行緒技術,因為單執行緒,先執行read,會發生死鎖,因為read方法是阻塞式的,沒有資料的read方法會讓執行緒等待。
public static void main(String[] args) throws IOException{
PipedInputStream pipin= new PipedInputStream();
PipedOutputStreampipout = new PipedOutputStream();
pipin.connect(pipout);
new Thread(newInput(pipin)).start();
new Thread(newOutput(pipout)).start();
}
------------------------------------------------------------------------------------------------
物件的序列化:目的:將一個具體的物件進行持久化,寫入到硬碟上。
注意:靜態資料不能被序列化,因為靜態資料不在堆記憶體中,是儲存在靜態方法區中。
如何將非靜態的資料不進行序列化?用transient 關鍵字修飾此變數即可。
Serializable:用於啟動物件的序列化功能,可以強制讓指定類具備序列化功能,該介面中沒有成員,這是一個標記介面。這個標記介面用於給序列化類提供UID。這個uid是依據類中的成員的數字簽名進行執行獲取的。如果不需要自動獲取一個uid,可以在類中,手動指定一個名稱為serialVersionUID id號。依據編譯器的不同,或者對資訊的高度敏感性。最好每一個序列化的類都進行手動顯示的UID的指定。
import java.io.*;
class ObjectStreamDemo {
public static voidmain(String[] args) throws Exception{
writeObj();
readObj();
}
public static voidreadObj()throws Exception{
ObjectInputStream ois = newObjectInputStream(new FileInputStream("obj.txt"));
Object obj =ois.readObject();//讀取一個物件。
System.out.println(obj.toString());
}
public static voidwriteObj()throws IOException{
ObjectOutputStreamoos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
oos.writeObject(newPerson("lisi",25)); //寫入一個物件。
oos.close();
}
}
class Person implements Serializable{
private static finallong serialVersionUID = 42L;
private transient Stringname;//用transient修飾後name將不會進行序列化
public int age;
Person(String name,intage){
this.name = name;
this.age = age;
}
public StringtoString(){
returnname+"::"+age;
}
}
-----------------------------------------------------------------------------------------------
DataOutputStream、DataInputStream:專門用於操作基本資料型別資料的物件。
DataOutputStream dos = new DataOutputStream(newFileOutputStream("data.txt"));
dos.writeInt(256);
dos.close();
DataInputStream dis =new DataInputStream(new FileInputStream("data.txt"));
int num = dis.readInt();
System.out.println(num);
dis.close();
-----------------------------------------------------------------------------------------------
ByteArrayInputStream:源:記憶體
ByteArrayOutputStream:目的:記憶體。
這兩個流物件不涉及底層資源呼叫,操作的都是記憶體中陣列,所以不需要關閉。
直接操作位元組陣列就可以了,為什麼還要把陣列封裝到流物件中呢?因為陣列本身沒有方法,只有一個length屬性。為了便於陣列的操作,將陣列進行封裝,對外提供方法運算元組中的元素。
對於陣列元素操作無非兩種操作:設定(寫)和獲取(讀),而這兩操作正好對應流的讀寫操作。這兩個物件就是使用了流的讀寫思想來運算元組。
//建立源:
ByteArrayInputStreambis = new ByteArrayInputStream("abcdef".getBytes());
//建立目的:
ByteArrayOutputStreambos = new ByteArrayOutputStream();
int ch = 0;
while((ch=bis.read())!=-1){
bos.write(ch);
}
System.out.println(bos.toString());
-----------------------------------------------------------------------------------------------