JavaIO流

  在Java.io包下主要包括输入、输出两种IO流,每种输入、输出流又可以分为字节流和字符流两大类。其中字节流以字节为单位处理输入、输出操作,而字符流则以字符来处理输入、输出操作。

一、File类

  如果希望在程序中操作文件和目录,都可以通过file类来完成。不管是文件还是目录都是使用file来操作的,File能新建、删除、重命名文件和目录,File不能访问文件内容本身。如果需要访问文件本身,则需要使用输入/输出流。

  File类可以使用文件路径字符串来创建file实例,该文件路径字符串既可以是绝对路径,也可以是相对路径。一旦创建了file对象后,就可以调用file对象的方法来访问,file类提供了很多方法来操作文件和目录。

  • String getName():返回的名称的文件或目录的路径名表示的抽象。
  •  String getPath():转换这个抽象路径名为路径名的字符串。
  • File getAbsoluteFile():返回此抽象路径名的绝对形式。 
  • String getAbsolutePath():返回此抽象路径名的绝对路径名的字符串。 
  • String getParent():返回此抽象路径名的父路径名的字符串,或 null如果路径名不叫父目录。  
  • boolean exists():检查文件或目录是否存在这种抽象路径名记。
  • boolean canRead():检查应用程序是否可以读取文件的抽象路径名记。
  • boolean canWrite():检查应用程序是否可以修改文件的抽象路径名记。 
  • boolean isFile():测试文件是否通过这种抽象路径名表示的是一种正常的文件。
  • boolean isDirectory():测试文件是否通过这种抽象路径名表示是一个目录。 
  • boolean createNewFile():自动创建一个新的空文件命名的抽象路径名的当且仅当该文件不存在。
  • boolean delete():删除文件或目录的路径名表示的抽象。 
public class FileTest {
    public static void main(String[] args) {
        File file = new File(".");
        System.out.println(file.getName());//.
        System.out.println(file.getAbsoluteFile());//E:BigData.
        System.out.println(file.getAbsolutePath());//E:BigData.
        System.out.println(file.getAbsoluteFile().getParent());//E:BigData
    }
}

二、JavaIO流的含义

  数据传输是需要通道的,而IO流就是数据传输的通道。IO流可以形象的比喻为运送货物的传输带。

  IO分类:

  • 流按操作数据类型的不同分为两种:字节流与字符流。
  • 根据数据的流向分为:输入流与输出流,程序(内存)作为参照物,程序从外部读取称为输入(Input),程序向外部写数据成为输出(Output)。
  • 根据流的角色来分,可以分为节点流和处理流,可以从一个特定的IO设备读数据的流称为节点流(低级流),处理流对一个已存在的流进行连接或封装,通过封装后的流来实现数据读写功能,也被称作高级流。

  Java的IO流共四十多个类,他们都是从四个抽象基类派生的:

  • InputStream/Reader:所有输入流的基类,前者是字节输入流,后者是字符输入流。
  • OutputStream/Writer:所有输出流的基类,前者是字节输出流,后者是字符输出流

  输入流采用隐式的记录指针来标识当前正准备从哪个“字节”开始读取,每当程序从InputStream或Reader里取出一个或多个“字节”后,记录指针自动向后移动;除此之后,InputStream和Reader里都提供了一些方法来控制记录指针的移动。

  处理流的主要功能体现在两个方面:

  • 性能的提高:主要以增加缓冲的方式来提高输入/输出的效率。
  • 操作的便捷:处理流可能提供了一系列便捷的方法来一次输入/输出大批量的内容,而不是输入/输出一个或多个字节/字符

  通过使用处理流,Java程序无须理会输入/输出节点是磁盘、网络还是其他的IO设备,程序只要将这些节点流包装成处理流,就可以使用相同的代码读写不同的设备的数据。

  IO流的特性:

  • 先进先出,最先写入输出流的数据最先被输入流读取到。
  • 顺序存取,可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。
  • 只读或只写,每个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读操作,对输出流只能进行写操作。在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流。

InputStream和Reader

  inputstream和reader是所有输入流的抽象基类,本身并不能创建实例来执行输入,几个常用方法

  • abstract int read():从输入流读取下一个数据字节。
  • int read(byte[] b):读取一定数量的字节从输入流并存入缓冲区阵列 b。 
  • int read(byte[] b, int off, int len):读到 len字节从输入流读入字节数组数据。
  • void mark(int readlimit):标记此输入流中的当前位置。
  • boolean markSupported():如果输入流的支持 markreset方法。 
  • void reset():重新定位该流在时间的 mark方法的位置上呼吁这个输入流。  
  • long skip(long n):跳过并丢弃 n字节从输入流中的数据。 
public class FileTest {
    public static void main(String[] args) throws IOException {
        FileInputStream stream = new FileInputStream("FileTest.java");
        byte[] bytes = new byte[1024];
        int hasRead = 0;
        while((hasRead = stream.read(bytes))>0){
            System.out.println(new String(bytes,0,hasRead));
        }
        stream.close();
    }
}

OutputStream和Writer

  几个常用方法:

  • abstract void write(int b):write(int b)
  • void write(byte[] b):写 b.length字节从指定的字节数组的输出流。
  • void write(byte[] b, int off, int len):写 len字节指定字节数组中的偏移 off开始到输出流。
public class FileTest {
    public static void main(String[] args) throws IOException {
        FileInputStream stream = new FileInputStream("FileTest.java");
        FileOutputStream outputStream = new FileOutputStream("newFile.txt");
        byte[] bytes = new byte[32];
        int hasRead = 0;
        while((hasRead = stream.read(bytes))>0){
            outputStream.write(bytes,0,hasRead);
        }
        stream.close();
    }
}

字节流和字符流的使用情况

  字符流和字节流的使用范围:字节流一般用来处理图像,视频,以及PPT,Word类型的文件。字符流一般用于处理纯文本类型的文件,如TXT文件等,字节流可以用来处理纯文本文件,但是字符流不能用于处理图像视频等非文本类型的文件。

字符流与字节流的转换

  转换流的作用,文本文件在硬盘中以字节流的形式存储时,通过InputStreamReader读取后转化为字符流给程序处理,程序处理的字符流通过OutputStreamWriter转换为字节流保存。

转换流的特点:

  1. 其是字符流和字节流之间的桥梁
  2. 可对读取到的字节数据经过指定编码转换成字符
  3. 可对读取到的字符数据经过指定编码转换成字节

何时使用转换流:

  1. 当字节和字符之间有转换动作时
  2. 流操作的数据需要编码或解码时

具体的对象表现:

  1. InputStreamReader:字节到字符的桥梁
  2. OutputStreamWriter:字符到字节的桥梁

这两个流对象是字符体系中的成员,它们有转换作用,本身又是字符流,所以在构造的时候需要传入字节流对象进来。

OutputStreamWriter(OutStreamout):将字节流以字符流输出。

InputStreamReader(InputStream in):将字节流以字符流输入。

字节流和字符流的区别

  字节流没有缓冲区,是直接输出的,而字符流是输出到缓冲区的。因此在输出时,字节流不调用colse()方法时,信息已经输出了,而字符流只有在调用close()方法关闭缓冲区时,信息才输出。要想字符流在未关闭时输出信息,则需要手动调用flush()方法。

  读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。

·       处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。

结论:只要是处理纯文本数据,就优先考虑使用字符流。除此之外都使用字节流。

处理流BufferedReader,BufferedWriter,BufferedInputStream

  BufferedOutputsStream,都要包上一层节点流。也就是说处理流是在节点流的基础之上进行的,带有Buffered的流又称为缓冲流,缓冲流处理文件的输入输出的速度是最快的。所以一般缓冲流的使用比较多。

三、JavaIO流的用法

  使用四个基类读取或写入文件看起来有些麻烦,如果希望简化编程,这就要借助于处理流。使用处理流来包装节点流,程序通过处理流来执行输入/输出功能,让节点流和底层的IO设置、文件交互。

public class FileTest {
    public static void main(String[] args) throws IOException {
        FileOutputStream outputStream = new FileOutputStream("newFile.txt");
        PrintStream printStream = new PrintStream((outputStream));
        printStream.println("zzzz");
        printStream.println(new FileTest());
    }
}

  通常只需要在创建处理流时传入一个节点流作为构造器参数即可,这样创建的处理流就是包装了该节点流的处理流。

public class FileTest {
    public static void main(String[] args) throws IOException {
        String src ="我愿乘风破浪
"+"去踏遍皇上海洋
";
        char[] chars = new char[32];
        int hasRead = 0;
        StringReader reader = new StringReader((src));
        while((hasRead = reader.read(chars))>0){
            System.out.println(new String(chars,0,hasRead));
        }

        StringWriter writer = new StringWriter();
        writer.write("在以为一无所有的时候
");
        writer.write("抬起头还有天空和彩虹
");
        System.out.println(writer.toString());
    }
}

字符流FileReader和FileWriter

FileReader类

构造方法摘要
FileReader(File file)
          在给定从中读取数据的 File 的情况下创建一个新 FileReader
FileReader(FileDescriptor fd)
          在给定从中读取数据的 FileDescriptor 的情况下创建一个新 FileReader
FileReader(String fileName)
          在给定从中读取数据的文件名的情况下创建一个新 FileReader

FileWriter类

构造方法摘要
FileWriter(File file)
          根据给定的 File 对象构造一个 FileWriter 对象。
FileWriter(File file, boolean append)
          根据给定的 File 对象构造一个 FileWriter 对象。
FileWriter(FileDescriptor fd)
          构造与某个文件描述符相关联的 FileWriter 对象。
FileWriter(String fileName)
          根据给定的文件名构造一个 FileWriter 对象。
FileWriter(String fileName, boolean append)
          根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。

使用FileReader和FileWriter类完成文本文件复制:

public class FileTest {
    public static void main(String[] args) throws IOException {
        //创建输入流对象
        FileReader fr=new FileReader("copyfrom.txt");
        //创建输出流对象
        FileWriter fw=new FileWriter("copyto.txt");
        //文本文件复制,一次读一个字符
        from(fr,fw);
        //文本文件复制,一次读一个字符数组
//        to(fr,fw);
        fr.close();
        fw.close();
    }

    public static void from(FileReader fr, FileWriter fw) throws IOException{
        int ch;
        while((ch=fr.read())!=-1){
            fw.write(ch);//写数据
        }
        fw.flush();
    }
    public static void to(FileReader fr, FileWriter fw) throws IOException{
        char[] chars = new char[1024];
        int len = 0;
        while((len = fr.read(chars))!=-1){
            fw.write(chars,0,len);//写数据
        }
        fw.flush();
    }
}

字符缓冲流BufferedReader和BufferedWriter

字符缓冲流具备文本特有的表现形式,行操作,public class BufferedReader extends Reader

  1. 从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
  2. 可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。
  3. 通常,Reader 所作的每个读取请求都会导致对底层字符或字节流进行相应的读取请求。因此,建议用 BufferedReader 包装所有其 read() 操作可能开销很高的 Reader(如 FileReader 和 InputStreamReader)。
  4. 将缓冲指定文件的输入。如果没有缓冲,则每次调用 read() 或 readLine() 都会导致从文件中读取字节,并将其转换为字符后返回,而这是极其低效的。 

public class BufferedWriter extends Writer

  1. 将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
  2. 可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
  3. 该类提供了 newLine() 方法,它使用平台自己的行分隔符概念,此概念由系统属性 line.separator 定义。并非所有平台都使用新行符 (' ') 来终止各行。因此调用此方法来终止每个输出行要优于直接写入新行符。
  4. 通常 Writer 将其输出立即发送到底层字符或字节流。除非要求提示输出,否则建议用 BufferedWriter 包装所有其 write() 操作可能开销很高的 Writer(如 FileWriters 和 OutputStreamWriters)。
  5. 缓冲 PrintWriter 对文件的输出。如果没有缓冲,则每次调用 print() 方法会导致将字符转换为字节,然后立即写入到文件,而这是极其低效的。

使用BufferedReader和BufferedWriter完成文件复制

public class FileTest {
    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new FileReader("copyfrom.txt"));
        BufferedWriter bw=new BufferedWriter(new FileWriter("copyto.txt"));
        char [] chs=new char[1024];
        int len=0;
        while((len = br.read(chs))!=-1){
            bw.write(chs, 0, len);
        }
        br.close();
        bw.close();
    }
}

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

字节流FileInputStream和FileOutputStream 

public class FileInputStream extends InputStream

(1)FileInputStream 从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。

(2)FileInputStream 用于读取诸如图像数据之类的原始字节流。

构造方法摘要
FileInputStream(File file)
          通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
FileInputStream(FileDescriptor fdObj)
          通过使用文件描述符 fdObj 创建一个 FileInputStream,该文件描述符表示到文件系统中某个实际文件的现有连接。
FileInputStream(String name)
          通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。

public class FileOutputStream extends OutputStream

(1)文件输出流是用于将数据写入 File 或 FileDescriptor 的输出流。文件是否可用或能否可以被创建取决于基础平台。特别是某些平台一次只允许一个 FileOutputStream(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。

(2) FileOutputStream 用于写入诸如图像数据之类的原始字节的流。

构造方法摘要
FileOutputStream(File file)
          创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
FileOutputStream(File file, boolean append)
          创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
FileOutputStream(FileDescriptor fdObj)
          创建一个向指定文件描述符处写入数据的输出文件流,该文件描述符表示一个到文件系统中的某个实际文件的现有连接。
FileOutputStream(String name)
          创建一个向具有指定名称的文件中写入数据的输出文件流。
FileOutputStream(String name, boolean append)
          创建一个向具有指定 name 的文件中写入数据的输出文件流。

使用字节流复制图片

public class FileTest {
    public static void main(String[] args) throws IOException {
        FileInputStream br = new FileInputStream("k1.jpg");
        FileOutputStream bw = new FileOutputStream("k2.jpg");
        byte [] chs=new byte[1024];
        int len=0;
        while((len = br.read(chs))!=-1){
            bw.write(chs, 0, len);
        }
        br.close();
        bw.close();
    }
}

字节缓冲流BufferedInputStream和BufferedOutputStream

public class BufferedInputStream extends FilterInputStream

BufferedInputStream 为另一个输入流添加一些功能,即缓冲输入以及支持 mark 和 reset 方法的能力。在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。mark 操作记录输入流中的某个点,reset 操作使得在从包含的输入流中获取新字节之前,再次读取自最后一次 mark 操作后读取的所有字节。

public class BufferedOutputStream extends FilterOutputStream

该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。

使用字节缓冲流实现图片的复制

public class FileTest {
    public static void main(String[] args) throws IOException {
        BufferedInputStream br = new BufferedInputStream(new FileInputStream("k1.jpg"));
        BufferedOutputStream bw = new BufferedOutputStream(new FileOutputStream("k2.jpg"));
        byte [] chs=new byte[1024];
        int len=0;
        while((len = br.read(chs))!=-1){
            bw.write(chs, 0, len);
        }
        br.close();
        bw.close();
    }
}

转换流:InputStreamReader和OutputStreamWriter

InputStreamReader和OutputStreamWriter是字符和字节的桥梁,也可称之为字符转换流。原理:字节流+编码。FileReader和FileWriter作为子类,仅作为操作字符文件的便捷类存在。当操作的字符文件,使用的是默认编码表时可以不用父类,而直接使用子类完成操作,简化代码。一旦要指定其他编码时,不能使用子类,必须使用字符转换流。

public class InputStreamReader extends Reader

(1)InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。

(2)每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。

(3)为了达到最高效率,可以考虑在 BufferedReader 内包装 InputStreamReader。例如

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

public class OutputStreamWriter extends Writer

(1)OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

(2)每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递给 write() 方法的字符没有缓冲。

(3)为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。例如:

Writer out = new BufferedWriter(new OutputStreamWriter(System.out));

public class FileTest {
    public static void main(String[] args) throws IOException {
        BufferedReader r=new BufferedReader(new InputStreamReader(System.in));
        FileWriter fw=new FileWriter("newFile.txt");
        char [] chs=new char[1024];
        int len=0;
        while((len = r.read(chs))!=-1){
            fw.write(chs, 0, len);
        }
        r.close();
        fw.close();
    }
}

public class OutputStreamWriter extends Writer

(1)OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

(2)每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递给 write() 方法的字符没有缓冲。

(3)为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。例如:

Writer out = new BufferedWriter(new OutputStreamWriter(System.out));

public class FileTest {
    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new FileReader("copy.java"));
        BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
        String line;
        while((line = br.readLine())!=null){
            bw.write(line);
            bw.write("
");
        }
        br.close();
        bw.close();
    }
}

打印流PrintWriter和PrintStream

public class PrintWriter extends Writer

(1)向文本输出流打印对象的格式化表示形式。此类实现在 PrintStream 中的所有 print 方法。不能输出字节,但是可以输出其他任意类型。

(2)与 PrintStream 类不同,如果启用了自动刷新,则只有在调用 printlnprintf 或 format 的其中一个方法时才可能完成此操作,而不是每当正好输出换行符时才完成。这些方法使用平台自有的行分隔符概念,而不是换行符。

(3)此类中的方法不会抛出 I/O 异常,尽管其某些构造方法可能抛出异常。客户端可能会查询调用 checkError() 是否出现错误。

public class FileTest {
    public static void main(String[] args) throws IOException {
        PrintWriter pw=new PrintWriter(new FileWriter("newFile.txt"),true);
        pw.write("测试打印流");
        pw.println("此句之后换行");
        pw.println("特有功能:自动换行和自动刷新");
        pw.println("利用构造器设置自动刷新");
        pw.close();
    }

    static void copy() throws IOException{
        BufferedReader br=new BufferedReader(new FileReader("copy.java"));
        PrintWriter pw=new PrintWriter("printcopy.java");
        String line;
        while((line = br.readLine())!=null){
            pw.print(line);
        }
        br.close();
        pw.close();
    }
}

public class PrintStream extends FilterOutputStreamimplements Appendable, Closeable

(1)PrintStream 为其他输出流添加了功能(增加了很多打印方法),使它们能够方便地打印各种数据值表示形式(例如:希望写一个整数,到目的地整数的表现形式不变。字节流的write方法只将一个整数的最低字节写入到目的地,
  比如写fos.write(97),到目的地(记事本打开文件)会变成字符'a',需要手动转换:fos.write(Integer.toString())

(2)与其他输出流不同,PrintStream 永远不会抛出 IOException;而是,异常情况仅设置可通过 checkError 方法测试的内部标志。
另外,为了自动刷新,可以创建一个 PrintStream;这意味着可在写入 byte 数组之后自动调用 flush 方法,可调用其中一个 println 方法,或写入一个换行符或字节 (' ')。
(3)PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。  
使用字节打印流复制文本文件:

public class FileTest {
    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new FileReader("copy.java"));
        PrintStream ps=new PrintStream("printcopy2.java");
        String line;
        while((line=br.readLine())!=null) {
            ps.println(line);
        }
        br.close();
        ps.close();
    }
}

四、IO流使用总结

  1. 明确要操作的数据是数据源还是数据目的(要读还是要写),源:InputStream  Reader,目的:OutputStream  Writer
  2. 明确要操作的设备上的数据是字节还是文本,源: 字节:InputStrea  文本:Reader  目的:字节:OutputStream  文本:Writer
  3. 明确数据所在的具体设备

    源设备:

            硬盘:文件 File开头

            内存:数组,字符串

            键盘:System.in

            网络:Socket

          目的设备:

            硬盘:文件 File开头

            内存:数组,字符串

            屏幕:System.out

            网络:Socket

  4. 明确是否需要额外功能?

    需要转换——转换流 InputStreamReader OutputStreamWriter

        需要高效——缓冲流Bufferedxxx

        多个源——序列流 SequenceInputStream

        对象序列化——ObjectInputStream,ObjectOutputStream

        保证数据的输出形式——打印流PrintStream Printwriter

        操作基本数据,保证字节原样性——DataOutputStream,DataInputStream

参考:https://www.cnblogs.com/hopeyes/p/9736642.html

原文地址:https://www.cnblogs.com/yfstudy/p/13434965.html