----------- android培訓、java培訓、java學習型技術博客、期待與您交流! ------------
一、裝飾設計模式
裝飾設計模式。
解決的問題:給已有的對象提供增強額外的功能。還不用對原有對象進行修改。
比繼承更爲靈活。
Writer
|--TextWriter
|--MediaWriter
現有一個體系用於各種數據的寫入。
但是,發現寫入效率有點低。想要對其進行效率的提高。
可以使用緩衝技術來完成的。
已有對象中的寫入方法,不夠高效,可以通過派生子類的形式對其進行復寫,定義高效的寫入動作。
Writer
|--TextWriter
|--BufferTextWriter
|--MediaWriter
|--BufferMediaWriter
|--DataWriter
|--BufferDataWriter
通過繼承的方式提高了效率。
但是對於擴展性是一個問題。而且所需的功能越多,子類就越多。
如何解決這個問題呢?優化!
既然都需要緩衝,對數據寫入效率進行提高 。
可以轉變一下思想,這樣來做,將緩衝技術單獨進行封裝。
哪個對象需要緩衝,就把哪個對象傳遞給緩衝對象即可。
class Buffer{
Buffer(TextWriter w){
}
Buffer(MediaWriter w){
}
}
體系就變成了這樣:
Writer
|--TextWriter
|--MediaWriter
|--BufferWriter
BufferWriter的出現,增強了Writer體系中的功能。
這種設計方式比原理更爲靈活,避免了繼承的臃腫。
將這樣解決方式就定義了一個名稱方便於後人使用:裝飾設計模式。
記住:裝飾類和被裝飾類都所屬於同一個體系。
二、行號裝飾類
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("tempfile\\demo.java");
LineNumberReader lnr = new LineNumberReader(fr);
String line = null;
lnr.setLineNumber(100);
while((line=lnr.readLine())!=null){
System.out.println(lnr.getLineNumber()+":"+line);
}
lnr.close();
}
三、字符流:
FileReader
FileWriter
BufferedReader
BuffereWriter
字節流。
InputStream OutputStream
FileInputStream FileOutputStream
BufferedInputStream BufferedOutputStream
轉換流:
字節流--->字符流的橋樑。InputStreamReader
字符流--->字節流的橋樑。OutputStreamWriter
public static void main(String[] args) throws IOException {
需求:將一個字符串寫入一個文件中。使用FileOutputStream來演示。
FileOutputStream fos = new FileOutputStream("tempfile\\fos.txt");
fos.write("abcdef".getBytes());//字節流的直接操作文件寫入時,直接將數據寫入到目的地。
爲啥FileWriter就需要刷呢?
那是因爲FileWriter底層使用了字節流在操作字符數據時,會先將這些數據進行臨時存儲,並查指定的編碼表。按照指定的編碼表中的內容寫入到目的地。
"你好"FileWriter ---> 編碼表 GBK--->數字字節--->目的地。
而字節流處理的數據不一定都是文字數據,所以是不需要指定查表的。
直接在操作文件數據時,就將具體的字節數據寫入到目的地。
fos.flush();使用的是父類OutputStream的flush。該方法什麼都沒有做,只有OutputStram某一些子類定義了該方法的具體內容。
fos.close();//必須有,因爲要關閉資源。
}
需求:讀取一個文件。用字節流。
int ch = fis.read();
System.out.println("ch="+ch);
//一次讀一個,打一個。
int ch = 0;
while((ch=fis.read())!=-1){
System.out.println((char)ch);
}
//讀取並存儲到數組,將數組打印。以該種方式。
byte[] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
//直接創建一個剛剛好的大小的數組緩衝區。
byte[] buf = new byte[fis.available()];//如果文件過大,內存溢出,bang 一聲!慎用!//
fis.read(buf);//將數據存儲到剛剛好的數組中。
System.out.println(new String(buf));
fis.read();
System.out.println(fis.available());//獲取流關聯的文件的字節數。
fis.close();
四、複製mp3
1、自定義數組緩衝區的方式。
public static void copy_1() throws IOException {
//1,讀取流對象,和mp3關聯。
FileInputStream fis = new FileInputStream("C:\\0.mp3");
//2,寫入流對象,明確存儲mp3數據的目的。
FileOutputStream fos = new FileOutputStream("c:\\1.mp3");
//3,定義一個字節緩衝區。
byte[] buf = new byte[1024*8];
int len = 0;
while((len=fis.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
fis.close();
}
2、不建議。使用剛剛好的緩衝區。因爲文件過大會溢出。
public static void copy_3() throws IOException {
FileInputStream fis = new FileInputStream("C:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\3.mp3");
byte[] buf = new byte[fis.available()];
fis.read(buf);
fos.write(buf);
fos.close();
fis.close();
}
3、使用字節流已有的緩衝區。
public static void copy_2() throws IOException {
FileInputStream fis = new FileInputStream("C:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\2.mp3");
BufferedInputStream bufis = new BufferedInputStream(fis);
BufferedOutputStream bufos = new BufferedOutputStream(fos);
int by = 0;
while((by=bufis.read())!=-1){
bufos.write(by);
}
bufos.close();
bufis.close();
}
4、讀一個 寫一個
public static void copy_4() throws IOException {
FileInputStream fis = new FileInputStream("C:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\4.mp3");
int by = 0;
while((by=fis.read())!=-1){
fos.write(by);
}
fos.close();
fis.close();
五、鍵盤錄入
讀取鍵盤錄入。
public static void readKey() throws IOException{
//獲取到讀取鍵盤錄入的流對象。類型是InputStream
InputStream in = System.in;
//用流讀取數據。如果沒有數據錄入,那麼控制檯會一致等待輸入。read方法就是一個阻塞式的方式。
int ch = in.read();
System.out.println("ch="+ch);
int ch1 = in.read();
System.out.println("ch1="+ch1);
int ch2 = in.read();
System.out.println("ch2="+ch2);
in.close();//不需要關閉。
//注意:系統中獲取的流對象都是唯一的,如果將其關閉,就不能在使用了。
//想要在使用只有重新再次運行這個程序纔可以。
//所以一般情況下,從System獲取到的流對象,一般不需要關閉。隨着程序的停止而結束。
// InputStream in2 = System.in;這裏會出錯 。
// int ch4 = in2.read();
// System.out.println("ch4="+ch4);
六、鍵盤錄入練習
需求:讀取鍵盤錄入的數據,將這些數據轉成大寫打印在屏幕上,
如果錄入的是 over, 程序結束。
思路:
1,通過鍵盤錄入讀取字節數據。
2,將這些讀到的字節數據進行存儲以變成字符串。
3,對這個字符串進行判斷。如果不是over就將其轉成大寫打印。如果是over就結束。
public static void readKey2() throws IOException {
//1,獲取鍵盤錄入流對象。
InputStream in = System.in;
//2,定義一個容器用於存儲讀取到的字節。
StringBuilder sb = new StringBuilder();
//3,循環讀取鍵盤。
int ch = 0;
while((ch=in.read())!=-1){
//需要對讀取到的字節進行判斷。
//如果是/r 或者 /n,不存儲,並視爲一次錄入內容結束符。對之前的錄入數據進行判斷。
if(ch=='\r')
continue;
if(ch=='\n'){
String s = sb.toString();
if("over".equals(s)){//記住:如果要使用鍵盤錄入,一定要自定義結束標記。
break;
}
else{
System.out.println(s.toUpperCase());
//清空緩衝區。
sb.delete(0, sb.length());
}
}
else
sb.append((char)ch);
}
}
七、轉換流
既然鍵盤錄入轉成大寫輸出,並判斷over結束的程序中
使用了到對字節數據的存儲,並對回車符進行判斷。
發現這個功能和readLine方法一致。
因爲readLine是字符流BufferedReader對象中過的方法。
而鍵盤錄入是字節流。
能不能將這個字節讀取流轉成字符流呢?因爲BufferedReader只能對字符流進行裝飾 。
這就用到了轉換流
轉換流:
字節流--->字符流的橋樑。InputStreamReader
字符流--->字節流的橋樑。OutputStreamWriter
從鍵盤讀取數據,轉成大寫,顯示在屏幕上。
獲取鍵盤錄入,數據源。
InputStream in = System.in;
爲了處理文字數據方便。將字節數據轉成字符數據.這個功能在轉換流中。InputStreamReader
InputStreamReader isr = new InputStreamReader(in);
//爲了提高了讀取的效率。使用緩衝區。
BufferedReader bufr = new BufferedReader(isr);
//打印到顯示器上。目的。
OutputStream out = System.out;
//因爲要打印到顯示器上的數據都是文字數據。所以必須將這些文字數據轉成字節數據。
//具備這個功能的對象是OutputStreamWriter.
OutputStreamWriter osw = new OutputStreamWriter(out);
//爲了提高寫入的效率。
BufferedWriter bufw = new BufferedWriter(osw);
//頻繁的讀寫操作。
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();//newLine()需要有BufferedWriter對象
bufw.flush();
}
//因爲是從System獲取的流可以不關閉,隨着系統的結束而結束。
bufw.close();
bufr.close();
}
八、流的操作規律:
規律就是四個明確?
1,明確源和目的。
源:InputStream Reader 一定是被讀取的。
目的:OutputStream Writer 一定是被寫入的。
2,處理的數據是否是純文本的數據?
是:使用字符流。Reader Writer
否:使用字節流。 InputStream OutputStream
如果是源並且是純文本,Reader
如果是目的並且是純文本,Writer
到這裏,兩個明確確定完,就可以確定出要使用哪個體系。
接下來,就應該明確具體這個體系要使用哪個具體的對象。
3,明確數據所在的設備:
源設備:
鍵盤(System.in)
硬盤(FileXXX)
內存(數組)
網絡(Socket)
目的設備:
顯示器(控制檯System.out)
硬盤(FileXXX)
內存(數組)
網絡(Socket)
具體使用哪個對象就可以明確了。
4,明確是否需要額外功能?
1,是否需要高效?緩衝區Buffered
2,是否需要轉換?轉換流
後面會學到更多。
九、需求練習
需求1:複製一個文本文件。
直接明確具體對象並創建。
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
然後是頻繁的讀寫操作。
需要額外功能嗎?
需要,高效。 使用緩衝區。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
需求3:讀取鍵盤錄入,存儲到一個文件中。
明確是否是純文本?一般鍵盤錄入的都是文字,所以是純文本的。
InputStream in = System.in;
FileWriter fw = new FileWriter("a.txt");
需要將鍵盤錄入的字節轉成字符。
使用轉換流。而且是 將字節-->字符的轉換流對象。InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
FileWriter fw = new FileWriter("a.txt");
需要,高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new FileWriter("a.txt"));
需求4:讀取一個文本文件,顯示到顯示器上。
FileReader fr = new FileReader("a.txt");
OutputStream out = System.out;
需要將已有的字符數據轉成字節。字符-->字節的橋樑 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 OutputStreamWrier(System.out));
需求5:讀取一個文本文件,將文件中文本按照指定的編碼表UTF-8寫入到另一個文件中。
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
這樣做不行,滿足不了需求,爲什麼呢?
因爲這個兩個對象在操作文本數據,都是用了默認的編碼表。在我的本機中默認碼錶是GBK.而需求中希望寫入到文件的數據是按照utf-8的碼錶。其實這兩個對象就是字節流+默認編碼表。
源對象不變。
FileReader fr = new FileReader("a.txt");
需要目的爲指定編碼表。
這時就要用到轉換流。因爲轉換流中可以指定具體的編碼表。 需要往裏傳遞一個字節流,而且要操作文件,FileOutputStream
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"));