-----------android培訓、java培訓、java學習型技術博客、期待與您交流!------------
概述
IO流:即InputOutput的縮寫。其實就是輸入輸出技術( Input Output),即將數據,從一個設備存儲到另一個設備。
數據就像水,從管道一端,流向另一端,IO流技術
IO可以實現互聯網的文件上傳和下載
JAVA中的IO
Java是一種面向對象的語言,所以也將IO技術也封裝成對象來使用,所以java中的IO對象也被稱爲IO流對象。
所有的IO技術,類都是存放在java.io包中的。
對應數據不同,文件不同,流對象出現了差異。
從程序中,將數據輸入存儲到文件,屬於輸出流過程,是寫文件過程。
從文件中,將數據放到程序中,屬於輸入流過程,是讀取文件過程。
IO流對象分類
按照操作的文件類型分類
字節流
操作文件的時候,每次都是操作單1字節,無論讀取還是寫入都是針對1個字節,8個二進制進行的操作。
所有的文件,都是採用字節組成,實現操作所有類型的文件。所以流對象都不可以操作文件夾。
字符流
計算機中非常特殊的一類文件——文本文件,其最大的特點就是人類可以直接識別.字符流出現的目的,就是爲了方便人們操作文本文件。
每次無論讀取和寫入,操作的都是1個字符,2個字節,16個二進制,並且它在操作當中都會去去查詢本機默認編碼表 GBK兼容ASCII
專門處理文本文件,其他文件不能處理,若強制使用會導致數據的丟失。
常見文本文件: 記事本txt java html
按照數據操作方向分類(流向分類)
輸出流 Output
從JAVA程序中,寫數據出去。一般用於寫文件。
輸入流 Input
從外部設備,讀取文件到JAVA程序中來。一般用於用於讀取文件。
IO流的常用基類:
1)字節流的抽象基流:InputStream和OutputStream
2)字符流的抽象基流:Reader和Writer
注:此四個類派生出來的子類名稱都是以父類名作爲子類名的後綴,以前綴爲其功能;如InputStream子類FileInputStream;Reader子類FileReader
字節輸入流
InputStream,讀取文本文件,核心方法是read()
//創建字節輸入流子類對象,傳遞文件名
FileInputStream fis = new FileInputStream("c:\\1.txt");
//調用方法read讀取文件,傳遞字節數組
byte[] bytes = new byte[1024];//長度 1024整數倍
//定義變量,int類型,接收read方法返回值
int len = 0 ;
while (( len = fis.read(bytes) )!= -1){
//數組輸出
System.out.print(new String(bytes,0,len));
字節輸出流
OutputStream,寫文件,核心方法是 write()
public static void main(String[] args) throws IOException{
//創建輸出流子類對象,構造方法中,寫字符串文件名
/*
* new 字節輸出流對象
* 創建對象在堆內存
* 創建文件,文件已經存在,覆蓋
* 拋出文件找不到異常
* 創建文件功能IO流找操作系統做的
*/
FileOutputStream fos = new FileOutputStream("c:\\1.txt");
//調用方法write寫數據, 只能寫字節
fos.write(97);
//調用write方法,寫字節數組,String類的方法getBytes()
fos.write("你好".getBytes());
//調用Write方法,寫,字節數組一部分
byte[] bytes = {100,101,102,103,104};
fos.write(bytes,0,bytes.length);
//關閉資源
fos.close();
}
測試用例:
/*
* 使用方法遞歸,實現全路徑的遍歷
* 文件和所有的子目錄
*/
import java.io.*;
public class GetAllDirDemo {
public static void main(String[] args) {
//deleteAllDir(new File("c:\\demo"));
}
/*
* 遞歸,刪除目錄
* 小心一點
*/
public static void deleteAllDir(File source){
//使用方法listFiles()獲取數據源目錄的下內容
File[] files = source.listFiles();
for(File f : files){
//判斷f是不是目錄,如果是進去
if(f.isDirectory())
deleteAllDir(f);
else
//不是目錄,是文件,走刪除
f.delete();
}
//刪除目錄
source.delete();
}
/*
* 定義方法,實現目錄遍歷
* 遍歷那一個目錄,參數傳遞
*/
public static void getAllDir(File source){
System.out.println(source);
//使用方法listFiles()獲取數據源目錄的下內容
File[] files = source.listFiles();
for(File f : files){
//遍歷過程中,判斷變量f獲取到的路徑是不是文件夾
if(f.isDirectory())
//如果是文件夾,進去遍歷
//遞歸調用,將f遍歷到的目錄,傳遞給自己方法
getAllDir(f);
else
System.out.println(f);
}
}
}
/*
練習:
複製一個圖片
思路:
1、用字節讀取流對象和圖片關聯。
2、用字節寫入流對象創建一個圖片文件。用於存儲獲取到的圖片數據。
3、通過循環讀寫,完成數據的存儲。
4、關閉資源。
*/
import java.io.*;
class CopyPic
{
public static void main(String[] args)
{
//常用方法複製
byteArrayCopy();
//利用輸入流的available方法進行復制
availableCopy();
//利用available方法對複製文件大小有限制,慎用
}
//使用available方法進行復制
public static void availableCopy()
{
FileInputStream fis=null;
FileOutputStream fos=null;
try
{
//關聯要複製的文件
fis=new FileInputStream("C:/Users/asus/Desktop/1.jpg");
//指定複製的路徑
fos=new FileOutputStream("C:/Users/asus/Desktop/3.jpg");
//利用available方法指定數組長度
byte[] b=new byte[fis.available()];
fis.read(b);//複製關聯文件數據
fos.write(b);//粘貼到指定路徑
}
catch (IOException e)
{
throw new RuntimeException("圖片複製失敗");
}
finally
{
try
{
if(fis!=null)
fis.close();//關閉輸入字節流
}
catch (IOException e)
{
throw new RuntimeException("讀取字節流關閉失敗");
}
try
{
if(fos!=null)
fos.close();//關閉輸出字節流
}
catch (IOException e)
{
throw new RuntimeException("寫入字節流關閉失敗");
}
}
}
//使用讀數組方式進行復制
public static void byteArrayCopy()
{
FileInputStream fis=null;
FileOutputStream fos=null;
try
{
//關聯要複製的文件
fis=new FileInputStream("C:/Users/asus/Desktop/1.jpg");
//指定複製的路徑
fos=new FileOutputStream("C:/Users/asus/Desktop/2.jpg");
//利用數組的讀取方式
byte[] b=new byte[1024];
int len=0;
while ((len=fis.read(b))!=-1)//複製文件的全部數據
{
fos.write(b,0,len);//粘貼到指定路徑
}
}
catch (IOException e)
{
throw new RuntimeException("圖片複製失敗");
}
finally
{
try
{
if(fis!=null)
fis.close();//關閉輸入字節流
}
catch (IOException e)
{
throw new RuntimeException("讀取字節流關閉失敗");
}
try
{
if(fos!=null)
fos.close();//關閉輸出字節流
}
catch (IOException e)
{
throw new RuntimeException("寫入字節流關閉失敗");
}
}
}
}
字符流
簡述
1、字符流中的對象融合了編碼表。使用的是默認的編碼,即當前系統的編碼。
2、字符流只用於處理文字數據,而字節流可以處理媒體數據。
3、既然IO流是用於操作數據的,那麼數據的最常見體現形式是文件。查看API,找到一個專門用於操作文件的Writer子類對象:FileWriter。 後綴是父類名。前綴名是流對象的功能。該流對象一被初始化,就必須有被操作的文件存在。
寫入字符流
步驟:
a、創建一個FileWriter對象,該對象一被初始化,就必須要明確被操作的文件。且該目錄下如果已有同名文件,則同名文件將被覆蓋。其實該步就是在明確數據要存放的目的地。
b、調用write(String s)方法,將字符串寫入到流中。
c、調用flush()方法,刷新該流的緩衝,將數據刷新到目的地中。
d、調用close()方法,關閉流資源。但是關閉前會刷新一次內部的緩衝數據,並將數據刷新到目的地中。
close()和flush()區別:
flush()刷新後,流可以繼續使用;
而close()刷新後,將會關閉流,不可再寫入字符流。
注意:
1、其實java自身不能寫入數據,而是調用系統內部方式完成數據的書寫,使用系統資源後,一定要關閉資源。
2、文件的數據的續寫是通過構造函數 FileWriter(Strings,boolean append),在創建對象時,傳遞一個true參數,代表不覆蓋已有的文件。並在已有文件的末尾處進行數據續寫。(windows系統中的文件內換行用\r\n兩個轉義字符表示,在linux系統中只用\n表示換行)
3、由於在創建對象時,需要指定創建文件位置,如果指定的位置不存在,就會發生IOException異常,所以在整個步驟中,需要對IO異常進行try處理。
演示用例:
/*
練習:
複製一個圖片
思路:
1、用字節讀取流對象和圖片關聯。
2、用字節寫入流對象創建一個圖片文件。用於存儲獲取到的圖片數據。
3、通過循環讀寫,完成數據的存儲。
4、關閉資源。
*/
import java.io.*;
class CopyPic
{
public static void main(String[] args)
{
//常用方法複製
byteArrayCopy();
//利用輸入流的available方法進行復制
availableCopy();
//利用available方法對複製文件大小有限制,慎用
}
//使用available方法進行復制
public static void availableCopy()
{
FileInputStream fis=null;
FileOutputStream fos=null;
try
{
//關聯要複製的文件
fis=new FileInputStream("C:/Users/asus/Desktop/1.jpg");
//指定複製的路徑
fos=new FileOutputStream("C:/Users/asus/Desktop/3.jpg");
//利用available方法指定數組長度
byte[] b=new byte[fis.available()];
fis.read(b);//複製關聯文件數據
fos.write(b);//粘貼到指定路徑
}
catch (IOException e)
{
throw new RuntimeException("圖片複製失敗");
}
finally
{
try
{
if(fis!=null)
fis.close();//關閉輸入字節流
}
catch (IOException e)
{
throw new RuntimeException("讀取字節流關閉失敗");
}
try
{
if(fos!=null)
fos.close();//關閉輸出字節流
}
catch (IOException e)
{
throw new RuntimeException("寫入字節流關閉失敗");
}
}
}
//使用讀數組方式進行復制
public static void byteArrayCopy()
{
FileInputStream fis=null;
FileOutputStream fos=null;
try
{
//關聯要複製的文件
fis=new FileInputStream("C:/Users/asus/Desktop/1.jpg");
//指定複製的路徑
fos=new FileOutputStream("C:/Users/asus/Desktop/2.jpg");
//利用數組的讀取方式
byte[] b=new byte[1024];
int len=0;
while ((len=fis.read(b))!=-1)//複製文件的全部數據
{
fos.write(b,0,len);//粘貼到指定路徑
}
}
catch (IOException e)
{
throw new RuntimeException("圖片複製失敗");
}
finally
{
try
{
if(fis!=null)
fis.close();//關閉輸入字節流
}
catch (IOException e)
{
throw new RuntimeException("讀取字節流關閉失敗");
}
try
{
if(fos!=null)
fos.close();//關閉輸出字節流
}
catch (IOException e)
{
throw new RuntimeException("寫入字節流關閉失敗");
}
}
}
}
讀取字符流
步驟:
1)創建一個文件讀取流對象,和指定名稱的文件相關聯。要保證該文件已經存在,若不存在,將會發生異常FileNotFoundException。
2)調用讀取流對象的read()方法。read():一次讀一個字符,且會繼續往下讀。
第一種方式:讀取單個字符。第二種方式:通過字符數組進行讀取。
3)讀取後要調用close方法將流資源關閉。
演示用例:
import java.io.*;
class FileReaderDemo
{
public static void main(String[] args)
{
//第一種讀取方式
singleCharReader();
//第二種讀取方式
arrayCharReader();
}
//第一種讀取方式,單個字符讀取
public static void singleCharReader()
{
FileReader fr=null;
try
{
fr=new FileReader("demo.txt");//讀取文件位置和文件名
for (int ch=0;(ch=fr.read())!=-1 ; )
{
System.out.print((char)ch);
}
}
catch (IOException e)
{
throw new RuntimeException("讀取失敗");
}
finally
{
try
{
if(fr!=null)
fr.close();//關閉讀取流資源
}
catch (IOException e)
{
}
}
}
//第二種讀取方式,通過字符數組進行讀取
public static void arrayCharReader()
{
FileReader fr=null;
char[] arr=new char[1024];//定義一個字符數組,用於臨時存儲讀取的字符
try
{
//讀取一個.java文件,並打印在控制檯上。
fr=new FileReader("FileReaderDemo.java");
for (int len=0;(len=fr.read(arr))!=-1 ; )
{
//顯示字符數組的字符個數
System.out.print(new String(arr,0,len));
}
}
catch (IOException e)
{
throw new RuntimeException("讀取失敗");
}
finally
{
if(fr!=null)
try
{
fr.close();
}
catch (IOException e)
{
}
}
}
}
注意:
1、定義文件路徑時,可以用“/”或者“\\”。
2、在創建一個文件時,如果目錄下有同名文件將被覆蓋。
3、在讀取文件時,必須保證該文件已存在,否則出異常。
小練習:文本文件的拷貝
/*
需求:
將c盤一個文本文件複製到e盤、
複製的原理:
其實就是將c盤下的文件數據存儲到e盤的一個文件中。
步驟:
1、在e盤創建一個文件。用於存儲c盤文件中的數據。
2、定義讀取流和c盤文件關聯。
3、通過不斷的讀寫完成數據存儲。
4、關閉資源。
*/
import java.io.*;
class ReaderWriterTest
{
publicstatic void main(String[] args)
{
//調用複製方法
//copy_1();
copy_2();
}
//用第一種讀取方式進行復制
publicstatic void copy_1()
{
FileWriterfw=null;
FileReaderfr=null;
try
{
//關聯讀取和寫入的文件
fw=newFileWriter("E:\\HelloWorld.java");
fr=newFileReader("C:\\HelloWorld.java");
for(int ch=0;(ch=fr.read())!=-1 ; )
{
fw.write(ch);//一個字符一個字符寫入
}
}
catch(IOException e)
{
thrownew RuntimeException("讀寫失敗");
}
finally
{
if(fr!=null)
try
{
fr.close();//對讀取流和寫入流分別關閉
}
catch(IOException e)
{
}
if(fw!=null)
try
{
fw.close();
}
catch(IOException e)
{
}
}
}
//第二種讀取方式進行復制
publicstatic void copy_2()
{
FileWriterfw=null;
FileReaderfr=null;
try
{
//關聯讀取和寫入的文件
fw=newFileWriter("E:\\HelloWorld.java");
fr=newFileReader("C:\\HelloWorld.java");
char[]arr=new char[1024];
for(int len=0;(len=fr.read(arr))!=-1 ; )
{
fw.write(arr,0,len);//利用數組一次存入數組中的字符
}
}
catch(IOException e)
{
thrownew RuntimeException("讀寫失敗");
}
finally
{
try
{
if(fr!=null)
fr.close();
}
catch(IOException e)
{
}
finally
{
if(fw!=null)
try
{
fw.close();
}
catch(IOException e)
{
}
}
}
}
}
字符流的緩衝區——BufferedReader和BufferedWriter
1、緩衝區的出現:提高了流的讀寫效率,所以在緩衝區創建前,要先創建流對象。即先將流對象初始化到構造函數中。
2、緩衝技術原理:此對象中封裝了數組,將數據存入,再一次性取出。
3、寫入流緩衝區BufferedWriter的步驟:
1)創建一個字符寫入流對象。
如:FileWriter fw=newFileWriter("buf.txt");
2)爲了提高字符寫入流效率。加入緩衝技術。只要將需要被提高效率的流對象作爲參數傳遞給緩衝區的構造函數即可。
如: BufferedWriter bufw =new BufferedWriter(fw);
3)調用write方法寫入數據到指定文件
如:bufw.write("adfg");
記住,只要用到緩衝區,就要記得刷新。(關閉流同樣會刷新,但爲了排除意外事故,保證數據存在,建議寫入一次就刷新一次)
如:bufw.flush();
4)其實關閉緩衝區,就是在關閉緩衝區中的流對象。
如: bufw.close();
小知識:BufferedWriter緩衝區中提供了一個跨平臺的換行符:newLine();可以在不同操作系統上調用,用作數據換行。
如:bufw.newLine();
4、讀取流緩衝區BufferedReader
該緩衝區提供了一個一次讀一行的方法readLine,方便於堆文本數據的獲取,當返回null時表示讀到文件末尾。readLine方法返回的時候,只返回回車符之前的數據內容。並不返回回車符。
readLine方法原理:
無論是讀一行。或者讀取多個字符。其實最終都是在在硬盤上一個一個讀取。所以最終使用的還是read方法一次讀一個的方法。
步驟:
1)創建一個讀取流對象和文件相關聯
如: FileReader fr=newFileReader("buf.txt");
2)爲了提高效率。加入緩衝技術。將字符讀取流對象作爲參數傳遞給緩衝區對象的構造函數。
如: BufferedReader bufr=new BufferedReader(fr);
3)調用該緩衝區提供的readLine方法一行一行讀取,如果到達文件末尾,則返回null
如: String s=bufr.readLine();
4)關閉流資源
如: bufr.close();、
示例:通過緩衝區複製一個文本文件。
/*
需求:使用緩衝技術copy一個文本文件
*/
import java.io.*;
class BufferedCopyDemo
{
public static void main(String[] args)
{
BufferedWriter bfw=null;
BufferedReader bfr=null;
try
{
//創建寫緩衝對象
bfw=new BufferedWriter(new FileWriter("ReaderWriterTest_copy.txt"));
//創建讀緩衝對象
bfr=new BufferedReader(new FileReader("ReaderWriterTest.java"));
//利用BufferedReader提供的readLine方法獲取整行的有效字符。直到全部獲取
for (String line=null; (line=bfr.readLine())!=null; )
{
bfw.write(line);//寫入指定文件中
bfw.newLine();//換行
bfw.flush();//將緩衝區數據刷到指定文件中
}
}
catch (IOException e)
{
throw new RuntimeException("文件copy失敗");
}
finally
{
if(bfw!=null)
try
{
bfw.close();//關閉寫入流
}
catch (IOException e)
{
throw new RuntimeException("寫入流關閉失敗");
}
if(bfr!=null)
try
{
bfr.close();//關閉讀取流
}
catch (IOException e)
{
throw new RuntimeException("讀取流關閉失敗");
}
}
}
}
5、自定義BufferedReader:
原理:
可根據BufferedReader類中特有一行一行讀取方法readLine()的原理,自定義一個類中包含相同功能的方法
步驟:
a、初始化自定義的類,加入流對象。
b、定義一個臨時容器,原BufferedReader封裝的是字符數組,此類中可定義一個StringBuilder的容器,最終可實現字符串的提取。
代碼:
/*
需求:根據readLine方法原理,模擬BufferedReader寫一個自己的MyBufferedReader
*/
import java.io.*;
//自定義緩衝類
class MyBufferedReader extends Reader
{
private Reader r;//定義接收的流對象
MyBufferedReader(Reader r)
{
this.r=r;
}
//自定義整行讀取
public String myReadLine()throws IOException
{
//創建一個容器,用來存儲一行的字符
StringBuilder sb =new StringBuilder();
//一個字符一個字符讀取
for (int ch=0;(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();
return null;
}
//複寫父類中的抽象方法
public int read(char[] cbuf, int off, int len) throws IOException
{
return r.read(cbuf,off,len);
}
//複寫父類的close方法
public void close()throws IOException
{
r.close();
}
}
//測試MyBufferedReader
class MyBufferedReaderDemo
{
public static void main(String[] args)
{
MyBufferedReader mbr=null;
try
{
mbr=new MyBufferedReader(new FileReader("BufferedCopyDemo.java"));
for (String line=null;(line=mbr.myReadLine())!=null ; )
{
System.out.println(line);//顯示效果
}
}
catch (IOException e)
{
throw new RuntimeException("讀取數據失敗");
}
finally
{
try
{
if(mbr!=null)
mbr.close();
}
catch (IOException e)
{
throw new RuntimeException("讀取流關閉失敗");
}
}
}
}
6、LineNumberReader
在BufferedReader中有個直接的子類LineNumberReader,其中有特有的方法獲取和設置行號:
setLineNumber();//設置初始行號
getLineNumber();//獲取行號
示例:
/*
需求:利用LineNumberReader的特有方法去設置和獲取文件中數據的行號
*/
class LineNumberReaderDemo
{
public static void main(String[] args)
{
LineNumberReader lnr=null;
try
{
//將讀取流對象傳入
lnr=new LineNumberReader(new FileReader("LineNumberReaderDemo.java"));
lnr.setLineNumber(100);//設置開始行號
for (String line=null;(line=lnr.readLine())!=null ; )
{
System.out.println(lnr.getLineNumber()+":"+line);//打印每行行號和字符
}
}
catch (IOException e)
{
throw new RuntimeException("讀取數據失敗");
}
finally
{
try
{
if(lnr!=null)
lnr.close();
}
catch (IOException e)
{
throw new RuntimeException("讀取流關閉失敗");
}
}
}
}
裝飾設計模式
1、簡述
當想對已有對象進行功能增強時,可定義類:將已有對象傳入,基於已有對象的功能,並提供加強功能,那麼自定義的該類稱之爲裝飾類。
2、特點
裝飾類通常都會通過構造方法接收被裝飾的對象,並基於被裝飾的對象的功能,提供更強的功能。
3、裝飾和繼承的區別:
1)裝飾模式比繼承要靈活。避免了繼承體系的臃腫,且降低了類與類之間的關係。
2)裝飾類因爲增強已有對象,具備的功能和已有的是相同的,只不過提供了更強的功能,所以裝飾類和被裝飾的類通常都是屬於一個體系。
3)從繼承結構轉爲組合結構。
注:在定義類的時候,不要以繼承爲主;可通過裝飾設計模式進行增強類功能。靈活性較強,當裝飾類中的功能不適合,可再使用被裝飾類的功能。
示例:上面講到的MyBufferedReader的例子就是最好的裝飾設計模式的例子。
字節流緩衝區
同樣是提高了字節流的讀寫效率。
1、讀寫特點:
read():會將字節byte型值提升爲int型值
write():會將int型強轉爲byte型,即保留二進制數的最後八位。
2、原理:將數據拷貝一部分,讀取一部分,循環,直到數據全部讀取完畢。
1)先從數據中抓取固定數組長度的字節,存入定義的數組中,再通過然後再通過read()方法讀取數組中的元素,存入緩衝區。
2)循環這個動作,直到最後取出一組數據存入數組,可能數組並未填滿,同樣也取出包含的元素。
3)每次取出的時候,都有一個指針在移動,取到數組結尾就自動回到數組頭部,這樣指針在自增。
4)取出的時候,數組中的元素在減少,取出一個,就減少一個,直到減到0即元素取完。
5)當文件中的全部數據都被讀取出時,read()方法就返回-1。
3、自定義讀取字節流緩衝區
需求:根據字節流緩衝區的原理,自定義一個字節流緩衝區。
注意:
1、字節流的讀一個字節的read方法爲什麼返回值類型不是byte,而是int。
因爲有可能會讀到連續8個二進制1的情況,8個二進制1對應的十進制是-1.那麼就會數據還沒有讀完,就結束的情況。因爲我們判斷讀取結束是通過結尾標記-1來確定的。
所以,爲了避免這種情況將讀到的字節進行int類型的提升。並在保留原字節數據的情況前面了補了24個0,變成了int類型的數值。而在寫入數據時,只寫該int類型數據的最低8位。
2、byte類型的-1提升爲int類型時還是-1。原因:因爲在bit8個1前面補的全是1導致的。如果在bit8個1前面補0,即可以保留原字節數據不變,又可以避免-1的出現。這時將byte型數據&0xff即255即可。
代碼:
/*
自定義字節流讀取緩衝區
思路:
1、定義一個固定長度的數組
2、定義一個指針和計數器用於讀取數組長度,和計數數組元素是否取完爲0
3、每次將字節數據存入元素要先將數組中的元素取完
*/
import java.io.*;
class MyBufferedInputStream
{
privateInputStream in;
privatebyte[] by=new byte[1024];
privateint count=0,pos=0;
MyBufferedInputStream(InputStreamin)
{
this.in=in;
}
//自定義讀方法,一次讀一個字節
publicint myRead()throws IOException
{
//通過in對象讀取硬盤上數據,並存儲by中。
//存儲在數組中的數據被讀取完,再通過in對象從硬盤上讀取數據
if(count==0)
{
count=in.read(by);
if(count<0)//文件數據全部被讀取出來了
return-1;
pos=0;//初始化指針
byteb=by[pos];
count--;//每被讀一個字節,表示數組中的字節數少一個
pos++;//指針加1
returnb&255;//返回的byte類型提升爲int類型,字節數增加,且高24位被補1,原字節數據改變。
//通過與上255,主動將byte類型提升爲int類型,將高24位補0,原字節數據不變。
//而在輸出字節流寫入數據時,只寫該int類型數據的最低8位。
}
elseif(count>0)//如果數組中的數據沒被讀取完,則繼續讀取
{
byteb=by[pos];
count--;
pos++;
returnb&0xff;
}
return-1;
}
//自定義關閉資源方法
publicvoid close()throws IOException
{
in.close();
}
}
//測試自定義輸入字節流緩衝區
class MyBufferedCopyMp3
{
publicstatic void main(String[] args)
{
longstart=System.currentTimeMillis();
//利用字節流的緩衝區進行復制
copy_2();
longend=System.currentTimeMillis();
System.out.println("複製共用時:"+(end-start)+"毫秒");
}
//使用字節流的緩衝區進行復制
publicstatic void copy_2()
{
BufferedOutputStreambout=null;
MyBufferedInputStreambin=null;
try
{
//關聯複製文件輸入流對象到緩衝區
bin=newMyBufferedInputStream(new FileInputStream("C:/Users/asus/Desktop/一樣的月光.mp3"));
//指定文件粘貼位置的輸出流對象到緩衝區
bout=newBufferedOutputStream(new FileOutputStream("C:/Users/asus/Desktop/一樣的月光2.mp3"));
intby=0;
while((by=bin.myRead())!=-1)
{
bout.write(by);//將緩衝區中的數據寫入指定文件中
}
}
catch(IOException e)
{
thrownew RuntimeException("MP3複製失敗");
}
finally
{
try
{
if(bin!=null)
bin.close();//關閉輸入字節流
}
catch(IOException e)
{
thrownew RuntimeException("讀取字節流關閉失敗");
}
try
{
if(bout!=null)
bout.close();//關閉輸出字節流
}
catch(IOException e)
{
thrownew RuntimeException("寫入字節流關閉失敗");
}
}
}
}
流操作規律
鍵盤錄入
1、標準輸入輸出流
System.in:對應的標準輸入設備,鍵盤。
Ssytem.out:對應的是標準的輸出設備,控制檯。
System.in的類型是InputStream.
System.out的類型是PrintStream是OutputStream的子類FilterOutputStream的子類。
2、整行錄入
當使用輸入流進行鍵盤錄入時,只能一個字節一個字節進行錄入。爲了提高效率,可以自定義一個數組將一行字節進行存儲。當一行錄入完畢,再將一行數據進行顯示。這種正行錄入的方式,和字符流讀一行數據的原理是一樣的。也就是readLine方法。
那麼能不能直接使用readLine方法來完成鍵盤錄入的一行數據的讀取呢?readLine方法是字符流BufferedReader類中方法。而鍵盤錄入的read方法是字節流InputStream的方法。
那麼能不能將字節流轉成字符流再使用字符流緩衝區的readLine方法呢?這就需要用到轉換流了。
3、轉換流
3.1 轉換流的由來:
a、字符流與字節流之間的橋樑
b、方便了字符流與字節流之間的操作
轉換流的應用:
字節流中的數據都是字符時,轉成字符流操作更高效。
3.2 InputStreamReader將字節流通向字符流
a、獲取鍵盤錄入對象。
InputStream in=System.in;
b、將字節流對象轉成字符流對象,使用轉換流。
InputStreamReaderisr=new InputStreamReader(in);
c、爲了提高效率,將字符串進行緩衝區技術高效操作。使用BufferedReader
BufferedReaderbr=new BufferedReader(isr);
//鍵盤錄入最常見寫法
BufferedReaderin=new BufferedReader(new InputStreamReader(System.in));
3.3 OutputStreamWriter字符流通向字節流
字符通向字節:錄入的是字符,存到硬盤上的是字節。步驟和InputStreamReader轉換流一樣。
示例:
/*
需求:將鍵盤錄入的數據,顯示在控制檯,當輸入over時,表示結束
源:鍵盤錄入。
目的:控制檯。
*/
import java.io.*;
class TransStreamDemo
{
public static void main(String[] args)throws IOException
{
//獲取鍵盤錄入對象。
//InputStream in=System.in;
//將字節流對象轉成字符流對象,使用轉換流。
//InputStreamReader isr=new InputStreamReader(in);
//爲了提高效率,將字符串進行緩衝區技術高效操作。使用BufferedReader
//BufferedReader br=new BufferedReader(isr);
//鍵盤錄入最常見寫法
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
//字符流通向字節流
BufferedWriter bw =new BufferedWriter(new OutputStreamWriter(System.out));
String s=null;
while((s=in.readLine())!=null)
{
if("over".equals(s))
break;
bw.write(s.toUpperCase());//寫入數據
bw.newLine();//換行
bw.flush();//刷新
}
bw.close();//關閉流資源
in.close();
}
}
流操作規律
1、
源:鍵盤錄入。
目的:控制檯。
2、需求:想把鍵盤錄入的數據存儲到一個文件中。
源:鍵盤
目的:文件。
使用字節流通向字符流的轉換流(橋樑):InputStreamReader
3、需求:想要將一個文件的數據打印在控制檯上。
源:文件
目的:控制檯
使用字符流通向字節流的轉換流(橋樑):OutputStreamWriter
4、流操作的基本規律:
最痛苦的就是流對象有很多,不知道該用哪一個。
通過三個明確來完成:
4.1 明確源和目的。
源:輸入流。InputStream Reader
目的:輸出流。OutputStream Writer
4.2 操作的數據是否是純文本。
是:字符流
否:字節流
4.3 當體系明確後,再明確要使用哪個具體的對象。通過設備來進行區分:
源設備:內存,硬盤,鍵盤
目的設備:內存,硬盤,控制檯
5、規律體現
5.1 將一個文本文件中數據存儲到另一個文件中。複製文件。
1)源:因爲是源,所以使用讀取流:InputStream和Reader
明確體系:是否操作文本:是,Reader
明確設備:明確要使用該體系中的哪個對象:硬盤上的一個文件。Reader體系中可以操作文件的對象是FileReader
是否需要提高效率:是,加入Reader體系中緩衝區 BufferedReader.
FileReader fr = new FileReader("a.txt");
BufferedReader bufr = new BufferedReader(fr);
2)目的:輸出流:OutputStream和Writer
明確體系:是否操作文本:是,Writer
明確設備:明確要使用該體系中的哪個對象:硬盤上的一個文件。Writer體系中可以操作文件的對象FileWriter。
是否需要提高效率:是,加入Writer體系中緩衝區 BufferedWriter
FileWriter fw = new FileWriter("b.txt");
BufferedWriter bufw = new BufferedWriter(fw);
練習:將一個圖片文件中數據存儲到另一個文件中。複製文件。要按照以上格式自己完成三個明確。
1)源:輸入流,InputStream和Reader
是否是文本?否,InputStream
源設備:硬盤上的一個文件。InputSteam體系中可以操作文件的對象是FileInputSteam
是否需要提供效率:是,BufferedInputStream
BufferedInputSteambis=newBufferedInputStream(newFileInputStream("c:/users/asus/desktop/1.jpg"));
2)目的:輸出流,OutputStream和Writer
是否是文本?否,OutputStream
源設備:硬盤上的文件,FileOutputStream
是否需要提高效率:是,加入BufferedOutputStream
BufferedOutputStreambos=newBufferedOutputStream(newFileOutputStream("c:/users/asus/desktop/2.jpg"));
5.2 需求:將鍵盤錄入的數據保存到一個文件中。
1)源:InputStream和Reader
是不是純文本?是,Reader
設備:鍵盤。對應的對象是System.in。——爲了操作鍵盤的文本數據方便。轉成字符流按照字符串操作是最方便的。所以既然明確了Reader,那麼就將System.in轉換成Reader。用Reader體系中轉換流,InputStreamReader
InputStreamReaderisr = new InputStreamReader(System.in);
需要提高效率嗎?需要,BufferedReader
BufferedReaderbufr = new BufferedReader(isr);
2)目的:OutputStream Writer
是否是存文本?是!Writer。
設備:硬盤。一個文件。使用 FileWriter。
FileWriter fw = newFileWriter("c.txt");
需要提高效率嗎?需要。
BufferedWriter bufw = new BufferedWriter(fw);
5.3 擴展:想要把錄入的數據按照指定的編碼表(UTF-8)(默認編碼表是GBK),將數據存到文件中。
目的:OutputStream Writer
是否是存文本?是!Writer。
設備:硬盤上的一個文件。使用 FileWriter。——但是FileWriter是使用的默認編碼表:GBK。而存儲時,需要加入指定編碼表utf-8。而指定的編碼表只有轉換流可以指定。所以要使用的對象是OutputStreamWriter。
該轉換流對象要接收一個字節輸出流,而且還可以操作的文件的字節輸出流:FileOutputStream
OutputStreamWriter osw =new OutputStreamWriter(newFileOutputStream("d.txt"),"UTF-8");
需要高效嗎?需要,BufferedWriter
BufferedWriter bufw = new BufferedWriter(osw);
記住:
轉換流什麼使用?
字符和字節之間的橋樑。通常,涉及到字符編碼轉換時,需要用到轉換流。
練習:將一個文本數據打印在控制檯上。要按照以上格式自己完成三個明確。
1)源:InputStreamReader
是文本?是:Reader
設備:硬盤。上的文件:FileReader
是否需要提高效率?是:BufferedReader
BufferedReader br=new BufferedReader(newFileReader("1.txt"));
2)目的:OutputStream Writer
是文本?是:Writer
設備:控制檯。對應對象System.out。由於System.out對應的是字節流,所以利用OutputSteamWriter轉換流
是否提高效率?是:BufferedWriter
BufferedWriter bw =new BufferedWriter(newOutputStreamWriter(system.out));
示例:
/*
2、需求:想把鍵盤錄入的數據存儲到一個文件中。
源:鍵盤
目的:文件
把錄入的數據按照指定的編碼表(UTF-8),將數據存到文件中。
3、需求:想要將一個文件的數據打印在控制檯上。
源:文件
目的:控制檯
*/
import java.io.*;
class TransStreamDemo2
{
public static void main(String[] args)throws IOException
{
//鍵盤錄入
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
//存入文件中,按照指定的編碼表(UTF-8)
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("readin1.txt"),"UTF-8"));
String line=null;
while((line=br.readLine())!=null)
{
if("over".equals(line))
break;
bw.write(line);
bw.newLine();
bw.flush();
}
/*
//錄入文件數據
BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("TransStreamDemo2.java")));
//顯示在控制檯
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
String line=null;
while((line=br.readLine())!=null)
{
if("over".equals(line))
break;
bw.write(line);
bw.newLine();
bw.flush();
}
*/
}
}
小知識:
1、異常的日誌信息:
當程序在執行的時候,出現的問題是不希望直接打印給用戶看的,是需要作爲文件存儲起來,方便程序員查看,並及時調整的。
示例:
import java.io.*;
import java.text.*;
import java.util.*;
class ExceptionInfo
{
public static void main(String[] args)
{
try
{
int[] arr =new int[2];
System.out.println(arr[3]);
}
catch (Exception e)
{
try
{
Date d=new Date();//創建時間對象
//時間模塊格式對象
SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String s=sdf.format(d);
PrintStream ps=new PrintStream("info.log");//打印流對象
System.setOut(ps);//修改輸出流設備
ps.println(s);//輸出時間
}
catch (IOException ex)
{
throw new RuntimeException("文件創建失敗");
}
e.printStackTrace(System.out);//將異常信息輸出指定輸出流
}
}
}
2、系統屬性信息存入文本
獲取系統信息:
Properties getProperties()
將信息輸出到指定輸出流中
void list(PrintStream out)
將輸出流中數據存入指定文件中
new PrintStream("systeminfo.txt")
示例:
//將系統屬性信息保存到指定文本中
import java.util.*;
import java.io.*;
class SystemInfo
{
public static void main(String[] args)
{
PrintStream ps = null;
try
{
//獲取系統信息:
Properties pop = System.getProperties();
//創建輸出流對象,將輸出流中數據存入指定文件中
ps = new PrintStream("systeminfo.txt");
//將屬性列表輸出到指定的輸出流
pop.list(ps);
}
catch (Exception e)
{
throw new RuntimeException("獲取系統信息失敗。");
}
}
}
3、通過System類的setIn,setOut方法可以對默認設備進行改變
System.setIn(newFileInputStream(“1.txt”));//將源改成文件1.txt。
System.setOut(newFileOutputStream(“2.txt”));//將目的改成文件2.txt
流的基本應用小結:
· 流是用來處理數據的。
· 處理數據時,一定要先明確數據源,與數據目的地(數據匯)。
· 數據源可以是文件,可以是鍵盤。
· 數據目的地可以是文件、顯示器或者其他設備。
· 而流只是在幫助數據進行傳輸,並對傳輸的數據進行處理,比如過濾處理.轉換處理等。
Properties類
概述
1、Properties是Hashtable的子類,它具備Map集合的特點。而且它裏面還有存儲的鍵值對,都是字符串,無泛型定義。是集合中和IO技術想結合的集合容器。
2、特點:
1)可用於鍵值對形式的配置文件
2)在加載時,需要數據有固定的格式,常用的是:鍵=值
特有方法
1、設置
Object setProperty(String key,String value);
//設置鍵和值,調用Hashtable的方法put
2、獲取
String getProperty(String key);
//指定key搜索value
Set<String> stringPropertyName();
//返回屬性列表的鍵集,存入Set集合
3、加載流和存入流
void load(InputStream ism);
//從輸入字節流中讀取屬性列表(鍵和元素對)。又稱將流中的數據加載進集合。
void load(Readerreader);
//從輸入字符流中讀取屬性列表(鍵和元素對)。又稱將流中的數據加載進集合。
voidlist(PrintStream out);//將屬性列表輸出到指定的輸出流
void store(OutputStreamout,String comments);
//對應load(InputStream )將屬性列表(鍵值對)寫入輸出流。comments屬性列表的描述。
void store(Writerwriter, String comments);
//對應load(Reader)將屬性列表(鍵值對)寫入輸出流。comments屬性列表的描述。
示例
//演示,如何將流中的數據存儲到集合中。
//想要將info.txt中鍵值數據存到集合中進行操作。
/*
1,用一個流和info.txt文件關聯。
2,讀取一行數據,將該行數據用"="進行切割。
3,等號左邊作爲鍵,右邊作爲值。存入到Properties集合中即可。
*/
//將文件數據存儲進Properties集合方法
public static void method()throws IOException
{
//使用字符讀取緩衝流關聯文件
BufferedReader bufr = new BufferedReader(new FileReader("info.txt"));
String line = null;
//定義Properties集合
Properties prop = new Properties();
while((line=bufr.readLine())!=null)
{
String[] arr = line.split("=");//將一行數據以“=”號進行分割
//將=左邊作爲鍵存入,將=右邊作爲值存入
prop.setProperty(arr[0],arr[1]);
}
bufr.close();//關流
System.out.println(prop);
}
練習:
/*
練習:用於記錄應用程序運行次數。如果使用次數已到,那麼給出註冊提示。
分析:
很容易想到的是:計數器。可是該計數器定義在程序中,隨着該應用程序的退出,該計數器也在內存中消失了。
所以要建立一個配置文件,用於記錄該軟件的使用次數。該配置文件使用鍵值對的形式。鍵值對數據是map集合。數據是以文件形式存儲。使用io技術。那麼map+io——>Properties。
思路:1、用讀取流關聯文本信息文件。如果存在則讀取,如果不存在,則創建
2、每次運行,將文件數據存入集合中,讀取值,判斷次數,如果小於等於5次,則次數增加1次,如果大於則輸出提示信息。
3、將值小於等於5次的信息數據存入文件中
*/
import java.util.*;
import java.io.*;
class RunCount
{
public static void main(String[] args)throws IOException
{
int count=runCount();
if(count>5)//如果程序被使用了超過5次,則終止使用,並提示
{
System.out.println("次數到了,交錢!!!!!");
return ;
}
else
System.out.println("程序第"+count+"次Run!");
}
//獲取程序運行的次數
public static int runCount()throws IOException
{
Properties ps=new Properties();//創建集合對象
File file=new File("info.ini");//將文件進行封裝
if(!file.exists())//判斷是否存在
file.createNewFile();
FileReader fr=new FileReader(file);//將文件於讀取流進行關聯
ps.load(fr);//加載流中的文件數據到集合中
int count=0;//定義計數器
String value=ps.getProperty("time");//獲取次數值
if(value!=null)//如過值不等於null,則將其賦值給count
{
count=Integer.parseInt(value);
}
count++;//每啓動一次自增
ps.setProperty("time",count+"");//將次數記錄住集合
FileWriter fw=new FileWriter(file);
ps.store(fw,"");//將集合中的數據存入硬盤文件中
fr.close();//關流
fw.close();
return count;//返回程序啓動的次數
}
}
打印流
概述
1、打印流包括:PrintStream和PrintWriter
2、該流提供了打印方法,可將各種類型的數據都原樣打印。
字節打印流:PrintStream
構造方法中可接收的參數類型:
1、File對象。File
2、字符串路徑:String
3、字符輸出流:OutputStream
字符串打印流:PrintWriter
構造方法中可接受的參數類型
1、File對象:File
2、字符串路徑:String
3、字節輸出流:OutputStream
4、字符輸出流:Writer
示例
import java.io.*;
class PrintStreamDemo
{
public static void main(String[] args) throws IOException
{
//鍵盤錄入
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
//打印流關聯文件,自動刷新
PrintWriter out = new PrintWriter(new FileWriter("a.txt"),true);
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))//結束字符
break;
out.println(line.toUpperCase());
//out.flush();
}
//關流
out.close();
bufr.close();
}
}
序列流
概述
1、SequenceInputStream對多個流進行合併。也被稱爲合併流。
2、常用構造函數
SequenceInputStream(Enumeration<?extends FileInputStream> e)
常見合併多個流文件步驟
1、創建集合,並將流對象添加進集合
2、創建Enumeration對象,將集合元素加入。
3、創建SequenceInputStream對象,合併流對象
4、創建寫入流對象,FileOutputStream關聯寫入文件
5、利用SequenceInputStream對象和FileOutputStream對象讀數據進行反覆讀寫操作。
示例:
/*
SequenceInputStream
合併流
需求:將三個文本文件中的數據合併到一個文本文件中
思路:1、創建一個Vector集合,將三個文本文件字節流添加到集合中
2、創建Enumeration對象,創建SequnceInputStream對象關聯Enumeration
3、輸出流關聯新文本文件
4、反覆讀寫操作
*/
import java.util.*;
import java.io.*;
class SequenceInputStreamDemo
{
public static void main(String[] args)throws IOException
{
Vector<InputStream> ve=new Vector<InputStream>();//創建vector集合,並添加相關流對象
ve.add(new FileInputStream("1.txt"));
ve.add(new FileInputStream("2.txt"));
ve.add(new FileInputStream("3.txt"));
Enumeration<InputStream> en=ve.elements();//創建枚舉對象
SequenceInputStream sis=new SequenceInputStream(en);//合併流
FileOutputStream fos=new FileOutputStream("4.txt");//關聯寫入文件
//反覆讀寫操作
byte[] buf=new byte[1024];
int len=0;
while((len=sis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
//關流
fos.close();
sis.close();
}
}
練習:
/*
切割文件
需求:將一個mp3文件按1M大小切割成幾部分
思路:1、使用文件字節流關聯mp3文件
2、定義一個容器存儲1M大小的數據,當存儲滿時,寫入一個新文件中
*/
import java.util.*;
import java.io.*;
class SplitFile
{
public static void main(String[] args) throws IOException
{
//指定要切割的文件
File file=new File("C:\\Users\\asus\\Desktop\\蘇芮 - 一樣的月光.mp3");
//將指定文件進行切割
splitFile(file);
//指定要合併到的文件
File file1=new File("E:\\Java Study\\Practice\\day20\\splitFile\\一樣的月光.mp3");
//將部分文件進行合併指定文件中
merge(file1);
}
//接收一個文件,將其按1M大小進行切割
public static void splitFile(File file)throws IOException
{
//關聯要切割的文件
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos=null;
//定義1M大小存儲容器
byte[] buf=new byte[1024*1024];
int len=0,x=0;
while ((len=bis.read(buf))!=-1)
{
//每滿1M就寫入一個新文件中
bos=new BufferedOutputStream(new FileOutputStream("E:\\Java Study\\Practice\\day20\\splitFile\\"+(++x)+".part"));
bos.write(buf,0,len);
bos.close();//沒寫完一個文件要記得關流
}
//關流
bis.close();
}
//將部分文件合併爲一個可執行文件
public static void merge(File file)throws IOException
{
//定義一個集合存儲這些部分文件關聯路徑數據
ArrayList<FileInputStream> al=new ArrayList<FileInputStream>();
for (int x=1;x<=6 ; x++)
{
al.add(new FileInputStream("E:\\Java Study\\Practice\\day20\\splitFile\\"+x+".part"));
}
//因爲Enumeration是Vector特有的迭代方法,所以這裏創建一個Enumeration類型的匿名內部類
final ListIterator<FileInputStream> it=al.listIterator();
Enumeration<FileInputStream> en=new Enumeration<FileInputStream>()
{
public boolean hasMoreElements()
{
return it.hasNext();
}
public FileInputStream nextElement()
{
return it.next();
}
};
//關聯枚舉對象
SequenceInputStream sis=new SequenceInputStream(en);
//將合併的文件數據寫入指定文件中
FileOutputStream fos=new FileOutputStream(file);
//定義臨時存儲數據的數組
byte[] buf=new byte[1024];
int len=0;
while((len=sis.read(buf))!=-1)
{
fos.write(buf,0,len);//寫數據
}
//關流
fos.close();
sis.close();
}
}
-----------android培訓、java培訓、java學習型技術博客、期待與您交流!------------