IO流

Keywords: 文件、字节流、字符流、编码与解码、装饰设计模式
文件:
File

字节流:
InputStream OutputStream
FileInputStream FileOutputStream
BufferedInputStream BufferedOutputStream

字符流:
Reader Writer
FileReader FileWriter
InputStreamReader OutputStreamWriter
BufferedReader BufferedWriter

IO(Input Output)流概述

IO流用来处理设备之间的数据传输。

输入流和输出流是相对于内存设备而言的:
将外设中的数据读取到内存中:输入。
将内存的数写入到外设中:输出。

文件File类:操作文件的属性信息

数据最终持久化到硬盘上上,体现就是文件。
找文件api思路:文件有名称、大小、创建时间。既然有这么多信息,最好将其封装成对象后,操作更容易。找对象,搜索file,找到File类。java.io代表的是文件或者目录(文件夹)。

java.io.File常用方法

查api
(io.xenaliu.io.day1.file)(io.xenaliu.io.day1.digui)

  1. 将文件/文件夹封装成File文件

    File f = new File(String pathName);
    File f = new File(File parent, String child);
    File f = new File(String parent, String child);
    
  2. 获取文件的信息:名称、大小、时间

    String filename = file.getName();
    long size = file.length();
    long time = file.lastModified();
    
  3. 对文件进行操作:创建、删除

    boolean b = file.createNewFile();
    boolean b = file.delete();//不去回收站,慎用。
    
  4. 对目录进行操作:创建、删除

    boolean b = dir.mkdir();//创建目录
    boolean b = dir.mkdirs();//创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。
    boolean b = dir.delete();//删除目录时,如果目录中有内容,无法直接删除
    
  5. 判断文件和目录

    boolean b = file.exists();//判断文件/目录是否存在
    //判断是否是文件或目录,先判断存在
    f.isFile();//判断是否为文件
    f.isDirectory();//判断是否为目录
    
  6. 获取给定目录内部的内容

    //方法一:
    String[] names = dir.list();//返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录。
    for (String name : names) {
        System.out.println(name);
    }
    //方法二:
    File[] files = dir.listFiles();//返回一个抽象路径名数组,表示由此抽象路径名表示的满足指定过滤器的目录中的文件和目录。
    for (File file : files) {
        System.out.println(file.lastModified());
    }
    
  7. 过滤器:获取给定目录下指定类型的文件(io.xenaliu.io.day1.filter)

    File[] files = dir.listFiles(new FileFilterByDir());
    

一个栗子:

(/io/src/io/xenaliu/io/day1/file/FileTest.java)
需求:列出当前目录下的子目录中的所有内容
方法:递归

字节流

IO程序的书写:

  1. 导入IO包中的类
  2. 进行IO异常处理
  3. finally中对流进行关闭

字节输出流

需求:数据写入到文件中

输入输出操作:File对象操作文件的属性等信息,但不能操作文件中的数据。要操作文件中的数据,需要对数据进行输入输出流操作(往文件中写入数据),通过api查找output,找到java.io.OutputStream

OutputStream:输出字节流的超类

  1. 操作的数据都是字节
  2. 定义了输出字节流的基本共性功能
  3. 输出流中定义都是写write方法操作字节数组或单个字节
  4. 子类有规律:所有名称后缀都是父类名,前缀名就是这个流对象的功能

FileOutputStream常用方法

  1. 将数据写到文件中
    (/io/src/io/xenaliu/io/day2/bytestream/FileOutputStreamDemo.java)
//创建临时目录
File dir =  new File("tempfile");
if (!dir.exists()) {
    dir.mkdir();
}
//创建存储数据的文件
File file = new File("tempfile\\file.txt");
//创建一个用户操作文件的字节输出流对象。一创建就必须明确数据存储目的地。
//输出流目的是文件,会自动创建,如果文件存在,则覆盖
FileOutputStream fos = new FileOutputStream(file);
byte[] data = "abcdef".getBytes();
//调用父类中的write方法
fos.write(data);
//关闭流资源
fos.close();
  1. 将数据续写到已有文件中
    (/io/src/io/xenaliu/io/day2/bytestream/FileOutputStreamDemo2.java)
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) throws IOException {
    //FileOutputStream(File file, boolean append)续写
    File file = new File("tempfile\\file.txt");
    FileOutputStream fos = new FileOutputStream(file, true);
    String str = LINE_SEPARATOR+"xenaliu";
    fos.write(str.getBytes());
    fos.close();
}

注意:自己写代码时一定要记得解决异常try/catch
(/io/src/io/xenaliu/io/day2/bytestream/FileOutputStreamDemo3.java)

public static void main(String[] args) {
    File file = new File("k:\\file.txt");
    FileOutputStream fos = null;
    try {
        fos = new FileOutputStream(file);
        fos.write("abdcf".getBytes());
        fos.close();
    }catch (IOException e) {
        System.out.println(e.toString()+"-------");
    }finally {
        if (fos!=null) {            
            try {
                fos.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                throw new RuntimeException();
            }
        }
    }
}

字节输入流

需求:读取文件中的数据,显示在屏幕上

通过api查找input,找到java.io.InputStream

InputStream:输出字节流的超类

  1. 操作的数据都是字节
  2. 定义了输出字节流的基本共性功能
  3. 输入流中定义都是读read方法操作字节数组或单个字节
  4. 子类有规律:所有名称后缀都是父类名,前缀名就是这个流对象的功能。如:InputStream的子类FileInputStream;Reader的子类FileReader

FileInputStream常用方法

  1. 读取文件中的数据
    (/io/src/io/xenaliu/io/day2/bytestream/read/FileInputStreamDemo.java)
    (/io/src/io/xenaliu/io/day2/bytestream/read/FileInputStreamDemo2.java)
File file = new File("tempfile\\\\file.txt");   
//创建一个字节输入流对象,必须明确数据源,其实就是创建字节读取流和数据源相关联
FileInputStream fis = new FileInputStream(file);

//方法一:
//读取数据,使用read(),一次读一个字节
int ch = 0;
while ((ch=fis.read())!=-1) {
    System.out.println("ch="+(char)ch);
}

//方法二:
int len = 0;//读入缓冲区的总字节数,如果没有更多的数据,因为文件的结尾已经到达, -1 。
//创建一个字节数组,作为读取数据的缓冲区
byte[] buffer = new byte[1024];//长度可以定义为1024的整数倍
//从该输入流读取最多b.length字节的数据到字节数组
while ((len=fis.read(buffer))!=-1) {
    System.out.println(new String(buffer,0,len));
}

//关闭资源
fis.close();

字节流缓冲区对象

其实自定义数组就可以解决缓冲区问题并提高效率,为什么还要使用流中的缓冲区对象呢?因为缓冲区对象中除了封装数组以外,还提供了更多的操作缓冲区数据的方法。

BufferedInputStream类:

BufferedInputStream bufis = new BufferedInputStream(InputStream in);//构造函数
bufis.read();

BufferedOutputStream类:

BufferedOutputStream bufos= new BufferedOutputStream(OutputStream out);//构造函数
bufos.write();
bufos.flush();

练习

练习:复制文件:读取一个已有的数据,并将这些读取的数据写入另一个文件中
(/io/src/io/xenaliu/io/day2/bytestream/test/CopyFileTest.java)
(/io/src/io/xenaliu/io/day2/bytestream/test/CopyFileByBufferTest.java)

练习:将学生对象(姓名,语文分数,数学分数,英语分数,总分)按照总分从高到低排序,并将姓名从高到低写入文件中
(/io/src/io/xenaliu/io/day2/test/Test2.java)

建立一个清单列表:

获取指定目录下所有的.java文件(包含子目录中的),并将这些java文件的绝对路径写入到一个文件中,建立一个java文件清单列表
(/io/src/io/xenaliu/io/day2/test/Test3.java)

编码表

生活中的文字和计算机二进制的对应关系表
文字--->二进制(数字):编码
二进制(数字)--->文字:解码

常见码表:

  1. ASKII:英文表,用一个字节中的7位表示,对应的字节都是正数。0-xxxxxx
  2. ISO8859-1:拉丁码表latin,用一个字节中的8位表示,对应的字节都是负数。1-xxxxxxx
  3. GB2312:简体中文码表,六七千个中文和符号。用两个字节表示。两个字节都是开头为1,两个字节都是负数
    GBK:目前最常用的中文码表,2w+的中文和符号,用两个字节表示,一部分文字第一个字节开头是1,第二个字节开头是0
    GB18030:目前收录量最大的中文码表
  4. unicode:国际标准码表。无论什么文字,都用两个字节存储。java中的char类型用的就是这个码表。char c = 'a';占两个字节。
  5. UTF-8:基于unicode改良,一个字节就可以存储数据,而不是必须两个字节。这个码表更加标准化,在每个字节头加入了编码信息(后期到api中查找)。

编码与解码

字符串:String 字节数组:byte[]
字符串--编码(getBytes())-->字节数组
字节数组--解码(new String(byte[]))-->字符串
(/io/src/io/xenaliu/io/d20100106/encoding/EncodingDemo.java)

注:

  1. java中,字符串是按照系统默认码表来解析的,简体中文版字符串默认的码表是GBK。
  2. 对于开发而言,常见的编码表有GBK、UTF-8、ISO8859-1
  3. 能识别中文的码表:GBK、UTF-8,正因为识别中文码表不唯一,涉及到了编码解码问题。

栗子:

对字符串按照字节数截取
(/io/src/io/xenaliu/io/d20100106/encoding/CutStringTest.java)

联通乱码问题
(/io/src/io/xenaliu/io/d20100106/encoding/LianTongDemo.java)

字符流

字符流的由来: 为了便于操作数据中的字符数据。字节流读取文字字节数据后,不直接操作而是先查指定的编码表,获取对应的文字,再对这个文字进行操作。
原理: 字节流+编码表

字符流的两个基类:
Reader:read()读取字符
Writer:write():写入字符

字节流操作的是字节数组,字符流操作的是字符数组。

字符流操作文件的便捷类

字符流创建文件

需求

将一些文字存储到硬盘一个文件中。
如果要操作文字数据,建议优先考虑字符流。而且要将数据从内存写到硬盘上,要使用字符流中的输出流Writer。硬盘的数据基本体现是文件,希望找到一个可以操作文件的writer
通过api找到FileWriter

基本操作

  1. 创建流对象,建立数据存放文件
  2. 调用流对象的写入方法,将数据写入流
  3. 关闭流资源,并将流中的数据清空到文件中。
    (/io/src/io/xenaliu/io/d20100103/charstream/FileWriteDemo.java)
FileWriter fw = null;
try{
    fw = new FileWriter("Test.txt");
    fw.write("text");
    // fw.flush();
}
catch (IOException e){
    System.out.println(e.toString());
}
finally{
    if(fw!=null){
        try{
            fw.close();
        }
        catch (IOException e){
            System.out.println(e.toString());
        }
    }
}

flush()close()的区别:

flush():将流中的缓冲区缓冲的数据刷新到目的地中,刷新后,流还可以继续使用
close():关闭资源,但在关闭前会将缓冲区中的数据先刷新到目的地,否则丢失数据,然后关闭流,流不可以继续使用

如果写入数据多,一定要一边写一边刷新,最后一次可以不刷新,由close()完成刷新并关闭。

注:如果缓冲区满了,即使没有刷新也会自动写入目的地,但如果缓冲区没满,则不会自动写入,数据可能丢失,所以一定要一边写一边刷新。

字符流读取文件

需求

读取一个文本文件。将读取到的字符打印到控制台。要使用字符流中的输出流Reader。硬盘的数据基本体现是文件,希望找到一个可以操作文件的Reader
通过api找到FileReader

基本操作

  1. 建立一个流对象,将已存在的一个文件加载进流
  2. 创建一个临时存放数据的数组
  3. 调用流对象的读取方法将流中的数据读入到数组中
    (/io/src/io/xenaliu/io/d20100103/charstream/CharStreamDemo.java)
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());
        }
    }
}

一些技巧(FileReader为例)

了解一个子类的功能,先它所属的体系顶层。

栗子:FileReader,它的体系顶层为Reader

Reader:读取字符流的抽象超类

Reader的功能:

int read();//Reads a single character
int read(char[] cbuf)//Reads characters into an array.

转换流:流中的编码与解码

需求

编码(存储): 字节--->字符:看不懂的--->看得懂的
既然识别中文的码表有两个,GBK和UTF-8,能不能将中文数据按照utf-8的方式进行文件的存储?
不能使用FileWriter,因为FileWriter中默认的是GBK。
通过FileWriter的api描述,要指定编码表这些值,需要使用OutputStreamWriterOutputStreamWriter是字符流通向字节流的桥梁:可使用指定的charset将要写入流中的字符编码成字节。它的作用是:将字符串按照指定的编码表转成字节,再使用字节流将这些字节写出去。
解码(读取): 字符--->字节:看得懂的--->看不懂的
同上理。InputStreamReader

转换流的由来:

  • 字符流与字节流之间的桥梁
  • 方便了字符流与字节流之间的操作

转换流的应用: 字节流中的数据都是字符时,转成字符流操作更高效

基本操作

public static void writeCN() throws UnsupportedEncodingException, FileNotFoundException, IOException {
    OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("tmpfile\\u8cn.txt"),"utf-8");
    osw.write("你好");
    osw.close();
}

public static void readCN() throws IOException {
    FileReader fr = new FileReader("tmpfile\\u8cn.txt");
    InputStreamReader isr = new InputStreamReader(new FileInputStream("tmpfile\\u8cn.txt"),"utf-8");
    char[] buf = new char[1024];
    int len = isr.read(buf);
    System.out.println(new String(buf,0,len));//你好
    fr.close();
}

总结

继承关系:
OutputStreamWriter:
  |--FileWriter
InputStreamReader:
  |--FIleReader

父类和子类的功能区别:
OutputStreamWriterInputStreamReader是字符和字节的桥梁:也可以称之为字符转换流。字符转换流原理:字节流+编码表。
FileWriterFIleReader作为子类,进作为操作字符文件的便捷类存在。当操作的字符文件内,使用的是默认编码表时可以不用父类,二直接使用子类就可以完成操作,简化了代码。

InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");
FileReader fr = new FileReader("a.txt");

这三句代码的功能是一样的,其中第三句最为便捷。

注意: 一旦要指定其他编码时,绝对不能用子类,必须使用字符转换流。

使用子类的条件:

  1. 操作的是文件
  2. 使用默认编码

字节--->字符:看不懂的--->看得懂的,需要读,输入流,InputStreamReader
字符--->字节:看得懂的--->看不懂的,需要写,输出流,OutputStreamWriter

练习:

复制文本文件
(/io/src/io/xenaliu/io/d20100106/charstream/copy/CopyTextFile.java)

字符流缓冲区

字符流缓冲区对象

BufferedReader类:

BufferedReader bufr = new BufferedReader(Reader in);//构造函数
bufr.read();
bufr.readLine();//一次读取一行

BufferedWriter类:

BufferedWriter bufw = new BufferedWriter(Writer out);
bufw.write();
bufw.newline();
bufw.flush();//写一行刷新一次

缓冲区原理:

  1. 使用了底层流对象从具体设备上获取数据,并将数据存储到缓冲区的数组内
  2. 通过缓冲区的read()方法从缓冲区中获取具体的字符数据,这样就提高了效率
  3. 如果用read()方法读取字符数据并存储到另一个容器中,直到读取到了换行符时,另一个容器中临时存储的数据转成字符串返回,就形成了readLine()功能

模拟缓冲区原理:

read()readLine()方法
(/io/src/io/xenaliu/io/d20100107/buffer/MyBufferReader.java)

装饰设计模式

一个引出栗子

TextReader:读取文本
MediaReader:读取媒体数据

抽取共性,形成体系
Reader
  |--TextReader
  |--MediaReader

需求1:提高读取文本的效率,使用缓冲技术,提供一个读取文本更高效的读取方法。覆盖TextReader中的方法,建立高效的Read()方法。所以建立一个TextReader的子类,用以高效的读取。

需求2:读取媒体数据也想高效,那就同理,也给读取媒体数据的对象派生一个高效子类

继承体系:
Reader
  |--TextReader
    |--BufferedTextReader
  |--MediaReader
    |--BufferedMediaReader

一个小问题: 如果Reader中还有读取其他数据的子类,如果要高效,就必须给其他子类也添加一个高效子类。为了给具体读取数据的对象增加一些功能,需要通过子类来完成,但是这样做,会导致这个集成体系很臃肿。仅仅为了增加一些功能,而进行继承,是不建议的

解决方法: 这些子类无非就是需要高效,而这些高效的功能实现是一致的,就是提供了一个缓冲区而已,没有必要每一个对象都存在一个功能重复的子类。干脆单独定义一个具备这个缓冲功能的对象class BufferedReader,哪个子类需要被缓冲,就将哪个子类传递进来。

class BufferedReader extends Reader{
    private [];//提供数组
    BufferedReader(Reader r){}//对Reader高效
    read(){//高效的读取动作
        操作的是数组
    }
}

解决后的继承体系:
Reader
  |--TextReader
  |--MediaReader
  |--BufferedReader

这种设计方式减少了继承体系的臃肿,增加了功能,比继承更为灵活,称为装饰设计模式

装饰设计模式概述

解决问题: 给一组类增加功能,避免继承的臃肿,提高灵活性。

注意: 装饰类和被装饰类必须所属于同一个体系,通常装饰类都会提供构造函数接收被装饰类对象,装饰类通常不单独存在。

举个栗子:
房子 居住()
|--毛坯楼房 居住(){简陋}
|--毛坯平房
|--田园风光房
|--欧式风格房

class 田园风光房 extends 房子{
    田园风光房(房子){}
    居住(){
        田园风格的居住,惬意!
    }
}

class 欧式风格房 extends 房子{
    欧式风格房(房子){}
    居住(){
        欧式设计的居住,高端大气上档次!
    }
}

毛坯楼房 x = new 毛坯楼房();
x.居住();//普通
田园风光房 y = new 田园风光房(x);
y.居住();//普通惬意 
欧式风格房 z = new 欧式风格房(y);
z.居住();//普通惬意高端大气上档次

标准输入输出流

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

需求: 将键盘录入的数据存储到文件中
按字节读取:
(/io/src/io/xenaliu/io/d20100107/readkey/ReadKeyDemo.java)
按行读取:
(/io/src/io/xenaliu/io/d20100107/readkey/ReadKeyDemo2.java)

以后但凡提到了键盘录入,就使用以下操作,一行一行的读取,除非要对读取每一个字节操作

BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

IO流对象使用规律

规律总结

字节流:
InputStream                 OutputStream
FileInputStream             FileOutputStream
FilterInputStream           FilterOutputStream
  |--BufferedInputStream       |--BufferedOutputStream

字符流:
Reader               Writer
InputStreamReader     OutputStreamWriter
  |--FileReader         |--FileWriter
BufferedReader        BufferedWriter

问题: IO流中对象很多,解决问题(处理设备上的数据)时到底改用哪个对象?

四个明确:

  1. 要操作的数据时数据源还是数据目的?根据需求明确要读还是要写
    源:InputStream Reader
    目的:OutputStream Writer

  2. 要操作的设备上的数据是字节还是文本?
    源:
      字节:InputStream
      文本:Reader
    目的:
      字节:OutputStream
      文本:Writer

  3. 明确数据所在的具体设备
    源设备:
      硬盘:文件,File开头
      内存:数组、字符串
      键盘:Syetem.in
      网络:Socker
    目的设备:
      硬盘:文件,File开头
      内存:数组、字符串
      屏幕:Syetem.out
      网络:Socker

  4. 是否需要额外功能?
    转换吗:转换流 InputStreamReader OutputStreamWriter
    高效吗:缓冲区对象 BufferedReader BufferedWriter
    有多个源吗:序列流 SequenceInputStream
    对象需要序列化吗:ObjectInputStream ObjectOutputStream
    需要操作基本类型数据保证字节原样性吗:DataOutputStream DataInputStream

规律应用

需求一:将字符串写入到文件中

明确1: 有源吗?有目的吗?

源:字符串String,不用IO技术,直接定义String字符串即可
目的:文件。使用IO技术,输出流OutputStream Writer

明确2: 是文本数据吗?

是。目的:Writer

明确3: 具体设备是?

目的设备:硬盘
Writer体系中的File开头的对象
具体要使用的对象是FileWriter

FileWriter fw = new FileWriter("a.txt");
fw.writer(string);

明确4: 需要额外功能吗?

需要,高效。缓冲区对象BufferedWriter

BufferedWriter bufw = new BufferedWriter(new FileWriter("a.txt"));
bufw.write(string);
bufw.newLine();
bufw.flush();

需求二:复制一个文本文件,有可能在复制过程中对文本进行过滤

明确1: 有源吗?有目的吗?

源:文件。使用IO技术,输入流InputStream Reader
目的:文件。使用IO技术,输出流OutputStream Writer

明确2: 是文本数据吗?

是。
源:Reader
目的:Writer

明确3: 具体设备是?

源设备:硬盘
Reader体系中的File开头的对象
具体要使用的对象是FileReader
目的设备:硬盘
Writer体系中的File开头的对象
具体要使用的对象是FileWriter

注: 如果仅做复制动作,不需要考虑数据是字节还是文本,直接使用字节流即可。

FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fis = new FileOutputStream("b.txt");

但是如果在复制过程中,需要对文本中的字符数据进行操作,必须使用字符流

FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
fw.writer(string);

明确4: 需要额外功能吗?

需要,高效。缓冲区对象BufferedReader BufferedWriter

BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
String line = bufr.readLine();
line = line.replace("nba","美国职业球赛大联盟");
bufw.write(line);

需求三:读取键盘录入将数据存储到文件中

明确1: 有源吗?有目的吗?

源:键盘。使用IO技术,输入流InputStream Reader

目的:文件。使用IO技术,输出流OutputStream Writer

明确2: 是文本数据吗?

是。
源:Reader
目的:Writer

明确3: 具体设备是?

源设备:键盘
System.in
目的设备:硬盘
Writer体系中的File开头的对象
具体要使用的对象是FileWriter

InputStream in = System.in;
FileWrite fw = new FileWrite("a.txt");

byte[] buf = new byte[1024];
int len = in.read(buf);
String str = new String(buf,0,len);
fw.write(str);

明确4: 需要额外功能吗?

需要,转换。因为明确源的体系是Reader,可是具体设备System.in时字节流,需要字符流,需要转换功能,将字节流转成字符流。字节-->字符 InputStreamReader

InputStreamReader isr = new InputStreamReader(System.in);
FileWriter fw = new FileWriter("a.txt");

还需要额外功能吗?

需要,高效

BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new FileWriter("a.txt"));

需求四:读取文本文件打印到屏幕上

明确1: 有源吗?有目的吗?

源:文件。使用IO技术,输入流InputStream Reader
目的:屏幕。使用IO技术,输出流OutputStream Writer

明确2: 是文本数据吗?

是。
源:Reader
目的:Writer

明确3: 具体设备是?

源设备:硬盘
Reader体系中的File开头的对象
具体要使用的对象是FileReader
目的设备:屏幕
System.out

FileReader fr = new FileReader("a.txt");
PrintStream out = System.out;

其实这样就已经完成需求了,因为PrintStream中有很多print方法。

fr.read();
out.println();

明确4: 需要额外功能吗?

需要,高效。缓冲区对象BufferedReader BufferedWriter

BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new OutputStreamWriter(System.out);

需求五:读取文件中的文本数据,将数据按照UTF-8的方式存储到文件中

是。
源:Reader
目的:Writer

明确3: 具体设备是?

源设备:硬盘
Reader体系中的File开头的对象
具体要使用的对象是FileReader
目的设备:硬盘
Writer体系中的File开头的对象
具体要使用的对象是FileWriter

FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
fw.writer(string);

但是不符合题目的要求,对于目的的要求必须是UTF-8的编码,使用必须使用额外功能
明确4: 需要额外功能吗?

需要,转换。

FileReader fr = new FileReader("a.txt");//默认编码
OutputStreamWriter osw = new OutputStreamWriter(new OutputStream("b.txt"),"utf-8");

还需要其他额外功能吗?

需要,缓冲区

BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new OutputStream("b.txt"),"utf-8"));

IO包中的其他类

总结

  1. IO基础流对象: 直接调用底层资源,操作数据的对象
  2. 根据io流的学习规律,后面出现的流对象无非是在增加一些额外功能 :

需求:写一个数据(整数)到文件中,保证输出值的表现形式
可以通过将整数转成字符串,变成字节数组写入目的地。
简化方式,之前学习过输出语句发现要输出的内容都原样不变的体现出来。输出语句对应的对象PrintStream,提供了很多打印的方法,打印方法的好处在于保证输出值的表现形式。
Printwriter:字符打印流

需求:保证数据值字节原样性不变
如:写一个整数,源是四个字节,希望目的也是四个字节。
需要可以操作基本数据类型数值的对象。
DataOutputStream

源和目的都是内存的流对象:
字节流:
ByteArrayInputStream ByteArrayOutputStream

字符流
CharArrayReader CharArrayWriter
StringReader StringWriter
原理其实是用过流的read()write()方法对数组及字符串进行操作
关闭这些流都是无效的,因为并未调用系统资源,不需要抛出IOException

需求:对文件进行读或者写的操作,想从哪里读就从哪里读,想从哪里写就从哪里写。

你可能感兴趣的:(IO流)