黑马程序员——IO流

——- android培训java培训、期待与您交流! ———-



黑马程序员——IO流

  • IO流用来处理设备之间的数据传输
  • Java对数据的操作是通过流的方式
  • Java用于操作流的对象都在IO包中
  • 流按操作数据分为两种:字节流与字符流。
  • 流按流向分为:输入流,输出流。

IO流常用基类

  • 字节流的抽象基类:
    • InputStream, OutputStream。
  • 字符流的抽象基类:
    • Reader, Writer。
  • 注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。
    • 如:InputStream的子类FileInputStream。
    • 如:Reader的子类FileReader。

字符流

字符流:为了处理文字数据方便而出现的对象。
其实这些对象的内部使用的还是字节流(因为文字最终也是字节数据)
只不过,通过字节流读取了相对应的字节数,没有对这些字节直接操作。
而是去查了指定的(本机默认的)编码表,获取到了对应的文字。
简单说:字符流就是 : 字节流+编码表。

字符流——创建文件

  • 创建流对象,建立数据存放文件
    • FileWriter fw = new FileWriter("Test.txt");
  • 调用流对象的写入方法,将数据写入流
    • fw.write("text");
  • 关闭流资源,并将流中的数据清空到文件中。
    • fw.close();

代码:

FileWriter fw = null;
try {
    fw = new FileWriter("Test.txt");
    fw.write("text");
} catch (IOException e) {
    System.out.println(e.toString());
} finally {
    if (fw != null) {
        try{
            fw.close();
        } catch (IOException e) {
            System.out.println(e.toString());
        }
    }
}

字符流——读取文件

  • 建立一个流对象,将已存在的一个文件加载进流。
    • FileReader fr = new FileReader("Test.txt");
  • 创建一个临时存放数据的数组。
    • char[] ch = new char[1024];
  • 调用流对象的读取方法将流中的数据读入到数组中。
    • fr.read(ch);

代码:

FileReader fr = null;
try {
    fr = new FileReader("c:\\test.txt");
    char[] buf = new char[1024];
    int len = 0;
    while ((len = fr.read(buf)) != -1) {
        System.out.println(new String(buf, 0, len));
    }
} catch (IOException e) {
    System.out.println("read-Exception :" + e.toString());
} finally {
    if (fr != null) {
        try {
            fr.close();
        } catch (IOException e) {
            System.out.println("close-Exception :" + e.toString());
        }
    }
}

字符流继承体系简图

字符流继承体系简图

字符流的缓冲区

  • 缓冲区的出现提高了对数据的读写效率。
  • 缓冲技术原理:此对象中封装了数组,将数据存入,再一次性取出。
  • 对应类
    • BufferedWriter
    • BufferedReader
  • 缓冲区要结合流才可以使用。
  • 在流的基础上对流的功能进行了增强。

装饰设计模式

  • 简述
    • 当想对已有对象进行功能增强时,可定义类:将已有对象传入,基于已有对象的功能,并提供加强功能,那么自定义的该类称之为装饰类。
  • 特点
    • 装饰类通常都会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。
  • 装饰和继承的区别:
    1. 装饰模式比继承要灵活。避免了继承体系的臃肿,且降低了类与类之间的关系。
    2. 装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰的类通常都是属于一个体系。
    3. 从继承结构转为组合结构。

注:在定义类的时候,不要以继承为主;可通过装饰设计模式进行增强类功能。灵活性较强,当装饰类中的功能不适合,可再使用被装饰类的功能。

  • BufferedReader的原理。
    1. 使用流的read方法从源中读取一批数据存储到缓冲区的数组中。
    2. 通过计数器记录住存储的元素个数。
    3. 通过数组的角标来获取数组中的元素(从缓冲区中取数据).
    4. 指针会不断的自增,当增到数组长度,会归0.计数器会自减,当减到0时,就在从源拿一批数据进缓冲区。

自定义BufferedReader:

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

/**
 * 自定义的读取缓冲区。其实就是模拟一个BufferedReader.
 * 
 * 分析: 缓冲区中无非就是封装了一个数组, 并对外提供了更多的方法对数组进行访问。 其实这些方法最终操作的都是数组的角标。
 * 
 * 缓冲的原理: 其实就是从源中获取一批数据装进缓冲区中。 在从缓冲区中不断的取出一个一个数据。
 * 
 * 在此次取完后,在从源中继续取一批数据进缓冲区。 当源中的数据取光时,用-1作为结束标记。
 * 
 * 
 * @author Administrator
 * 
 */
public class MyBufferedReader extends Reader {

    private Reader r;

    // 定义一个数组作为缓冲区。
    private char[] buf = new char[1024];

    // 定义一个指针用于操作这个数组中的元素。当操作到最后一个元素后,指针应该归零。
    private int pos = 0;

    // 定义一个计数器用于记录缓冲区中的数据个数。 当该数据减到0,就从源中继续获取数据到缓冲区中。
    private int count = 0;

    MyBufferedReader(Reader r) {
        this.r = r;
    }

    /**
     * 该方法从缓冲区中一次取一个字符。
     * 
     * @return
     * @throws IOException
     */
    public int myRead() throws IOException {

        // 从源中获取一批数据到缓冲区中。需要先做判断,只有计数器为0时,才需要从源中获取数据。
        if (count == 0) {
            count = r.read(buf);
            pos = 0; // 每次获取数据到缓冲区后,角标归零.
        }
        if (count < 0)
            return -1; // 代表流读完了

        char ch = buf[pos++];
        count--;
        return ch;
    }

    public String myReadLine() throws IOException {

        StringBuilder sb = new StringBuilder();

        int ch = 0;
        while ((ch = myRead()) != -1) {

            if (ch == '\r')
                continue;
            if (ch == '\n')
                return sb.toString();
            // 将从缓冲区中读到的字符,存储到缓存行数据的缓冲区中。
            sb.append((char) ch);

        }

        if (sb.length() != 0)
            return sb.toString();
        return null;
    }

    public void myClose() throws IOException {

        r.close();
    }

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {

        return 0;
    }

    @Override
    public void close() throws IOException {
    }
}

字节流

  • 基本操作与字符流类相同
  • 但它不仅可以操作字符,还可以操作其他媒体文件

字节流继承体系简图

字节流继承体系简图

字符流的缓冲区

  • 同样是提高了字节流的读写效率。
  • 读写特点:
    • read():会将字节byte型值提升为int型值
    • write():会将int型强转为byte型,即保留二进制数的最后八位。

转换流

  • InputStreamReader, OutputStreamWriter
  • 转换流的由来
    • 字符流与字节流之间的桥梁
    • 方便了字符流与字节流之间的操作
  • 转换流的应用
    • 字节流中的数据都是字符时,转成字符流操作更高效。

标准输入输出流

  • System类中的字段:in,out。
  • 它们各代表了系统标准的输入和输出设备。
  • 默认输入设备是键盘,输出设备是显示器。
  • System.in的类型是InputStream.
  • System.out的类型是PrintStream是OutputStream的子类FilterOutputStream的子类.

示例

  • InputStreamReader将字节流通向字符流
    • 获取键盘录入对象。
      • InputStream in=System.in;
    • 将字节流对象转成字符流对象,使用转换流。
      • InputStreamReaderisr=new InputStreamReader(in);
    • 为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
      • BufferedReaderbr=new BufferedReader(isr);
    • //键盘录入最常见写法
      • BufferedReaderin=new BufferedReader(new InputStreamReader(System.in));
  • OutputStreamWriter字符流通向字节流
    • 字符通向字节:录入的是字符,存到硬盘上的是字节。步骤和InputStreamReader转换流一样。
/* 
需求:将键盘录入的数据,显示在控制台,当输入over时,表示结束 
源:键盘录入。 
目的:控制台。 

 */
import java.io.*;

class TransStreamDemo {
    public static void main(String[] args) throws IOException {
        // 获取键盘录入对象。
        // InputStream in=System.in;
        // 将字节流对象转成字符流对象,使用转换流。
        // InputStreamReader isr=new InputStreamReader(in);
        // 为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
        // BufferedReader br=new BufferedReader(isr);

        // 键盘录入最常见写法
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

        // 字符流通向字节流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                System.out));

        String s = null;
        while ((s = in.readLine()) != null) {
            if ("over".equals(s))
                break;
            bw.write(s.toUpperCase());// 写入数据
            bw.newLine();// 换行
            bw.flush();// 刷新

        }
        bw.close();// 关闭流资源
        in.close();
    }
}

IO流的操作规律总结

  1. 明确体系:
    • 数据源:InputStream ,Reader
    • 数据汇:OutputStream,Writer
  2. 明确数据:因为数据分两种:字节,字符。
    • 数据源:是否是纯文本数据呢?
      • 是:Reader
      • 否:InputStream
    • 数据汇:
      • 是:Writer
      • 否:OutputStream
  3. 明确设备:
    • 数据源:
      • 键盘:System.in
      • 硬盘:FileXXX
      • 内存:数组。
      • 网络:socket socket.getInputStream();
    • 数据汇:
      • 控制台:System.out
      • 硬盘:FileXXX
      • 内存:数组
      • 网络:socket socket.getOutputStream();
  4. 明确额外功能:
    1. 需要转换?是,使用转换流。InputStreamReader OutputStreamWriter
    2. 需要高效?是,使用缓冲区。Buffered
    3. 需要其他?

示例:

  1. 复制一个文本文件。
    1. 明确体系:
      • 源:InputStream ,Reader
      • 目的:OutputStream ,Writer
    2. 明确数据:
      • 源:是纯文本吗?是 Reader
      • 目的;是纯文本吗?是 Writer
    3. 明确设备:
      • 源:硬盘上的一个文件。 FileReader
      • 目的:硬盘上的一个文件。FileWriter
      • FileReader fr = new FileReader(“a.txt”);
      • FileWriter fw = new FileWriter(“b.txt”);
    4. 需要额外功能吗?
      • 需要,高效,使用buffer
      • BufferedReader bufr = new BufferedReader(new FileReader(“a.txt”));
      • BufferedWriter bufw = new BufferedWriter(new FileWriter(“b.txt”));
  2. 读取键盘录入,将数据存储到一个文件中。
    1. 明确体系:
      • 源:InputStream ,Reader
      • 目的:OutputStream ,Writer
    2. 明确数据:
      • 源:是纯文本吗?是 Reader
      • 目的;是纯文本吗?是 Writer
    3. 明确设备:
      • 源:键盘,System.in
      • 目的:硬盘,FileWriter
      • InputStream in = System.in;
      • FileWriter fw = new FileWriter(“a.txt”);
    4. 需要额外功能吗?
      • 需要,因为源明确的体系时Reader。可是源的设备是System.in。
      • 所以为了方便于操作文本数据,将源转成字符流。需要转换流。InputStreamReader
      • InputStreamReader isr = new InputStreamReader(System.in);
      • FileWriter fw = new FileWriter(“a.txt”);
      • 需要高效不?需要。Buffer
      • BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
      • BufferedWriter bufw = new BufferedWriter(new FileWriter(“a.txt”));
  3. 读取一个文本文件,将数据展现在控制台上。
    1. 明确体系:
      • 源:InputStream ,Reader
      • 目的:OutputStream ,Writer
    2. 明确数据:
      • 源:是纯文本吗?是 Reader
      • 目的;是纯文本吗?是 Writer
    3. 明确设备:
      • 源:硬盘文件,FileReader。
      • 目的:控制台:System.out。
      • FileReader fr = new FileReader(“a.txt”);
      • OutputStream out = System.out;
    4. 需要额外功能?
      • 因为源是文本数据,确定是Writer体系。所以为了方便操作字符数据,
      • 需要使用字符流,但是目的又是一个字节输出流。
      • 需要一个转换流,OutputStreamWriter
      • FileReader fr = new FileReader(“a.txt”);
      • OutputStreamWriter osw = new OutputStreamWriter(System.out);
      • 需要高效吗?需要。
      • BufferedReader bufr = new BufferedReader(new FileReader(“a.txt”));
      • BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
  4. 读取键盘录入,将数据展现在控制台上。
    1. 明确体系:
      • 源:InputStream ,Reader
      • 目的:OutputStream ,Writer
    2. 明确数据:
      • 源:是纯文本吗?是 Reader
      • 目的;是纯文本吗?是 Writer
    3. 明确设备:
      • 源:键盘:System.in
      • 目的:控制台:System.out
      • InputStream in = System.in;
      • OutputStream out = System.out;
    4. 需要额外功能吗?
      • 因为处理的数据是文本数据,同时确定是字符流体系。
      • 为方便操作字符数据的可以将源和目的都转成字符流。使用转换流。
      • 为了提高效率,使用Buffer
      • BufferedReader bufr =new BufferedReader(new InputStreamReader(Systme.in));
      • BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
  5. 读取一个文本文件,将文件按照指定的编码表UTF-8进行存储,保存到另一个文件中。
    1. 明确体系:
      • 源:InputStream ,Reader
      • 目的:OutputStream ,Writer
    2. 明确数据:
      • 源:是纯文本吗?是 Reader
      • 目的;是纯文本吗?是 Writer
    3. 明确设备:
      • 源:硬盘:FileReader.
      • 目的:硬盘:FileWriter
      • FileReader fr = new FileReader(“a.txt”);
      • FileWriter fw = new FileWriter(“b.txt”);
    4. 额外功能:
      • 注意:目的中虽然是一个文件,但是需要指定编码表。
      • 而直接操作文本文件的FileWriter本身内置的是本地默认码表。无法明确具体指定码表。
      • 这时就需要转换功能。OutputStreamWriter,而这个转换流需要接受一个字节输出流,而且
      • 对应的目的是一个文件。这时就使用字节输出流中的操作文件的流对象。FileOutputStream.
      • FileReader fr = new FileReader(“a.txt”);
      • OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(“b.txt”),”UTF-8”);
      • 需要高效吗?
      • BufferedReader bufr = new BufferedReader(new FileReader(“a.txt”));
      • BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(“b.txt”),”UTF-8”));

IO包中的其他类

  • RandomAccessFile
    • 随机访问文件,自身具备读写的方法。
    • 通过skipBytes(int x),seek(int x)来达到随机访问。
  • 管道流
    • PipedInputStream和PipedOutputStream
      • 输入输出可以直接进行连接,通过结合线程使用。
  • 打印流
    • PrintWriter与PrintStream
      • 可以直接操作输入流和文件。
  • 序列流
    • SequenceInputStream
      • 对多个流进行合并。
  • 操作对象
    • ObjectInputStream与ObjectOutputStream
      • 被操作的对象需要实现Serializable (标记接口);
  • 操作基本数据类型
    • DataInputStream与DataOutputStream
  • 操作字节数组
    • ByteArrayInputStream与ByteArrayOutputStream
  • 操作字符数组
    • CharArrayReader与CharArrayWrite
  • 操作字符串
    • StringReader 与 StringWriter

IO流体系

字符流:
Reader
    |--BufferedReader
        |--LineNumberReader
    |--CharArrayReader
    |--StringReader
    |--InputStreamReaer
        |--FileReader
Writer
    |--BufferedWriter
    |--CharArrayWriter
    |--StringWriter
    |--OutputStreamWriter
        |--FileWriter
    |--PrintWriter

字节流:
InputStream
    |--FileInputStream
    |--FilterInputStream
        |--BufferedInputStream
        |--DataInputStream
    |--ByteArrayInputStream
    |--ObjectInputStream
    |--SequenceInputStream
    |--PipedInputStream
OutputStream
    |--FileOutputStream
    |--FilterOutputStream
        |--BufferedOutputStream
        |--DataOutputStream
    |--ByteArrayOutputStream
    |--ObjectOutputStream
    |--PipedOutputStream
    |--PrintStream
RandomAccessFile
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值