Java基礎——IO(1)

IO流

概述

Java中採用IO流的方式來進行數據傳輸,IO流分爲兩種:

    1)字節流的抽象基流:InputStream和OutputStream

    2)字符流的抽象基流:Reader和Writer

P.S.

此四個類派生出來的子類名稱都是以父類名作爲子類名的後綴,以前綴爲其功能;如InputStream子類FileInputStream;Reader子類FileReader

 記住:如果要操作文字數據,建議優先考慮字符流。
    而且要將數據從內存寫到硬盤上,要使用字符流中的輸出流:Writer。
    硬盤的數據基本體現是文件,希望找到一個可以操作文件的Writer:FileWriter

字符流

字符流輸入:

用例:


public class Test1 {
    public static void main(String[] args) throws IOException {
        File file = new File("d://IO");
        if(!file.exists()){
            //不存在則創建路徑
            file.mkdirs();
        }
        FileWriter fw = new FileWriter(file+"/123.txt");
        fw.write("你好,字符流輸入!");
        //刷新緩存到指定文件
        fw.flush();
        fw.close();
    }
}

若更改爲:FileWriter fw = new FileWriter(file+”/123.txt”,true);

表示: 如果爲 true,則將字節寫入文件末尾處,而不是寫入文件開始處 。

加ture常用來進行文件續寫。

字符流輸出

1.單個字符讀取


public class Test2 {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("d://IO/123.txt");
        int c = 0;
        while((c= fr.read())!=-1){
            //這裏read的返回值是char值的int型,可以轉型爲char類型
            System.out.print((char)c);
        }
    }
}

2.字符數組讀取


public class Test3 {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("d://IO/123.txt");
        char []c = new char[1024];
        //返回值是讀取的長度
        int len = fr.read(c);
            System.out.println(new String(c, 0, len));
    }
}

練習:

需求: 
將d盤一個文本文件複製到f盤、 
複製的原理: 
其實就是將c盤下的文件數據存儲到e盤的一個文件中。 
步驟: 
1、在e盤創建一個文件。用於存儲c盤文件中的數據。 
2、定義讀取流和c盤文件關聯。 
3、通過不斷的讀寫完成數據存儲。 
4、關閉資源。
/**
單字符傳輸
*/

public class Test4 {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("D:\\IO\\123.txt");
        FileWriter fw = new FileWriter("f:\\copy123.txt");
        //單個字符傳送
        int c ;
        while((c=fr.read())!=-1){
            /**public void write(int c)
             * c - 指定要寫入字符的 int。
             */
            fw.write(c);
        }
        fr.close();
        fw.close();
    }
}

public class Test5 {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("D:\\IO\\123.txt");
        FileWriter fw = new FileWriter("f:\\copy123.txt");
        // 字符數組傳送
        char[] c = new char[1024];
        String buf = null;
        int len = 0;
        while ((len = fr.read(c)) != -1) {
            buf = new String(c, 0, len);
            fw.write(buf);
        }
        fw.close();
        fr.close();
    }
}

字符流的緩衝區——BufferedReader和BufferedWriter

使用注意:

1.使用緩衝區技術是爲了解決性能問題,提高效率

2.需要先建立流對象,再將流對象交給緩衝區構造函數去處理

3. 記住,只要用到緩衝區,就要記得刷新。(關閉流同樣會刷新,但爲了排除意外事故,保證數據存在,建議寫入一次就刷新一次)

     如:bufw.flush();

4.小知識:BufferedWriter緩衝區中提供了一個跨平臺的換行符:newLine();可以在不同操作系統上調用,用作數據換行。

    如:bufw.newLine();

5.讀取流緩衝區BufferedReader

     BufferedReader.readLine():另外開闢了一個緩衝區,存儲的是原緩衝區一行的數據,不包含換行符。所以實際使用中常使用BufferedReader.newLine();方法換行

練習:使用字符緩衝區複製文本


public class Test6 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("D:\\IO\\Hello.java"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("f:\\CopyHello.java"));
        String buf = null;
        while((buf = br.readLine())!=null){
            bw.write(buf);
            //添加換行,否則輸出文本沒有格式
            bw.newLine();
            // //使用緩衝區的刷新方法將數據刷目的地中 
        }
        bw.close();
        br.close();
    }
}

練習:模仿一個BufferedReader的readLine方法


/*
需求:根據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
public class  Test7
{
    public static void main(String[] args) 
    {
        MyBufferedReader mbr=null;
        try
        {
            mbr=new MyBufferedReader(new FileReader("D:\\IO\\Hello.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("讀取流關閉失敗");
            }
        }   
    }
}

LineNumberReader

此類定義了方法 setLineNumber(int) 和 getLineNumber(),它們可分別用於設置和獲取當前行號。 

換行符('\n')、回車符('\r')

/*
需求:利用LineNumberReader的特有方法去設置和獲取文件中數據的行號
*/
public class Test8 {
    public static void main(String[] args) {
        LineNumberReader lnr = null;
        try {
            // 將讀取流對象傳入
            lnr = new LineNumberReader(new FileReader("D:\\IO\\Hello.java"));
            lnr.setLineNumber(10);// 設置開始行號

            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.讀寫字節流:InputStream   輸入流(讀)

             OutputStream  輸出流(寫)

練習:複製圖片等媒體資源


public class Test1 {
    public static void main(String[] args) throws IOException {
        InputStream in = new FileInputStream("d:\\IO\\ME.JPG");
        OutputStream out = new FileOutputStream("f:\\CopyME.JPG");

        /**
         * 另種寫法:不推薦 
         * byte []b= new byte[in.available()];
         * 若in.available()值過大,會導致內存溢出
         *  p.s.
         *  in.available():返回文件中的字節個數
         */

        // 推薦寫法,自定義字符緩衝區

        byte[] b = new byte[1024];
        int len = 0;
        while ((len = in.read(b)) != -1) {
            out.write(b, 0, len);
        }
        out.close();
        in.close();
    }
}

字節流緩衝區

練習:自定義字節流讀取緩衝區


/* 
自定義字節流讀取緩衝區 
思路: 
1、定義一個固定長度的數組 
2、定義一個指針和計數器用於讀取數組長度,和計數數組元素是否取完爲0 
3、每次將字節數據存入元素要先將數組中的元素取完 
*/  

/*
自定義字節流讀取緩衝區
思路:
1、定義一個固定長度的數組
2、定義一個指針和計數器用於讀取數組長度,和計數數組元素是否取完爲0
3、每次將字節數據存入元素要先將數組中的元素取完

*/
import java.io.*;
class MyBufferedInputStream
{
       private InputStream in;
       private byte[] by=new byte[1024];
       private int count=0,pos=0;
       MyBufferedInputStream(InputStream in)
       {
              this.in=in;
       }


       //自定義讀方法,一次讀一個字節
       public int myRead()throws IOException
       {
              //通過in對象讀取硬盤上數據,並存儲by中。
              //存儲在數組中的數據被讀取完,再通過in對象從硬盤上讀取數據
              if(count==0)
              {
                     count=in.read(by);
                     if(count<0)//文件數據全部被讀取出來了
                            return -1;


                     pos=0;//初始化指針
                     byte b=by[pos];

                     count--;//每被讀一個字節,表示數組中的字節數少一個
                     pos++;//指針加1
                     return b&255;//返回的byte類型提升爲int類型,字節數增加,且高24位被補1,原字節數據改變。
                                          //通過與上255,主動將byte類型提升爲int類型,將高24位補0,原字節數據不變。
                                          //而在輸出字節流寫入數據時,只寫該int類型數據的最低8位。
              }
              else if(count>0)//如果數組中的數據沒被讀取完,則繼續讀取
              {
                     byte b=by[pos];

                     count--;
                     pos++;
                     return b&0xff;
              }
              return -1;
       }

       //自定義關閉資源方法
       public void close()throws IOException
       {
              in.close();
       }
}


//測試自定義輸入字節流緩衝區
public class Test2
{
       public static void main(String[] args) 
       {
              long start=System.currentTimeMillis();
              //利用字節流的緩衝區進行復制
              copy_2();
              long end=System.currentTimeMillis();
              System.out.println("複製共用時:"+(end-start)+"毫秒");
       }
       //使用字節流的緩衝區進行復制
       public static void copy_2()
       {
              BufferedOutputStream bout=null;
              MyBufferedInputStream bin=null;
              try
              {
                     //關聯複製文件輸入流對象到緩衝區
                     bin=new MyBufferedInputStream(new FileInputStream("d:\\IO\\ME.JPG"));
                     //指定文件粘貼位置的輸出流對象到緩衝區
                     bout=new BufferedOutputStream(new FileOutputStream("f:\\CopyME.JPG"));
                     int by=0;


                     while((by=bin.myRead())!=-1)
                     {
                            bout.write(by);//將緩衝區中的數據寫入指定文件中
                     }
              }
              catch(IOException e)
              {
                     throw new RuntimeException("MP3複製失敗");
              }
              finally
              {
                     try
                     {
                            if(bin!=null)
                                   bin.close();//關閉輸入字節流
                     }
                     catch(IOException e)
                     {
                            throw new RuntimeException("讀取字節流關閉失敗");
                     }
                     try
                     {
                            if(bout!=null)
                                   bout.close();//關閉輸出字節流
                     }
                     catch(IOException e)
                     {
                            throw new RuntimeException("寫入字節流關閉失敗");
                     }
              }
       }
}

流操作

鍵盤錄入

一、鍵盤錄入

1.標準輸入輸出流

    System.in:對應的標準輸入設備,鍵盤。

    Ssytem.out:對應的是標準的輸出設備,控制檯。

    System.in的類型是InputStream.

    System.out的類型是PrintStream是OutputStream的子類FilterOutputStream的子類。

2.改進

由於鍵盤錄入是字節流,效率較低。可不可以使用整行讀取,那麼需要藉助readLine方法,但是這個是字符流的方法。所以,需要將字節流轉換成字符流。

 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
       BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

練習:


/**
 * 需求:將鍵盤錄入的數據轉換成大寫輸出,顯示在控制檯,當輸入over時,表示結束 
源:鍵盤錄入。 
目的:控制檯。 
 */
public class Test1 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        String s = null;
        while((s = br.readLine())!=null){
            if(s.equals("over"))
                break;
            bw.write(s.toUpperCase());
            bw.flush();

        }
    }
}

練習:


/**
 * 需求:想把鍵盤錄入的數據存儲到一個文件中。 
 * 源:鍵盤 
 * 目的:文件 
 * 把錄入的數據按照指定的編碼表(UTF-8),將數據存到文件中。  
 * 需求:想要將一個文件的數據打印在控制檯上。 
 * 源:文件 
 * 目的:控制檯 
 */
public class Test2 {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new FileWriter("f:\\MyTest.txt"));
        String s = null;
        while((s = br.readLine())!=null){
            if(s.equals("over"))
                break;
            bw.write(s);
            bw.flush();
            bw.newLine();
        }
        bw.close();
        br.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));

練習:帶編碼的流操作


/**
 * 帶編碼集的操作
 * @author LQX
 *
 */
public class Test3 {

    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in, "UTF-8"));
        //指定UTF-8編碼
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("f://MyTest.java"), "UTF-8");
        String s = null;
        while((s = br.readLine())!=null){
            if(s.equals("over"))
                break;
            osw.write(s+"\r\n");
            osw.flush();            
        }
    }
}

public class Test4 {

    public static void main(String[] args) throws IOException {
        /*BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("f:\\MyTest.java")));
        String s = null;
        while((s = br.readLine())!=null){
            System.out.println(s);
        }*/
        //該文件在創建時我指定了編碼爲UTF-8,所以多出來是亂碼
        FileReader fr = new FileReader("f:\\MyTest.java");
        char []c = new char[1024];
/*      int len = 0;
        while((len = fr.read(c))!=-1){
            System.out.println(new String(c, 0, len));
        }*/
        //修改後,設定了編碼爲UTF-8,能正確讀出來
        InputStreamReader isr = new InputStreamReader(new FileInputStream("f:\\MyTest.java"), "UTF-8");
        int len = 0;
        while((len = isr.read(c))!=-1){
            System.out.println(new String(c, 0, len));
        }
    }
}

什麼時候使用流轉換?

1.目標設備是字節流,但操作是字符流,使用轉換流作爲橋樑,提高效率。

2.涉及文本字符編碼表時,必須使用轉換流,因爲只有它才提供自定義編碼。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章