Java基础-IO流

深入理解Java IO流及高效使用技巧

终于学习到了本人认为Java中体系最复杂的IO流这一章了。如果在学习之前没有好好的捋一捋IO流的体系,各种流之间的关系,学习起来真的会非常吃力。

IO概述:
IO流(Input&Output):是为了解决不同设备之间数据传输的问题。IO流一般配合着网络,才能发挥其强大的功能。

IO的体系图
这里写图片描述

分析:
    在学习IO体系的时候,我们一般是按照字符流和字节流来展开的。每一个体系中,都有对应的普通的流和高效的流。

首先,学习字符流
字符流包括:
A:字符输入流 Reader —— 是一个抽象类,要想实现数据的输入,需要new其子类对象
B:字符输出流 Writer —— 是一个抽象类,new其子类对象

在学习过程中,通常会遇到这样的问题:一个流到底是输入流还是输出流,会有不同的争论。其实这个是相对的,一般情况下,我们都是站在Java程序的角度上去看待这个问题。在Java的角度,流进Java程序的,就是输入流,流出Java程序的就是输出流。

需求:我们一般都会使用程序对文本文件来处理数据。要想对文件进行读取和写入数据,那么需要用到与File有关的输入输出流
**FileReader & FileWriter**   下面我们来学习一下这两个类

代码: FileWriter

import java.io.FileWriter;
import java.io.IOException;

public class FileWriterDemo {
    public static void main(String[] args) {
        // 创建FileWriter对象
        FileWriter fw = null;
        try {
            /*
             * 创建字符输出流对象做了几件事? A:调用系统功能创建了指定的文件; B:创建了字符输出流对象; C:把字符输出流对象指向创建的文件
             */
            fw = new FileWriter("a.txt");
            //调用写数据的功能
            fw.write("你好,傻逼IO");
            /*
             * 为什么要flush() 在Java中,1个字符=2个字节
             * 在系统文件数据的底层单位是字节,而我们现在使用的是字符输出流,所以不能直接把数据写入文件,
             * 在这里,是把要输出的数据存放在缓冲区里,只有我们刷新缓冲区,数据才会被写入到文件中.
             */
            fw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                /*
                 * 为什么一定要close() 1.让流对象变成垃圾 2.通知系统去释放与文件相关的资源.
                 */
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

结果:
这里写图片描述
分析:
A:创建字符流对象,做了几件事? 1、调用了系统功能创建了指定文件;2、创建了字符输出流对象;3、把字符输出流对象
指向了创建的文件。
B:为什么要flush() 在Java中,1个字符=2个字节
在系统文件数据的底层单位是字节,而我们现在使用的是字符输出流,所以不能直接把数据写入文件,
在这里,是把要输出的数据存放在缓冲区里,只有我们刷新缓冲区,数据才会被写入到文件中.
C:为什么一定要close()
1、让流变成垃圾,释放资源
2、通知系统去释放与文件管理相关的资源。

FileWriter的几个小问题

            字符输出流操作步骤:
             *      A:创建字符输出流对象
             *      B:调用字符输出流对象的写入数据方法,并刷新缓冲区
             *      C:释放资源

*
* 三个小问题:
*
* 问题1:为什么FileWriter没有无参构造方法?
* 因为在写数据的时候,必须要明确些到哪里去.
*
* 问题2:flush()和close()的区别?
* flush(): 只刷新缓冲区,流对象还可以使用
* close(): 先刷新缓冲区,再关闭流对象,流对象不可以继续被使用.
*
* 问题3:难道每次调用完流对象的写入数据方法时,都需要刷新缓冲区吗?是否可以不刷新,直接等到close()来解决?
* 这两种方法都不可取.

代码:FileWriter 加入了捕捉异常标准代码

package cn.itcast.filewriter;
/*
 * FileWriter加入异常处理的标准代码
 * 
 */

import java.io.FileWriter;
import java.io.IOException;

public class FileWriterDem3 {
    public static void main(String[] args) {
        // 创建字符输出流对象
        FileWriter fw = null;
        try {
            fw = new FileWriter("c.txt");
            fw.write("干了这杯热翔");
            fw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fw != null) {
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

FileReader

FileReader 字符输入流
* 构造方法:
* FileReader(File file)
* FileReader(String fileName) 选用
*
* 读取方法:
* int read(): 每次读取一个字符,没读取完一个字符,自动移动到下一个数据位置等待读取
* 当没有数据可读时,会返回-1
int read(char[] cbuf):

代码:
public class FileReaderDemo {
    public static void main(String[] args) {
        // 创建字符输入流对象
        FileReader fr = null;
        try {
            fr = new FileReader("FileReaderDemo.java");
//          int ch = fr.read();
//          while(ch != -1){
//              System.out.print((char)ch);
//              ch = fr.read();
//          }

            //开发是的写法
            int ch = 0;
            while((ch=fr.read())!=-1){
                System.out.print((char)ch);
            }


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fr != null) {
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

结果:
这里写图片描述
分析:
读取一个文件,在控制台上输出

FileReader读取数据方式二:
* int read(char[] cbuf): 每次读取数据,把数据存储到字符数组中,读取长度自定义,一般为1024的整数倍
* 返回值是实际读取的长度.
代码2:

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class FileReaderDemo3 {
    public static void main(String[] args) throws IOException {
        //创建字符输入流对象
        FileReader fr = new FileReader("FileReaderDemo.java");

        //读取 开发中的代码
        char[] chs = new char[1024];
        int len = 0;
        while((len = fr.read(chs))!=-1){
            System.out.print(new String(chs,0,len));
        }
    }
}

结果:
这里写图片描述
分析:
这个是开始中的标准代码,每次读取1024个字符,大大的提高了效率。在开发中,一般这个字符数组的大小,都是设置为1024的整数倍。

那么在学习完FileReader和FileWriter两个对文件操作的类后,就可以用这些学习过的方法,来进行文件的复制了。
需求:将本项目目录下的FileReaderDemo.java文件,复制到D:\copy.java
* 思路:
* 数据源:
* FileReaderDemo.java – Reader –FileReader
*
* 目的地:
* D:\copy.java – Writer – FileWriter

代码;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CopyFileDemo {
    public static void main(String[] args) throws IOException {
        // 创建字符输入流对象
        FileReader fr = new FileReader("FileReaderDemo.java");
        // 创建字符输出流对象
        FileWriter fw = new FileWriter("D:\\copy.java");

        // 读入数据
        int ch = 0;
        while ((ch = fr.read()) != -1) {
            fw.write(ch);
        }
        fw.flush();

        // 释放资源
        fw.close();
        fr.close();
    }
}

结果:
这里写图片描述

相对高效的复制操作,其实也就是用了字符数组的原理:
代码:

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CopyFileDemo2 {
    public static void main(String[] args) throws IOException {
        //创建字符输入流对象
        FileReader fr = new FileReader("FileReaderDemo.java");

        //创建字符输出流对象
        FileWriter fw = new FileWriter("d:\\copy1.java");

        //创建一个大小为1024的字符数组,存储输入流读取到的数据
        char[] chs = new char[1024];
        int ch = 0;
        //读取
        while((ch = fr.read(chs))!= -1){
            //将存储在字符数组中的数据写进指定的文件
            fw.write(chs);
        }
        //刷新输出流
        fw.flush();

        //关闭资源
        fw.close();
        fr.close();
    }
}

学习完简单的文件字符流类,下面来学习一下简单的文件字节流类
FileInputStream 和 FileOutputStream

在IO体系里,基本上方法和格式都是固定的,出了部分类有自己特有的方法外,其他基本上都是通用。那么,我们直接使用在FileReader 和 FileWriter类中的方法,进行操作

通过字节流往文件中写数据。
*
* 字节输出流操作步骤:
* A:创建字节输出流对象
* B:调用写数据的方法
* C:释放资源

FileOutputStream

代码:
public class FileOutputStreamDemo {
    public static void main(String[] args) throws IOException {
        // 创建字节输出流对象
        // FileOutputStream fos = new FileOutputStream("a.txt",true);
        FileOutputStream fos = new FileOutputStream("a.txt");

        // 调用写数据的方法
        // 写入一个字节
        // fos.write(97);
        // fos.write(98);
        // fos.write(99);
        // fos.flush();
        // 写一个字节数组
        // byte[] bys = { 97, 98, 99, 100, 101 };
        byte[] bys = "abcde".getBytes();
        // fos.write(bys);
        // 写一个字节数组的一部分
        fos.write(bys, 0, 2);

        // 释放资源
        fos.close();
    }
}

FileInputStream
代码:

public class FileInputStreamDemo {
    public static void main(String[] args) throws IOException {
        // 创建字节输入流对象
        FileInputStream fis = new FileInputStream("b.txt");

        // 调用读取数据的方式,并显示
        // 方式1
        // int by = 0;
        // while ((by = fis.read()) != -1) {
        // System.out.println(by);
        // }

        // 方式2
        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = fis.read(bys)) != -1) {
            System.out.print(new String(bys, 0, len));
        }

        // 释放资源
        fis.close();
    }
}

——————————————————————————————————————————-到这里,已经把简单的字符流和字节流学习了。不过这些方法都属于比较低效的。
下面,我们学习一下高效的字符缓冲流和字节缓冲流
A:字符缓冲流
|–BufferedWriter
|–BufferedReader
B:字节缓冲流
|–BufferedInputStream
|–BufferedOutputStream

字符缓冲流
* 我们开始自己定义数组,给出缓冲区大小,是为了提高效率。
* 那么,java在设计类的时候,它也考虑到了这一点,所以,就提供了一种高效的流。带缓冲区的流。
* BufferedWriter:写入数据
* BufferedReader:读取数据
*
* BufferedWriter:
* 构造方法:BufferedWriter(Writer out)
* 为什么传递的是Writer呢?
* 因为BufferedWriter这种流,被称为缓冲流,它只提供数据的缓冲功能。
* 真正的读写还得靠别人。所以,我们这里将使用FileWriter作为参数传递。
*
* 缓冲区只提供缓冲功能,没有真正的读写。
*
* 基本流:能直接进行读写的流对象。
* 高级流:站在基本流的基础上,提供一些特殊的功能。(处理流。)

代码:BufferedWriter
public class BufferedWriterDemo {
    public static void main(String[] args) throws IOException {
        // 创建缓冲流对象
        // Writer fw = new FileWriter("c.txt");
        // FileWriter fw = new FileWriter("c.txt");
        // BufferedWriter bw = new BufferedWriter(fw);
        BufferedWriter bw = new BufferedWriter(new FileWriter("c.txt"));

        // 写入数据
        bw.write("hello");
        bw.flush();

        // 释放资源
        // fw.close();// 这个不用关闭
        bw.close();
    }
}
代码:FileReader:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

/*
 * 字符缓冲流读取数据
 */
public class BufferedReaderDemo {
    public static void main(String[] args) throws IOException {
        // 创建字符缓冲流对象
        BufferedReader br = new BufferedReader(new FileReader("c.txt"));

        // 读取数据
        // int ch = 0;
        // while ((ch = br.read()) != -1) {
        // System.out.print((char) ch);
        // }

        char[] chs = new char[1024];
        int len = 0;
        while ((len = br.read(chs)) != -1) {
            System.out.print(new String(chs, 0, len));
        }

        // 释放资源
        br.close();
    }
}

在学习简单IO流的时候,有一个复制文件的操作,如果要复制的文件比较大,可以使用字符缓冲流来提高效率
代码:

package cn.itcast_03;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/*
 * 用字符高效流复制文本文件。
 * 
 * 数据源:
 *      FileOutputStreamDemo.java
 * 目的地:
 *      Copy.java
 */
public class CopyFile {
    public static void main(String[] args) throws IOException {
        // 封装数据源和目的地
        BufferedReader br = new BufferedReader(new FileReader(
                "FileOutputStreamDemo.java"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.java"));

        // 一次一个字符
        // int ch = 0;
        // while ((ch = br.read()) != -1) {
        // bw.write(ch);
        // // bw.flush();
        // }

        // 一次一个字符数组
        char[] chs = new char[1024];
        int len = 0;
        while ((len = br.read(chs)) != -1) {
            bw.write(chs, 0, len);
        }

        // 释放资源
        bw.close();
        br.close();
    }
}

结果:
由于操作速度非常快,都是用毫秒值来计算的,那么我们要怎样才能知道,到底字符缓冲流,是不是真的比较高效呢?
我们在学习System类的时候,有一个currentTimeMillis()的方法,可以用来计算一段代码之间的时间

不多说,马上改造:
代码
package cn.itcast_04;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 需求:把d:\\哥有老婆.mp4复制到项目路径下x.mp3(x=1,2,3,4)
 * 
 * @author Administrator
 * 
 *         基本字节流一次读写一个字节: 共耗时:66922毫秒 
 *         基本字节流一次读写一个字节数组: 共耗时:94毫秒
 *         高效字节流一次读写一个字节:共耗时:1235毫秒
 *         高效字节流一次读写一个字节数组:共耗时:47毫秒
 */
public class CopyMP3 {
    public static void main(String[] args) throws IOException {
        long start = System.currentTimeMillis();
        // method1();
        // method2();
//      method3();
         method4();
        long end = System.currentTimeMillis();
        System.out.println("共耗时:" + (end - start) + "毫秒");
    }

    //高效字节流一次读写一个字节数组
    private static void method4() throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
                "d:\\哥有老婆.mp4"));
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("4.mp4"));

        byte[] bys = new byte[1024];
        int len = 0;
        while((len=bis.read(bys))!=-1){
            bos.write(bys,0,len);
        }

        bos.close();
        bis.close();
    }

    //基本字节流一次读写一个字节数组
    private static void method3() throws IOException {
        FileInputStream fis = new FileInputStream("d:\\哥有老婆.mp4");
        FileOutputStream fos = new FileOutputStream("3.mp4");

        byte[] bys = new byte[1024];
        int len = 0;
        while((len=fis.read(bys))!=-1){
            fos.write(bys,0,len);
        }

        fos.close();
        fis.close();
    }

    // 高效字节流一次读写一个字节
    private static void method2() throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
                "d:\\哥有老婆.mp4"));
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("2.mp4"));

        int by = 0;
        while ((by = bis.read()) != -1) {
            bos.write(by);
        }

        bos.close();
        bis.close();
    }

    // 基本字节流一次读写一个字节
    private static void method1() throws IOException {
        FileInputStream fis = new FileInputStream("d:\\哥有老婆.mp4");
        FileOutputStream fos = new FileOutputStream("1.mp4");

        int by = 0;
        while ((by = fis.read()) != -1) {
            fos.write(by);
        }

        fos.close();
        fis.close();
    }
}

结果:
* 基本字节流一次读写一个字节: 共耗时:66922毫秒
* 基本字节流一次读写一个字节数组: 共耗时:94毫秒
* 高效字节流一次读写一个字节:共耗时:1235毫秒
* 高效字节流一次读写一个字节数组:共耗时:47毫秒
分析:
高效流确实会更有效率,一般在开发中,我们都是选择高效流来使用。


BufferedWriter 和 BufferedReader 的特有方法

         * BufferedWriter:
         *      public void newLine():根据系统平台写入行分隔符
         *      
         * BufferedReader:
         *      public String readLine():一次读取一行的数据。但是不包含换行符。

代码如下:
public class BufferedStreamDemo {
    public static void main(String[] args) throws IOException {
        //创建流对象
        BufferedReader br = new BufferedReader(new FileReader("bw.txt"));
        // 读取数据
        String line = null;
        while((line=br.readLine())!=null){
            System.out.println(line);
        }

        br.close();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值