- java內存中存的字符都是Unicode編碼的,當讀取磁盤中的文件的時候,默認通過GBK解碼(記事本的ANSI編碼,就是這種默認編碼),但是磁盤/網絡中有各種各樣的的編碼方式(比如:UTF-8,UTF-8是網頁比較流行的一種格式,但是它無法通過默認的GBK解碼,只能通過UTF-8進行解碼),因此從java內存到磁盤/網絡或者從磁盤/網絡到java內存中會有一個轉換過程,InputStreamReader和OutputStreamWriter可指定編碼格式。
- https://blog.csdn.net/m0_37732829/article/details/80571166
- 將數據存到電腦中,最根本的格式是二進制的格式,但是當存入比如文檔中或者別的文件中的時候,會有自己的一個編碼格式,將二進制轉換成自己的編碼格式,當讀入到java程序中的時候,java又將他們自己的編碼格式變成Unicode編碼格式。當從java程序讀到別的文件中的時候,又將Unicode編碼格式轉換成其餘的編碼格式。
- 做題的時候不需要想太多,比如需要一個String類型的,就用Buffered流,需要讀int類型的,就用Data流,需要讀結構體類型的,就用結構體類型的流。
- 字符流的時候,write(String類型)一般文件中不會錯
- 做題的時候,先想到底是讀/寫字節/字符進來,最終以什麼形式,然後再想各種形式之間的轉換問題。
- 想要什麼類型,中間需要經過多少轉換要想清楚
一、輸入流和輸出流的四種抽象基本類是InputStream、OutputStream、Reader、Writer
輸入流和輸出流是相對於java內存而言的
輸入流(讀到java內存中):InputStream、Reader
輸出流(從java內存中讀出):OutputStream、Writer
(一)InputStream
直接子類有:FileInputStream, ObjectInputStream
間接子類有:BufferedInputStream(FileInputStream),DataInputStream(FileInputStream)
以字節爲單位,通過默認編碼從磁盤/網絡中讀取文件到java內存中進行的操作
1、基本操作
read括號中沒有或者是byte[]類型,返回值是int類型
(二)Reader
直接子類有:BufferedReader, InputStreamReader
間接子類有:FileReader(InputStreamReader)
以字符爲單位從磁盤/網絡中讀取文件到java內存中進行的操作
1、基本操作
read括號中沒有或者是char[]類型,返回值是int類型
(三)OutputStream
直接子類有:ObjectOutputStream、BufferedOutputStream
間接子類有:DataOutputStream, FileOutputStream
以字節爲單位將java內存中的數據寫到磁盤/網絡中
1、基本操作
write括號中的是int或者byte[]類型,返回值是int類型,但是作用:將指定的字節寫入該輸出流中
(四)Writer
直接子類有:BufferedWriter, OutputStreamWriter
間接子類有:FileWriter, PrintWriter
以字符爲單位將java內存中的數據寫到磁盤/網絡中
1、基本操作
write括號中的是int或者char[]或者String類型,返回值是void類型
二、文件輸入流和輸出流的四種基本類是FileInputStream、FileOutputStream、FileReader、FileWriter
輸入流和輸出流是相對於java內存而言的
輸入流(讀到java內存中):FileInputStream、FileReader
輸出流(從java內存中讀出):FileOutputStream、FileWriter
(一)FileInputStream(以字節的形式讀入char,輸出也要爲char)
FileInputStream in=null;//可能會拋出一個已檢查異常//別忘記導入包
try
{
in=new FileInputStream("TestFileInputStream.java");
}
catch(FileNotFoundException e)
{
System.out.println("找不到指定文件");
System.exit(-1);//直接退出程序
}
try
{
ong num=0;
while((b=in.read())!=-1)//每次讀一個字節給b,-1的意思是已經讀到文件結尾了
{
System.out.print((char)b);//讀入char,輸出也要爲char//read的返回值是int類型的
num++;//統計多少個字節
}
System.out.println();
System.out.println("共讀取了"+num+"個字節");
}
- read()返回類型是int,結果爲-1代表讀到結尾了
- 因爲讀的時候是按照字節來讀的,所以輸出的時候需要強制轉換成char類型的
(二)FileReader(以字符的形式讀入char,輸出也要爲char)
FileReader fr=null;//可能會拋出一個已檢查異常//別忘記導入包
int c=0;
try
{
fr=new FileReader("E:\\workplace\\KeJian\\src\\TestFileReader.java");
int ln=0;
while((c=fr.read())!=-1)//沒有讀到文件結尾
{
System.out.print((char)c);//讀入char,輸出也要爲char
ln++;
}
System.out.println("共讀取了"+ln+"個字符");
}
- read()返回類型是int,結果爲-1代表讀到結尾了
- 因爲讀的時候是按照字節來讀的,所以輸出的時候需要強制轉換成char類型的
(三)FileOutputStream(以字節的形式寫到磁盤中)
- 如果通過FileInputStream讀入,那麼就需要用FileOutputStream與其相匹配,如果用FileWriter,會導致中文輸出不正常,因爲按照字節讀入,就要按照字節輸出,不能按照字符輸出,否則會導致亂碼。
- 如果通過FileInputStream讀入,FileOutputStream與其相匹配輸出到文件中的時候,不需要必須爲char類型,因爲讀入和輸出的方式(以字節爲單位)相同
FileInputStream in=null;//可能會拋出一個已檢查異常//別忘記導入包
FileOutputStream out=null;
FileWriter f=null;
try
{
in=new FileInputStream("HelloWorld.java");
out=new FileOutputStream("newHello.java");
while((b=in.read())!=-1)
{
out.write(b);//中文正常//讀完之後,寫到newHello.java這個文件中去
}
}
(四)FileWriter(以字符的形式寫到磁盤)
FileWriter fw=null;//可能會拋出一個已檢查異常//別忘記導入包
FileOutputStream fs=null;
try
{
fw=new FileWriter("E:\\workplace\\KeJian\\unicode.txt");//雙斜槓是轉移符的問題,\\代表\
fs=new FileOutputStream("E:\\workplace\\KeJian\\unicode1.txt");
for(int c=0;c<=50;c++)
{
fw.write(c);//亂碼
fs.write(c);//亂碼
}
}
- 都是亂碼的原因是:需要的是字符/字節,但是傳進來的是int不是一個字節/字符,所以會導致亂碼
- 可能會想,爲什麼前面從文件中讀取完之後是int類型直接寫到文件中卻不是亂碼呢?因爲即使是int類型,也是從文件中按照字節/字符的形式讀取,按照字節/字符的形式拷貝的,因此不是亂碼,這裏是直接把一個int類型的數字放進去,不是字節/字符形式,必然會出現亂碼的問題
三、字符處理流
(一)BufferedInputStream(字節流)
括號中需要是InputStream的子類
直接子類有:FileInputStream, ObjectInputStream
間接子類有:BufferedInputStream(FileInputStream),DataInputStream(FileInputStream)
BufferedInputStream bis=null;
try
{
/*FileInputStream fis=new FileInputStream("HelloWorld.java");
bis=new BufferedInputStream(fis);*/
bis=new BufferedInputStream(new FileInputStream("HelloWorld.java"));
int c=0;
for(int i=0;(c=bis.read())!=-1;i++)
{
System.out.print((char)c);
}
}
- 與 FileInputStream相比,沒有太大的區別,僅僅是加快了速度
- BufferedInputStream括號中只能new FileInputStream,別的不行
(二)BufferedReader(字符流)
括號中需要是Reader的子類
直接子類有:BufferedReader, InputStreamReader
間接子類有:FileReader(InputStreamReader)
- 作用:緩衝字符,以便提供字符、數組和行的有效讀取
- BufferedReader中能new FileReader,new InputStreamReader
- 增加了readLine方法,返回值是String類型,比較好用,一次讀取一行,因此,如果想要讀取String類型的數據,就用BufferedReader
br=new BufferedReader(new FileReader("test.txt"));//讀test.txt中的內容
String s=null;
while((s=br.readLine())!=null)
{
System.out.println(s);
}
(三)BufferedOutputStream(字節流)
括號中需要是OutputStream的子類
直接子類有:ObjectOutputStream
間接子類有: DataOutputStream,FileOutputStream
(四)BufferedWriter(字符流)
括號中只能是Writer的子類
直接子類有:BufferedWriter, OutputStreamWriter
間接子類有: FileWriter, PrintWriter
- 作用:緩衝字符,以便提供字符、數組和行的有效讀取
- BufferedWriter中 能new FileWriter 能new OutputStreamWriter
- 增加了newLine方法,比較好用,每次寫完一行之後,自動換行,因此,如果想要讀完一行之後回車,就用BufferedWriter
四、轉換流
(一)InputStreamReader(字符流)
括號中只能是InputStream的子類,可以指定用何種形式編碼
- InputStreamReader是將 InputStream中的字節轉換成字符
- 爲什麼要指定編碼集合呢?
因爲java程序在內存中的字符都是Unicode編碼的,但是從文件中讀取的內容有自己的編碼格式,可能是jbk編碼,可能是ISO8859_1編碼,還有可能是ASCILL編碼,編碼不同,所以要根據已有編碼轉換成Unicode編碼,因此,要告訴java原來是什麼樣的編碼格式(但是如果指定編碼格式錯誤的話,會出現亂碼問題)。 - 只能new InputStream的子類,如:FileInputStream
FileWriter和FileOutputStream都可以實現在文章結尾繼續寫東西而不是覆蓋掉以前的東西,至於true加到哪裏上面的兩張圖片已經說的很清楚了。
InputStreamReader isr=null;
try
{
isr=new InputStreamReader(new FileInputStream("TestTileInputStream.java"));//通過InputStreamReader讀取FileInputStream中讀取的TestTileInputStream.java中的字符
long num=0;
while((b=isr.read())!=-1)
{
System.out.print((char)b);
num++;
}
System.out.println("共讀取了"+num+"個字符");
}
將鍵盤輸入的內容直接輸入到文檔中
String s;
InputStreamReader isr=new InputStreamReader(System.in);
/*
* 本來應該套一個InputStream,但是套了System.in
* 所以System.in也是InputStream類型的
* 所以可以傳給InputStreamReader
* InputStreamReader套接了控制檯鍵盤輸入
* System.in讀入的是字節,InputStreamReader轉換成了字符
* InputStreamReader只能一個字符一個字符地讀,比較麻煩,所以又套接了一個BufferedReader
* */
BufferedReader br=new BufferedReader(isr);//BufferedReader的特點,能一次讀取一行
System.out.println("Unix:Type ctrl-d or cril-c to exit."+"\nWindows:Type ctrl-z to exit");
try
{
//用Ctrl+z來結束
while((s=br.readLine())!=null)
{
System.out.println("Read: "+s);//輸入一行,打印一行
}
}
(二)OutputStreamWriter(字符流)
括號中只能是OutputStream的子類,可以指定編碼格式
- OutputStreamWriter是將 OutputStream中的字節轉換成字符
- 爲什麼要指定編碼集合呢?
因爲java程序在內存中的字符都是Unicode編碼的,到文件中的時候,會有自己的編碼格式,可能是jbk編碼,可能是ISO8859_1編碼,還有可能是ASCILL編碼,編碼不同,所以要將Unicode編碼轉換成自己想要的編碼格式,因此,要告訴java自己需要什麼樣的編碼格式。 - 只能new OutputStream的子類,如:FileOutputStream
try
{
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("transform1.txt"));//通過FileOutputStream文件輸出流,往transform1.txt中寫數據
osw.write("計算機學院");
osw.write("SUN Java");
System.out.println(osw.getEncoding());//寫入的時候沒有指定編碼格式,那麼就採用中文Window默認編碼GBK
osw.close();//FileOutputStream構造方法第二個參數的含義,是否追加
osw=new OutputStreamWriter(new FileOutputStream("transform1.txt",true),"ISO8859_1");
/*這裏用FileOutputStream的重載方法,形參加了個true
* 這裏本來默認是false,每次寫的時候,會覆蓋原來文件的內容
* 但是加了true,意思是保留原有文件內容,並且在原有文件的尾部追加新的內容
* 編碼格式改成IS08859_1,指的是英文字符,沒有中文字符串
* lantin-1
* */
osw.write("山東科技大學");//上面指定的編碼格式中,沒有文字字符串
osw.write("C#");
System.out.println(osw.getEncoding());
sw.close();
}
- ISO8859_1編碼格式中沒有文字字符串,所以是錯誤的。
- osw=new OutputStreamWriter(new FileOutputStream(“transform1.txt”,true),“ISO8859_1”);
/*這裏用FileOutputStream的重載方法,形參加了個true
* 這裏本來默認是false,每次寫的時候,會覆蓋原來文件的內容
* 但是加了true,意思是保留原有文件內容,並且在原有文件的尾部追加新的內容
* 編碼格式改成IS08859_1,指的是英文字符,沒有中文字符串
* lantin-1
* */
五、數據流
(一)DataInputStream(字節流)
括號內只能是InputStream的子類
- 可以直接將字節類型的轉換爲基本類型數據
(二)DataOutputStream(字節流)
括號內只能是OutputStream的子類
- 可以直接將字節類型的轉換爲基本類型數據
- 將基本類型轉換爲字節存起來
六、Print流
(一)PrintStream(字節流)
PrintStream ps=null;
try
{
FileOutputStream fos=new FileOutputStream("redirect.txt");
ps=new PrintStream(fos,true,"utf-8");
/*
* PrintStream中有構造方法,可以有三個參數的構造方法
* 第一個創建一個OutputStream
* 第二個是是不是自動的刷新、緩衝、清空
* 第三個指定輸出的字符是什麼編碼格式
* */
if(ps!=null)
{
System.setOut(ps);//實現輸出的重定向,不再向控制檯輸出,而是向指定文件中輸出
}
int line=0;
for(char c=100;c<=200;c++)
{
System.out.println();//此時通過FileOutputStream這個流往redirect.txt這個文件中輸出
line=0;
}
}
- System.setOut(ps);可以實現重定向
(二)PrintWriter(字符流)
對象序列化
爲什麼要進行對象序列化呢?
目的是爲了轉化成字節序列,便於存盤或者是進行網絡傳輸。存盤和網絡傳輸的是以字節爲單位,對象序列化之後,就不會考慮到編碼差異等問題了。
1、想要被對象序列化,就必須要實現Serializable或者Externalizable兩個接口中的某一個
2、Serializable這個接口中沒有任何接口,無需實現任何方法,僅僅繼承這個接口就好。
3、使對象序列化的流