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.涉及文本字符编码表时,必须使用转换流,因为只有它才提供自定义编码。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章