1. 程式人生 > >java io 嘔心瀝血的學習 學了就忘

java io 嘔心瀝血的學習 學了就忘

感覺io的知識,需要學以致用,否則學了就忘,忘了再學.........

看整體結構圖,哇,sb了,是不是有這種感覺,其實無所謂,用的時候找api文件嘛,英文不行,找度娘嘛,別找我就行~


學習幾個常用的吧,希望不會再忘!

1、FileInputStream  和  FileOutputStream

package IO_TEST;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class FileOutputStream_test {
    public static void main(String[] args) {  
        byte[] buffer=new byte[512];   //一次取出的位元組數大小,緩衝區大小  
        int numberRead=0;  
        InputStream input=null;  
        OutputStream out =null;  
        try {  
           input = new FileInputStream("/Users/zhengchao/cctv/File_test.txt");  
           out = new FileOutputStream("/Users/zhengchao/cctv/File_test_out.txt"); //如果檔案不存在會自動建立  
           //每次從INPUT中讀取buffer大小的資料到buffer中
           while ((numberRead=input.read(buffer))!=-1) {  //numberRead的目的在於防止最後一次讀取的位元組小於buffer長度,  
              out.write(buffer, 0, numberRead);           //否則會自動被填充0  
           }  
        } catch (final IOException e) {  
            e.printStackTrace();  
        }finally{  
           try {  
              input.close();  
              out.close();  
           } catch (IOException e) {  
              e.printStackTrace();  
           }  
        }  
    }  
}

這兩個類居然是很多流淚(流類)的父類哦,想都搞清楚,估計真得流淚了,如下:

FileOutputStream直接已知子類:

BufferedOutputStream, CheckedOutputStream, CipherOutputStream, DataOutputStream, DeflaterOutputStream, DigestOutputStream, InflaterOutputStream, PrintStream

FileInputStream直接已知子類:

BufferedInputStream, CheckedInputStream, CipherInputStream, DataInputStream, DeflaterInputStream, DigestInputStream, InflaterInputStream, LineNumberInputStream, ProgressMonitorInputStream, PushbackInputStream

2、PipedOutputStream  和  PipedintputStream

Java裡的管道輸入流PipedInputStream與管道輸出流PipedOutputStream實現了類似管道的功能,用於不同執行緒之間的相互通訊

Java的管道輸入與輸出實際上使用的是一個迴圈緩衝陣列來實現,這個陣列預設大小為1024位元組。輸入流PipedInputStream從這個迴圈緩衝陣列中讀資料,輸出流

PipedOutputStream往這個迴圈緩衝陣列中寫入資料。當這個緩衝陣列已滿的時候,輸出流PipedOutputStream所在的執行緒將阻塞;當這個緩衝陣列首次為空的時候,輸入流

PipedInputStream所在的執行緒將阻塞

。Java在它的jdk文件中提到不要在一個執行緒中同時使用PipeInpuStream和PipeOutputStream,這會造成死鎖。

下面的例子:一個管道輸出流的執行緒WriteThread,一個輸入流的執行緒ReadThread,加一個測試案例Demo。

package IO_TEST;

import java.io.PipedOutputStream;

class WriteThread implements Runnable {  
    private PipedOutputStream pout;  

    WriteThread(PipedOutputStream pout){  
      this.pout=  pout;  
    }  

    @Override
    public void run(){  
        try {  
          System.out.println("W:開始將資料寫入:但等個5秒讓我們觀察...");  
          Thread.sleep(5000);  //釋放cpu執行權5秒  
          pout.write("writePiped 資料...".getBytes());  //管道輸出流  
          pout.close();  
        } catch(Exception e) {  
          throw new RuntimeException("W:WriteThread寫入失敗...");  
        }  
    }  
}  
package IO_TEST;
import java.io.PipedInputStream;

class ReadThread implements Runnable {  
    
    private PipedInputStream pin;  

    ReadThread(PipedInputStream pin) {  
      this.pin=pin;  
    }  

    @Override
    public void run() {  //由於必須要覆蓋run方法,所以這裡不能拋,只能try  
    
      try {  
            System.out.println("R:讀取前沒有資料,阻塞中...等待資料傳過來再輸出到控制檯...");  
            byte[] buf = new byte[1024];  
            int len = pin.read(buf);  //read阻塞  
            System.out.println("R:讀取資料成功,阻塞解除...");  
            String s= new String(buf,0,len);  
            System.out.println(s);  //將讀取的資料流用字串以字串打印出來  
            pin.close();       
      }  catch(Exception e)  {  
            throw new RuntimeException("R:管道讀取流失敗!");  
      }     
    }  
}  
package IO_TEST;

import java.io.PipedInputStream;
import java.io.PipedOutputStream;


public class Demo {  
    public static void main(String[] args)  throws Exception {  
      PipedInputStream pin = new PipedInputStream();  
      PipedOutputStream pout = new PipedOutputStream();  
      pin.connect(pout);  //輸入流與輸出流連線  

      ReadThread readTh   = new ReadThread(pin);  
      WriteThread writeTh = new WriteThread(pout);  
      new Thread(readTh).start();  
      new Thread(writeTh).start();  
    }  
}
pipe流的輸入輸出具體使用起來可能會讓人有點混亂的感覺,需搞清楚管道的概念。

我們再看一個實際下載用的例子,一些看不懂不要緊,後面會逐步解釋:(ejb  rest )

@GET
@Path("/download/{status}")
public Response download(@PathParam("status") final LoanStatus status) throws IOException {
        
        String contentDisposition = "attachment; filename*=UTF-8''" + URLEncoder.encode(status.getKey().toString().concat("_借款列表"), "UTF-8") + ".csv";
        
        final PipedOutputStream output = new PipedOutputStream();
        PipedInputStream input = new PipedInputStream(output);

        final List<Loan> loanList = loanBridge.listByStatusOrdDesc(PageInfo.ALL, status).getResults();

        Runnable writer = new Runnable() {
            @Override
            public void run() {
                //用到了裝飾模式:BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(output, "GBK"));
                //字元流轉換成位元組流
                try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(output, "GBK"));) {
                    StringBuilder sb = new StringBuilder();
                    sb.append("借款單標的號").append(CSV_SEPERATOR)
                            .append("借款人").append(CSV_SEPERATOR)
                            .append("工號").append(CSV_SEPERATOR)
                            .append("期限");
                    bw.append(sb.toString());
                    bw.newLine();
                    for (Loan loan : loanList) {
                        sb = new StringBuilder();
                        LoanRequest request = loan.getLoanRequest();
                        sb.append(loan.getTitle() + CSV_WRAP).append(CSV_SEPERATOR)
                                .append(appBean.getUser(request.getUserId()).getName()).append(CSV_SEPERATOR)
                                .append(appBean.getUser(request.getUserId()).getEmployeeNumber() + "\t").append(CSV_SEPERATOR)
                                .append(loan.getDuration().getShowDuration());
                        bw.append(sb.toString());
                        bw.newLine();
                    }
                    bw.flush();
                } catch (Exception ex) {
                    logger.error("Exception happened when write CSV for user list.", ex);
                }
            }
        };

        Thread thread = new Thread(writer);
        thread.start();
        return Response.ok(input, "text/csv").encoding("GBK").header("Content-Disposition", contentDisposition).build();
    }

3、BufferedInputStream  和   BufferedOutputStream

為InputStream,OutputStream類增加緩衝區功能。上面說的FileInputStream和FileOutputStream 在使用時,我們介紹了可以用byte陣列作為資料讀入的快取區,以讀檔案為列,讀取硬碟的速度遠遠低於讀取記憶體的資料,為了減少對硬碟的讀取,通常從檔案中一次讀取一定長度的資料,把資料存入快取中,在寫入的時候也是一次寫入一定長度的資料,這樣可以增加檔案的讀取效率。我們在使用FileInputStream的時候是用byte陣列來做了快取,而BufferedInputStream  and BufferedOutputStream已經為我們增加了這個快取功能。構建BufferedInputStream例項時,需要給定一個InputStream型別的例項,實現BufferedInputStream時,實際上最後是實現InputStream例項。同樣,構建BufferedOutputStream時,也需要給定一個OutputStream例項,實現BufferedOutputStream時,實際上最後是實現OutputStream例項。BufferedInputStream的資料成員buf是一個位數組,預設為2048位元組。當讀取資料來源時,例如檔案,BufferedInputStream會盡量將buf填滿。當使用read()方法時,實際上是先讀取buf中的資料,而不是直接對資料來源作讀取。當buf中的資料不足時,BufferedInputStream才會再實現給定的InputStream物件的read()方法,從指定的裝置中提取資料。BufferedOutputStream的資料成員buf也是一個位數組,預設為512位元組。當使用write()方法寫入資料時實際上會先將資料寫到buf中,當buf已滿時才會實現給定的OutputStream物件的write()方法,將buf資料寫到目的地,而不是每次都對目的地作寫入的動作。

package IO_TEST;
import java.io.*;  

public class BufferOutputStream_test {
    
    public static void main(String[] args) {   
        
        try {   
            byte[] data = new byte[1];   
  
            File srcFile = new File(args[0]);   
            File desFile = new File(args[1]);   
  
            BufferedInputStream bufferedInputStream =    
                new BufferedInputStream(new FileInputStream(srcFile));   
  
            BufferedOutputStream bufferedOutputStream =    
                new BufferedOutputStream(new FileOutputStream(desFile));   
  
            System.out.println("複製檔案:"+srcFile.length()+"位元組");   
  
            while(bufferedInputStream.read(data)!=-1) {   
                bufferedOutputStream.write(data);   
            }   
  
            //將緩衝區中的資料全部寫出   
            bufferedOutputStream.flush();   
  
            //關閉流   
            bufferedInputStream.close();   
            bufferedOutputStream.close();   
  
            System.out.println("複製完成");   
        } catch(ArrayIndexOutOfBoundsException e)  {   
            System.out.println("using:java UseFileStream src des");   
            e.printStackTrace();   
        }   catch(IOException e) {   
            e.printStackTrace();   
        }   
    }   
};  

4、上面講的流物件都是成雙成對,有輸入類也有輸出類,現在看一個特殊的光棍io流類(1111快到啦)

SequenceInputStream:

    合併流,將與之相連線的流集組合成一個輸入流並從第一個輸入流開始讀取, 直到到達檔案末尾,接著從第二個輸入流讀取,依次類推,直到到達包含的最後一個輸入流的檔案末尾為止。 合併流的作用是將多個源合併合一個源。可接收列舉類所封閉的多個位元組流物件。 

package IO_TEST;  
   
import java.io.*;  
import java.util.Enumeration;  
import java.util.Vector;  
   
public class NewClass {  
 
  public static void main(String[] args) {  
     doSequence();  
  }  
   
  private static void doSequence() {  
     SequenceInputStream sis = null;  // 建立一個合併流的物件  
     BufferedOutputStream bos = null;   // 建立輸出流。  
     try {  
        // 構建流集合。  
        Vector<InputStream> vector = new Vector<InputStream>();  
        vector.addElement(new FileInputStream("/Users/zhengchao/cctv/File_1.txt"));  
        vector.addElement(new FileInputStream("/Users/zhengchao/cctv/File_1.txt"));  
        Enumeration<InputStream> e = vector.elements();  
        sis = new SequenceInputStream(e);  
        bos = new BufferedOutputStream(new FileOutputStream("/Users/zhengchao/cctv/File_OUT.txt"));  
        // 讀寫資料  
        byte[] buf = new byte[1024];  
        int len = 0;  
        while ((len = sis.read(buf)) != -1) {  
           bos.write(buf, 0, len);  
           bos.flush();  
        }  
     } catch (FileNotFoundException e1) {  
        e1.printStackTrace();  
     } catch (IOException e1) {  
        e1.printStackTrace();  
     } finally {  
        try {  
           if (sis != null)  
              sis.close();  
        } catch (IOException e) {  
           e.printStackTrace();  
        }  
        try {  
           if (bos != null)  
              bos.close();  
        } catch (IOException e) {  
           e.printStackTrace();  
        }  
     }  
  }  
}  

5、讀寫物件:ObjectInputStream 和ObjectOutputStream

package IO_TEST;  
  
import java.io.FileInputStream;  
import java.io.FileOutputStream;  
import java.io.IOException;  
import java.io.ObjectInputStream;  
import java.io.ObjectOutputStream;  
import java.io.Serializable;  
public class ObjetStream_test {    
   public static void main(String[] args) {    
      ObjectOutputStream objectwriter=null;    
      ObjectInputStream objectreader=null;    
         
      try {    
         objectwriter=new ObjectOutputStream(new FileOutputStream("/Users/zhengchao/cctv/File_objectwriter_out.txt"));    
         objectwriter.writeObject(new Student("gg", 22));    
         objectwriter.writeObject(new Student("tt", 18));    
         objectwriter.writeObject(new Student("rr", 17));    
         objectreader=new ObjectInputStream(new FileInputStream("/Users/zhengchao/cctv/File_objectwriter_out.txt"));    
         for (int i = 0; i < 3; i++) {    
            System.out.println(objectreader.readObject());    
         }    
      } catch (IOException | ClassNotFoundException e) {      
         e.printStackTrace();    
      }finally{    
         try {    
            objectreader.close();    
            objectwriter.close();    
         } catch (IOException e) {     
            e.printStackTrace();    
         }    
      }    
   }    
}    
class Student implements Serializable{    
   private String name;    
   private int age;    
   public Student(String name, int age) {    
      super();    
      this.name = name;    
      this.age = age;    
   }    
   @Override    
   public String toString() {    
      return "Student [name=" + name + ", age=" + age + "]";    
   }    
}    
Student類必須實現Serializable介面,並重寫toString()方法,否則objectwriter.writeObject(new Student("gg", 22)); 執行會報錯。

6、ByteArrayInputStream和ByteArrayOutputStream,

用於以IO流的方式來完成對位元組陣列內容的讀寫,來支援類似記憶體虛擬檔案或者記憶體對映檔案的功能

import java.io.*;  
  
public class ByteArrayStreamTest {  
    public static void main(String [] args) {  
        String str = "abcdef";  
          
        ByteArrayInputStream in = new ByteArrayInputStream(str.getBytes());  
        ByteArrayOutputStream out = new ByteArrayOutputStream();  
          
        transform(in, out);  
          
        byte[] result = out.toByteArray();  
          
        System.out.println(out);  
        System.out.println(new String(result));  
          
        transform(System.in, System.out); // 從鍵盤讀,輸出到顯示器  
    }  
      
    public static void transform(InputStream in, OutputStream out) {  
        int ch = 0;  
          
        try {  
            while ((ch = in.read()) != -1) {  
                int upperChar = Character.toUpperCase((char)ch);  
                out.write(upperChar);  
            } // close while  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}  

總結:感覺學的差不多了,總結一下,總結?好像上面說的都是位元組流啊,難道?對了,這才學一半不到呢。

字元流:


 1、Writer

       寫入字元流的抽象類。子類必須實現的方法僅有 write(char[], int, int)、flush() 和 close()。但是,多數子類將重寫此處定義的一些方法,以提供更高的效率和/或其他功能。 其子類如下:

   

    ~BufferedWriter   :

將文字寫入字元輸出流,緩衝各個字元,從而提供單個字元、陣列和字串的高效寫入。

可以指定緩衝區的大小,或者接受預設的大小。在大多數情況下,預設值就足夠大了。

2、Reader

   用於讀取字元流的抽象類。子類必須實現的方法只有 read(char[], int, int) 和 close()。但是,多數子類將重寫此處定義的一些方法,以提供更高的效率和/或其他功能。 子類有:

      


示例: 1、使用BufferedReader和BufferedWriter字元處理流實現檔案複製
import java.io.*;  
class IODemo {  
    public static void main(String[] args){  
        try {  
        //使用BufferedReader和BufferedWriter進行檔案複製(操作的是字元,以行為單位讀入字元)  
        FileReader fr=new FileReader("a.txt");  
        BufferedReader br=new BufferedReader(fr);  
        FileWriter fw=new FileWriter("d.txt");  
        BufferedWriter bw=new BufferedWriter(fw);  
  
        String s=br.readLine();  
            while(null!=s) {  
                bw.write(s);  
                //由於BufferedReader的rendLIne()是不讀入換行符的,所以寫入換行時須用newLine()方法  
                bw.newLine();  
                //read=fis.read(b);  
                s=br.readLine();  
            }  
            br.close();  
            bw.close();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }   
    }  
}  
位元組字元轉換:

1:位元組輸入流轉換為字元輸入流:

InputStreamReader是位元組流向字元流的橋樑,它使用指定的charset讀取位元組並將其解碼為字元,它使用的字符集可以由名稱指定或顯示給定。根據InputStream的例項建立InputStreamReader的方法有4種:

InputStreamReader(InputStream in)//根據預設字符集建立

InputStreamReader(InputStream in,Charset cs)//使用給定字符集建立

InputStreamReader(InputStream in,CharsetDecoder dec)//使用給定字符集解碼器建立

InputStreamReader(InputStream in,String charsetName)//使用指定字符集建立

2:位元組輸出流轉換為字元輸出流

OutputStreamWriter是字元流通向位元組流的橋樑,它使用指定的charset將要寫入流中的字元編碼成位元組,它使用的字符集可以由名稱指定或顯示給定,否則將接受預設的字符集:

根據根據InputStream的例項建立OutputStreamWriter的方法有4種:

OutputStreamWriter(outputstream out)//根據預設的字符集建立

OutputStreamWriter(outputstream out,charset cs)//使用給定的字符集建立

OutputStreamWriter(outputstream out,charsetDecoder dec)//使用組定字符集建立

OutputStreamWriter(outputstream out,String charsetName)//使用指定字符集建立


Java.io包中操作檔案內容的主要有兩大類:位元組流、字元流,兩類都分為輸入和輸出操作。在位元組流中輸出資料主要是使用OutputStream完成,輸入使的是InputStream,在字元流中輸出主要是使用Writer類完成,輸入流主要使用Reader類完成。(這四個都是抽象類)

java中提供了專用於輸入輸出功能的包Java.io,其中包括:
     InputStream,OutputStream,Reader,Writer
     InputStream 和OutputStream,兩個是為位元組流設計的,主要用來處理位元組或二進位制物件,
     Reader和 Writer.兩個是為字元流(一個字元佔兩個位元組)設計的,主要用來處理字元或字串.


字元流處理的單元為2個位元組的Unicode字元,分別操作字元、字元陣列或字串,而位元組流處理單元為1個位元組,操作位元組和位元組陣列。所以字元流是由Java虛擬機器將位元組轉化為2個位元組的Unicode字元為單位的字元而成的,所以它對多國語言支援性比較好!如果是音訊檔案、圖片、歌曲,就用位元組流好點,如果是關係到中文(文字)的,用字元流好點
     所有檔案的儲存是都是位元組(byte)的儲存,在磁碟上保留的並不是檔案的字元而是先把字元編碼成位元組,再儲存這些位元組到磁碟。在讀取檔案(特別是文字檔案)時,也是一個位元組一個位元組地讀取以形成位元組序列

      位元組流可用於任何型別的物件,包括二進位制物件,而字元流只能處理字元或者字串; 2. 位元組流提供了處理任何型別的IO操作的功能,但它不能直接處理Unicode字元,而字元流就可以
       位元組流是最基本的,所有的InputStrem和OutputStream的子類都是,主要用在處理二進位制資料,它是按位元組來處理的 但實際中很多的資料是文字,又提出了字元流的概念,它是按虛擬機器的encode來處理,也就是要進行字符集的轉化 這兩個之間通過 InputStreamReader,OutputStreamWriter來關聯,實際上是通過byte[]和String來關聯 在實際開發中出現的漢字問題實際上都是在字元流和位元組流之間轉化不統一而造成的 

==================我們還可以看到:============
Reader類的read()方法返回型別為int :作為整數讀取的字元(佔兩個位元組共16位),範圍在 0 到 65535 之間 (0x00-0xffff),如果已到達流的末尾,則返回 -1


inputStream的read()雖然也返回int,但由於此類是面向位元組流的,一個位元組佔8個位,所以返回 0 到 255 範圍內的 int 位元組值。如果因為已經到達流末尾而沒有可用的位元組,則返回值 -1。因此對於不能用0-255來表示的值就得用字元流來讀取!比如說漢字.