轉載自:http://blog.csdn.net/chq497130800/article/details/8299778
字節流的抽象基類:
InputStream , OutputStream 。字符流的抽象基類:
Reader , Writer 。
注:由這四個類派生出來的子類名稱都是以其父類名作爲子類名的後綴
一、字符流:
1.Writer :
*FileWriter :
//創建該對象的同時創建一個文件,如果該文件已經存在,則覆蓋。
File Writer fw = new FileWriter("demo.txt");
//調用write方法,將數據寫入到輸出流中。
fw.write("abcde");//此方法有很多重載的方法。
//刷新流對象中的緩衝數據。將數據刷新到目的地中。
fw.flush();
//關閉流資源,但是關閉之前會刷新一次內部的緩衝區中的數據。將數據刷新到目的地中。
fw.close();
*對IO異常的處理方式。
利用try,catch,finally語句處理,在finally語句塊中進行關閉流對象。
//若文件存在,則將寫入的文件加到文件的末尾(true||false);
FileWriter(String fileName, boolean append);
2.Reader :
*FileReader :
//創建一個文件讀取流對象,和指定名稱的文件相關聯,要保證該文件是已經存在的,否則異常。
FileReader fr = new FileReader("demo.txt");
*兩種讀取方式:
1.//每次讀取一個字符,如果已達到末尾,則返回-1;
fr.read();
int ch = 0;//循環讀取,直到文件末尾,效率低。
while((ch = fr.read())!=-1){
System.out.println((char)ch);
}
2.//將文件中的字符讀取到字符數組中。返回讀取的字符個數,到文件末尾處返回-1;
public int read(char[] cbuf);
char[] buf = new char[1024];
int num = 0;
while((num = fr.read(buf))!= -1){
//不能使用println,因爲如果字符已經達到1024個,那麼就要換行,與字符在文件中的格式不同。
System.out.print(new String(buf,0,num));
}
*拷貝文本文件原理:
創建一個FileReader(String fileName)數據輸入流對象 fr,
再創建一個FileWriter(String fileNmae)數據輸出流對象 fw,
利用 fr 對象的read方法讀取數據流中的數據,並存入數組(緩衝區)中。
在用 fw 對象的write方法將數組中的數據存入輸出流中,
刷新輸出流的時候就會將文本刷新到目的文件中。
3.BufferedWriter
這個類的作用,將文本寫入字符輸入流,緩衝各個字符,
從而提供單個字符、數組和字符串的高效寫入。
*使用方法:
FileWriter fw = new FileWriter("buf.txt");
//爲了提高字符寫入流效率,加入了緩衝技術。
//只要將需要被提高效率的流對象作爲參數傳遞給緩衝區的構造函數即可。
BufferedWriter bufw = new BufferedWriter(fw);
bufw.newLine();//換行符,跨平臺的。
4.BufferedReader
此類的作用與BufferedWriter基本相同,都是爲了提高效率的。
//爲了提高效率,加入緩衝技術,將字符讀取流對象作爲參數傳遞給緩衝對象的構造函數。
BufferedReader bufr = new BufferedReader(new FileReader("buf.txt"));
readLine();//讀取一個文本行。包含該行內容的字符串,不包含任何行終止符如果已到達流末尾,則返回 null;
*用法:
String line = null;
while((line = bufr.readLine())!= null){
System.out.println(line);
}
5.裝飾設計模式:
*定義:當想要對已有的對象進行功能增強時,可以定義類,將已有對象傳入,基於已有的功能,
並提供加強功能,那麼自定義的該類成爲裝飾類。BufferedWriter與BufferedReader就是
裝飾設計模式,對FileWriter與FileReader的功能加強。
*裝飾和繼承:
繼承雖然可以擴展父類的功能,對父類的方法進行覆寫,但同時也使得整個體系變得臃腫了。
裝飾模式比繼承要靈活,避免了集成體系臃腫,而且降低了類與類之間的關係,
只不過提供了更強功能。裝飾類一般都可以接收被裝飾類的父類和父類的所有子類,
所以裝飾類和被裝飾類通常是都屬於一個體系中的。
二、字節流:
1.OutputStream
*FileOutputStream :
FileOutputStream fos = new FileOutputStream("fos.txt");
//操作字節的write方法不需要刷新輸出流。
fos.write("abcde".getBytes());
2.InputStream
*FileInputStream :
FileInputStream fis = new FileInputStream("fos.txt");
byte[] buf = new byte[1024];
int len = 0;
//讀取的方法與字符流讀取的方法基本相同
while((len = fis.read(buf))!= -1){
System.out.println(new String(buf,0,len);
}
3.BufferedInputStream
4.BufferedOutputStream :
//利用緩衝區對象拷貝MP3實例
public static void copy()throws IOException{
BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("c:\\0.mp3");
BufferedOutputStream bufis = new BufferedOutputStream(new FileOutputStream("d:\\0.mp3");
int by = 0;
while((bu = bufis.read())!=-1){
bufos.write(by);
}
}
*模擬緩衝區字節流讀取方法。
class MyBufferedInputStream{
private InputStream in = null;
private int count = 0;
private int pose = 0;
byte[] buf = new byte[1024];
MybufferedInputStream(InputStream in){
this.in = in;
}
public int myRead(){
if(count == 0){
count = in.read(buf);
if(count<0)//調用InputStream對象方法,如果count爲-1,則表示數據到尾了。
return -1;//返回-1作爲結束標記。
count--;//用來記錄緩衝區字節的剩餘個數
pose++;//用來記錄緩衝區數組buf當前讀取下角標。
//在返回數據的過程中,有可能會讀到字節爲11111111的數,像這樣的數
//值爲-1 ,如果返回-1則表示讀取結束了,但實際上並沒有,所以要對其進行處理。
//因爲byte爲2個字節,存爲int類型的時候,會轉成8個字節,這裏就採用如下方式解決。
// 11111111 11111111 11111111 11111111
//&00000000 00000000 00000000 11111111 8個1的值爲255,或者爲0xff(16進制)
// ---------------------------------------
//=00000000 00000000 00000000 11111111
return b&255;//將每一次從數組中讀取的字節返回出去。
}else if(count > 0 ){
pose++;//每讀取一次,角標加一,作爲指針用。
count--;//記錄數組中字節剩餘個數,每讀取一次則減一。
return b&0xff;
}
}
}
public static void copy()throws IOException{
MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream("c:\\0.mp3");
BufferedOutputStream bufis = new BufferedOutputStream(new FileOutputStream("d:\\0.mp3");
int by = 0;
while((bu = bufis.MyRead())!=-1){
//疑慮:既然MyRead()方法將byte形式轉成int類型,由2個字節轉成8個字節,那豈不是多了4倍
//在此,write方法,會強制將int類型的by數據轉成byte類型並寫入文件中去。
bufos.write(by);
}
}
-----------------------------------------------
讀取鍵盤錄入:
System.out:對應的是標準輸出設備:控制檯。
System.in:對應的標準輸入設備:鍵盤。
InputStream in = System.in;//System.in獲取鍵盤輸入轉換成輸入字節流對象InputStream
char a = in.read();//調用in對象的讀取字節的方法。此方法爲阻塞式方法,若沒有鍵盤錄入,則等待。
System.out.println('\r'+0);//獲取字符'\r'的ASCII值;
*錄入一行數據,並將改行數據打印,如果錄入數據時over,那麼停止錄入。
public static void main(String[] args)throws IOException{
InputStream in = System.in;
StringBuilder sb = new StringBuilder();//定義一個緩衝區。
while(true){
int ch = in.read();
if(ch=='\r')
continue;
if(ch=='\n'){
if("over".equals(sb.toString()))
break;
System.out.println(sb.toString().toUpperCase());
sb.delete(0,sb.length());//清空緩衝區。
}
else
sb.append((char)ch);
}
}
轉換流:
1.InputStreamReader :將字節流轉換成字符流。
public static void main(String[] args)throws IOException{
InputStream in = System.in;//獲取鍵盤錄入對象。
//將字節流對象轉換成字符流對象,使用轉換流。
InputStreamReader inReader = new InputStreamReader(in);
//爲了提高效率,將字符串進行緩衝區技術高效錄入。
BufferedReader bufr = new BufferedReader(inReader);
String line = null;
while((line = bufr.readLine())!=null){
if("over".equals(line))
break;
System.out.println(line.toUpperCase());
}
bufr.close();
}
2.OutputStreamWriter :字符流通向字節流的橋樑。
public static void main(String[] args)throws IOException{
//爲了提高效率,將字符串進行緩衝區技術高效錄入。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
//System.out爲輸出流對象,輸出到控制檯上,同樣也可以將此對象替換爲指向文件的。
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while((line = bufr.readLine())!=null){
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.flush();//因爲字符流將數據存入緩衝區內,所以需要刷新緩衝區。
}
bufr.close();
bufw.close();
}
3.流操作的基本規律:
流對象有很多,到底該用哪一個?
通過兩個明確來完成:
1.明確源和目的。
源:輸入流,inputStream Reader .
目的:輸出流,OutputStream Writer.
2.操作的數據是否是純文本,
是:字符流。
不是:字節流。
3.當體系明確後,再明確要用哪個具體類的對象。
通過設備來進行區分:
源設備:內存,硬盤,鍵盤。
目的設備:內存,硬盤,控制檯。
如果在存儲的時候需要對編碼格式進行改變,則需要用如下方式。
OutputStreamWriter osw = new OutputStreamWriter(FileOutputStream("demo.txt"),"utf-8");
setIn(InputStream in) 重新分配“標準”輸入流。將原有的System.in對象改爲重新分配的輸入流對象。
setOut(PrintStream out) 重新分配“標準”輸出流。將原有的System.out對象該爲重新分配的輸出流。
------------------------------------------------
File 類:用來將文件或者文件夾封裝成對象的類。
*構造函數:
//將a.txt封裝成file對象,可以將已有的和未出現的文件或者文件夾封裝成對象。
File f1 = new File("a.txt");
//將目錄與文件名分開傳遞。
File f2 = new File("c:\\abc",str);
File f2 = new File("c:"+File.separator+"abc",str);//File.separator爲目錄分割符,跨平臺使用。
*File類常見方法:
1.創建。 boolean createNewFile();//若文件不存在則創建並返回true,反之亦然。
boolean mkdir();創建單級目錄。
boolean mkdirs();創建多級目錄。
2.刪除。 boolean delete();//若文件存在則刪除返回true,反之亦然。
void deleteOnExit();//虛擬機終止時,將指定文件刪除。
3.判斷。 boolean exists() :文件是否存在。
boolean isDirectory();//判斷所指對象是不是目錄,在判斷之前一定要先判斷文件或目錄是否存在。
boolean isFile();//判斷所指對象時不時文件,在判斷前,要先判斷存在與否。
boolean isAbsolute();//判斷是否是絕對路徑。即使文件不存在也能判斷。
4.獲取。 String getPath();//獲取文件對象的相對路徑。
String getAbsolutePath();//獲取絕對路徑。
String getParent();創建文件的時候用的是絕對路徑,則此方法返回其父目錄,如果創建文件對象時用的是
文件名,則返回空。意思就是返回文件名的所有上級,但與文件創建路徑有關。
getParent()與getName();放在一起就是絕對路徑。
long lastModified();//最後一次修改時間。
File[] listFiles();//獲取指定目錄下的所有文件及文件夾。
String[] list();//獲取指定目錄下的所有文件及文件夾。
遞歸調用需要注意:
一定要有限定條件,否則將會務休止的遞歸下去。同時也要注意遞歸的次數,防止內存溢出。
5.刪除一個帶內容的目錄。
刪除原理:在window中,刪除目錄是從裏面往外面刪除的。既然是從裏往外刪除,就要用到遞歸。
public static void removeDir(File dir){
File[] files = dir.listFiles();
for(int x = 0;x<files.length;x++){
if(files[x].isDirectory())
removeDir(files[x]);
else
System.out.println(files[x].toString()+"...file...."+files[x].delete());
}
System.out.println(dir+"...dir..."+dir.delete());
}
-------------------------------------------------------
Properties 是 Hashtable的子類。
也就是說,它具備map集合的特點,而且它裏面存儲的鍵值對都是字符串。
是集合與IO技術相結合的容器,該對象特點:可以用於鍵值對形式的配置文件。
Set<T> stringPropertyNames();返回鍵的set集合。
*本地配置文件加載到Properties對象中。
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("info.txt");
prop.load(fis);//將輸入流加載到prop對象中。
prop.setProperty(key,value);//修改集合中相應鍵值。
FileOutputStream fos = new FileOutputStream("info.txt");
prop.store(fos,"comments");//將修改後的內容保存到文件中,並加comments註釋。
------------------------------------------------------
IO包中的其他類:
1.打印流對象:
打印流:該流提供了打印方法,可以講各種數據類型的數據都原樣打印。
*字節打印流:
PrintStream :構造函數可以接收的類型:
1.file對象。 File
2.字符串路徑。 String
3.字節輸出流, OutputStream
*字符打印流:
PrintWriter :構造函數可以接收的參數類型:
1.File對象, File
2.字符串路徑, String
3.字節輸出流, OutputStream
4.字符輸出流, Writer
*對應一個小實例:
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.close();
bufr.close();
}
2.合併流(序列流): SequenceInputStream
構造函數: SequenceInputStream(Enumeration<? extends InputStream> e)
SequenceInputStream(InputStream s1, InputStream s2)
有如下小實例:
public static void main(String[] args) throws IOException{
//爲什麼會用Vector,因爲此集合是通過Enumeration遍歷的。可以返回一個枚舉對象。
Vector<FileInputStream> v = new Vector<FileInputStream>();
v.add(new FileInputStream("c:\\1.txt"));//添加文件輸入流
v.add(new FileInputStream("c:\\2.txt"));
v.add(new FileInputStream("c:\\3.txt"));
Enumeration<FileInputStream> en = v.elements();
//通過構造函數,將三股輸入流合併爲一股輸入流。
SequenceInputStream sis = new SequenceInputStream(en);
//定義目的;
FileOutputStream fos = new FileOutputStream("c:\\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();
}
切割文件:
切割小實例:
public static void splitFile()throws IOException{
FileInputStream fis = new FileInputStream("c:\\1.bmp");
FileOutputStream fos = null;
byte[] buf = new byte[1024];
int len = 0;
int count = 1;
while((len=fis.read(buf))!=-1){
fos = new FileOutputStream("c:\\splitfile\\"+count+".part");
fos.write(buf,0,len);
fos.close();
}
fis.close();
}
3.序列化對象:ObjectInputStream、ObjectOutputStream
對象序列化:
*對於序列化的一些規定:靜態成員不能被序列化,如果對與非靜態的成員也不想進行序列化的話,
被 transient 修飾的成員將不被序列化,只在堆內存中存在,不會被序列化到文件中。
class ObjectStreamDemo{
public static void main(String[] args)throws Exception{
readObj();
}
//讀取序列化對象文件,並解析
public static void readObj()throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
Person p = (Person)ois.readObject();
System.out.println(p);
ois.close();
}
//將對象存入文件中,進行持久化操作。
public static void writeObj()throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
oos.writeObject(new Person("lisi",39));
oos.close();
}
}
//序列化對象Person
class Person implements Serializable{
//自定義的序列號,防止對象內部有改動時產生的序列號與之前不一致。
public static final long serialVersionUID = 42L;
private String name;
private int age;
static String country = "cn";
Person(String name,int age,String country){
this.name = name;
this.age = age;
this.country = con;
}
}
4.管道流:PipedInputStream、PipedOutputStream.
特點:管道輸入流應該連接到管道輸出流;管道輸入流提供要寫入管道輸出流的所有數據字節。通常,
數據由某個線程從 PipedInputStream 對象讀取,並由其他線程將其寫入到相應的 PipedOutputStream。
不建議對這兩個對象嘗試使用單個線程,因爲這樣可能死鎖線程。
class Read implements Runnable{
private PipedInputStream in;
Read(PipedInputStream in){
this.in = in;
}
//讀取流線程
public void run(){
try{
byte[] buf = new byte[1024];
int len = in.read(buf);
String s = new String(buf,0,len);
System.out.println(s);
in.close();
}catch(IOException e){
throw new RuntimeException("管道讀取流失敗");
}
}
}
class Write implements Runnable{
private PipedOutputStream out;
Write(PipedOutputStream out){
this.out= out;
}
//輸入流線程
public void run(){
try{
out.write("piped lai la");
out.close();//本應放在finally中,在此簡寫。
}catch(IOException e){
throw new RuntimeException("管道輸出流失敗");
}
}
}
class PipedStreamDemo{
public static void main(String[] args)throws IOException{
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream();
in.connect(out);//將輸入與輸出關聯。
new Thread(new Read(in)).start();
new Thread(new Write(out)).start();
}
}
5.RandomAccessFile :操作文件
介紹:該類不是IO體系中子類,而是直接繼承自Object,但是他是IO包中的成員,因爲它具備讀和寫功能。
內部封裝了一個數組,而且通過指針對數組的元素進行操作。可以通過getFilePointer獲取指針位置,
同時可以通過seek改變指針的位置。
其實完成讀寫的原理就是內部封裝了字節輸入流和輸出流。
通過構造函數可以看書,該類只能操作文件。而且操作文件有模式:只讀,讀寫等
可以實現分段寫入,例如多線程下載軟件,多個線程同時向同一個硬盤下載文件,就需要用到此類。
seek(offset)調整對象中指針位置。
6.DataOutputStream:操作java基本數據類型
DataInputStream:
介紹:數據輸出流允許應用程序以適當方式將基本 Java 數據類型寫入輸出流中。然後,
應用程序可以使用數據輸入流將數據讀入。
7.ByteArrayInputStream:操作字節數組
ByteArrayOutputStream:
介紹:ByteArrayInputStream在構造的時候,需要接受數據源,而且數據源是一個字節數組。
ByteArrayOutputStream:在構造的時候,不用定義數據目的地,因爲該對象中已經內部封裝了可變長度的字節數組。
這就是數數據目的地。
因爲這兩個流對象都操作數組,並沒有使用系統資源,所以不用進行close關閉。