IO流常规操作

IO流

IO就是输入输出,IO设备在计算机中起着举足轻重的作用,IO流也就是输入输出流,用来交互数据,进程和进程交互,进程也可以和网络等媒介交互。

一、IO流的分类

要分类,肯定得站得不同角度来看这个问题。

  • 根据流向划分,分为输入流和输出流
  • 根据数据的单位来分划,分为字节流和字符流
  • 根据功能划分,分为节点流和包装流
  • 还有很多

二、四大基本流

在java中IO流非常之多,但都继承于这四大基流,分别为,字节输入流(InputStream),字节输出流(OutputStream),字符输入流(Reader)和字符输出流(Writer).

它们都是抽象类,不能创建对象,只能创建它们的子类对象,阅读API发现,它们都有一个共同的方法close方法,用来关闭流,如果不关闭流的话进程会一直引用着文档。

三、模板操作

  • 创建源和目标
  • 创建流对象
  • 具体的IO操作
  • 关闭流

在下面的例子中穿插讲述

四、文档流

文档流操作是是文档。分为字符流和字节流,字节流操作是所有的二进制文档包括视频啊,图片啊什么的。字符流主要解决的还是中文的一些问题,因为中文多个字节(GBK是2个字节,UTF-8是3个字节),所有可能出现读一半或者写一半就不读了或者不写了,这样就出现乱码了。具体用字符流还是字节流得具体选择,字节流是可以操作字符流的,但是反过来就可能出现问题。输入就是从文档中读到进程中来,而输出就是从进程中输出到文档中。在这样章节记住一句话:读进来,写出去。下面操作一下各个流的常用方法,

  • 文档字节输入流(FileInputStream)

    int read()  从此输入流中读取一个数据字节。 
    int read(byte[] b) 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中
    int read(byte[] b, int off, int len)从此输入流中将最多 len 个字节的数据读入一个 byte 数组中

    读取文档中所有的数据,用到了一个缓冲数组,也就是每次读取5个字节,直到读完为止有个len返回当前读了几个字节,如果后面没有字节了,那么它就返回-1

    //读取文档中所有的数据
    byte[] buffer = new byte[5];//定义一个缓冲数组长度为5,每次只能读取5个字节
    int len = 0;//当前读取的字节数,没有字节就返回 -1
    while((len = in.read(buffer))!= -1 ){
        String str = new String(buffer,0,len);
        System.out.println(str);
    }
  • 文档字节输出流(FileOutputStream)

    1.void write(byte[] b) 
    将 b.length 个字节从指定 byte 数组写入此文档输出流中。 
    2.void write(byte[] b, int off, int len)
    将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文档输出流。 
    3.void write(int b) 
    将指定字节写入此文档输出流。 

    输出就是想文档中写数据,可以一次写一个字节,也可以写一个数组中的元素,这个比较简单。可以用一下四个步骤操作一下

    
    public class FileOutputStreamDemo {
      public static void main(String[] args) throws Exception {
          //1.创建源
          File f = new File("C:/Users/15626/Desktop/test/123.txt");
          //2.创建输出流对象
          FileOutputStream out = new FileOutputStream(f);
          //3.写操作
          out.write(97);//只能一个一个的写
    
          //将buffer数组中的数据写在文档中
          byte[] buffer = "ABCDEF".getBytes();
          out.write(buffer);
    
          //从索引1开始的3个字节写在文档中
          out.write(buffer, 1, 3);
    
          //4.关闭资源
          out.close();
      }
    }
  • 文档字符输入流(FileReader)

    字符流的方法是从父类中继承过来的,也和上面字节流的方法大同小异

     int read()  读取单个字符。 
     int read(char[] cbuf, int offset, int length)  将字符读入数组中的某一部分。 
    //文档字符输入流
    public class FileReaderDemo {
      public static void main(String[] args) throws IOException {
          //1.创建源
          File src = new File("C:/Users/15626/Desktop/新建文档夹 (2)/test.txt");
          //2.创建字符输入流对象
          FileReader in = new FileReader(src);
          //3.读操作
          char[] buffer = new char[10];
          int len = -1;
          while((len = in.read(buffer)) != -1){
              String str = new String(buffer,0,len);
              System.out.println(str);
          }
          //4.关闭流
          in.close(); 
      }
    }
  • 文档字符输出流(FileWriter)

    void write(char[] cbuf, int off, int len) 写入字符数组的某一部分。          
    void write(int c)  写入单个字符。 
    void write(String str, int off, int len) 写入字符串的某一部分。 

文档的拷贝操作

把指定目录的.java文档拷贝到指定的目录


//拷贝文档:把指定目录的.java文档拷贝到指定的目录
public class FileCopyDemo2 {
    public static void main(String []args) throws Exception{
        //得到指定目录的文档
        File srcDir = new File("C:/Users/15626/Desktop/新建文档夹 (2)");
        File destDir = new File("C:/Users/15626/Desktop/新建文档夹 (2)/qqq");
        //筛选出后缀是.java的文档存储在fs这个文档数组中
        File[] fs = srcDir.listFiles(new FilenameFilter(){
            @Override
            public boolean accept(File dir, String name){
                return new File(dir,name).isFile() && name.endsWith(".java");
            }
        });
        //遍历这些文档
        for (File srcFile : fs) {
            //1.创建目标
            File destFile = new File(destDir,srcFile.getName());
            //2.创建输入流和输出流对象
            FileInputStream in = new FileInputStream(srcFile);
            FileOutputStream out = new FileOutputStream(destFile);
            //3.输入输出操作
            byte[] buffer = new byte[10];
            int len = -1;
            while((len = in.read(buffer)) != -1){
                out.write(buffer, 0, len);
            }
            //4.关闭流
            in.close();
            out.close();
        }
    }
}

文档夹的拷贝

https://www.cnblogs.com/tfper/p/9855228.html

文档流正确关闭资源

https://www.cnblogs.com/tfper/p/9833722.html

五、缓冲流

缓冲流是一个包装流,目的起缓冲作用.操作流的时候,习惯定义一个byte/char数组. int read():每次都从磁盘文档中读取一个字节. 直接操作磁盘文档性能极低,为了解决这个问题,我们 定义一个数组作为缓冲区. byte[] buffer = new byte[1024]; 该数组其实就是一个缓冲区. 一次性从磁盘文档中读取1024个字节. 如此以来,操作磁盘文档的次数少了,性能得以提升,java提供的默认缓存区大小是8192(1024*8),我们一般不用修改大小.

缓冲流也有四种,分别是 BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter.操作方法都和文档流差不多,我们来看一下缓冲流到底快了多少?

/**
 * @author 15626
 *  操作字符或者字节流都应该用缓冲流包装起来,现在比较一下用缓冲流和不用缓冲流的情况写,复制一个文档所花费时间
 *  长度的比较
 */
public class BufferCompare {
    public static void main(String[] args) throws Exception {
        File srcFile = new File("C:/Users/15626/Desktop/File/test.pdf");
        File destFile = new File("C:/Users/15626/Desktop/File/copy.pdf");
        //test1(srcFile,destFile);//不用缓冲流包装       
        test2(srcFile,destFile);//用缓冲流包装       
    }
    //使用缓冲流,从内存每次读取1024个字节:15ms
    public static void test2(File srcFile, File destFile) throws Exception {
        long begin = System.currentTimeMillis();
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(srcFile));
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(destFile));
    
        byte[] buffer = new byte[1024];
        int len = -1;
        while((len = in.read(buffer)) != -1){
            out.write(buffer, 0, len);
        }
        in.close();
        out.close();
        System.out.print(System.currentTimeMillis() - begin);
    }

    //使用字节流,从磁盘中每次读取1024个字节:38ms
    public static void test1(File srcFile,File destFile) throws Exception {
        long begin = System.currentTimeMillis();
        FileInputStream in = new FileInputStream(srcFile);
        FileOutputStream out = new FileOutputStream(destFile);
        byte[] buffer = new byte[1024];
        int len = -1;
        while((len = in.read(buffer)) != -1){
            out.write(buffer, 0, len);
        }
        in.close();
        out.close();
        System.out.print(System.currentTimeMillis() - begin);
    }
}

缓冲流的效率很高,所有以后操作字符字节流都可以用缓冲流包装起来。

六、内存流

内存流就是把进程中的数据暂存在内存中,在这里也就是数组中,或者把内存中的数据输出到其他媒介,内存流也是分为字节流,字符流还有一个字符串流(保存在字符串中)。简单操作一下,熟悉一个方法

import java.io.*;
/**
 * @author 15626
 *  字节内存流,或者说是字节数组流
 *  字节内存输入流 : ByteArrayOutputStream
 *  字节内存输出流 : ByteArrayInputStream
 */
public class ByteArrayStreamDemo {
    public static void main(String[] args) throws Exception {
        //字节内存输出流:把进程的内容写到内存中的byte数组中
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        out.write("ABSAK".getBytes());
        //把进程中的数据存在了缓冲数组中,toByteArray()方法用来获取缓冲区的数据
        byte[] buffer = out.toByteArray();
        
        //字节内存输入流: 把内存中的数据读到进程中
        ByteArrayInputStream in = new ByteArrayInputStream(buffer);
        byte[] by = new byte[1024];
        int len = -1;
        while((len = in.read(by)) != -1){
            System.out.print(new String(by,0,len));
        }
        
        //关闭流无效,因为本身就在存储在内存中
        in.close();
        out.close();
    }
}
/**
 * @author 15626
 *  字符内存流或者说是字符数组流
 *  字符内存输出流:CharArrayWriter
 *  字符内存输入流:CharArrayReader
 */
public class CharArrayReaderWriterDemo {
    public static void main(String[] args) throws Exception {
        //字符内存输出流 : 从进程把数据存储到内存中的一个缓冲数组中
        CharArrayWriter cWriter = new CharArrayWriter();
        cWriter.write("你是天边最美的云彩");
        //获取缓冲数组中的内容,用toCharArray()方法获取
        char[] ch= cWriter.toCharArray();
        
        //字符内存输入流:从内存中被数据读取到进程中来
        CharArrayReader sReader = new CharArrayReader(ch);
        char[] buffer = new char[1024];
        int len = -1;
        while((len = sReader.read(buffer)) != -1){
            System.out.println(new String(buffer,0,len));
        }
        
        
        //关闭流无效,因为本身就在存储在内存中
        sReader.close();
        cWriter.close();
    }
}
/**
 * @author 15626
 *  字符串的内存流,把进程中的数据存储在一个缓冲字符串中
 *  字符串的输出流 :StringWriter
 *  字符串的输入流:StringReader
 */
public class StringWriterReaderDemo {
    public static void main(String[] args) throws Exception {
        //字符串的输出流
        StringWriter out = new StringWriter();
        out.write("你真的想成为一只帅帅气气的一只小人吗");
        //获取缓冲区的字符串
        //System.out.print(out.toString());
        
        //字符串的输入流
        StringReader in = new StringReader("海纳百川,有容乃大");
        char[] buffer = new char[1024];
        int len = -1;
        while((len = in.read(buffer)) != -1){
            System.out.print(new String(buffer,0,len));
        }
        
        //关闭流无效,因为本身就在存储在内存中
        in.close();
        out.close();
    }
}

要注意的关闭流无效,但是为了追求完美的格式,防止忘记了其他流的关闭,我还还是自娱自乐的关一下吧!

七、对象流

对象流包括ObjectInputStream和ObjectOutputStream,名字起得很有范,肯定是字节流

一般用对象流进行串行化和反串行化通过writeObject方法做串行化操作的,通过readObject方法做反串行化操作

1.串行化

指把堆内存中的Java对象数据,通过某种方式把对象存储到磁盘文档中或者传递给其他网络的节点(在网络上传输). 我们把这个过程称之为串行化.

需要做串行化的对象的类,必须实现串行化接口:java.io.Serializable接口(标志接口[没有抽象方法]). 底层会判断,如果当前对象是Serializable的实例,才允许做串行化.大多数类都已经实现了这个接口了

2.反串行化

把磁盘文档中的对象数据或者把网络节点上的对象数据,恢复成Java对象的过程.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * @author 15626
 *  对象流的串行化和反串行化操作
 *  ObjectInputStream 用WriterObjcet 来做串行化
 *  ObjectOutputStream 用ReaderObject 来做反串行化
 *  需要做串行化的类必须实现Serializable接口 
 */
public class ObjectStreamDemo {
    public static void main(String[] args) throws Exception {
        File f = new File("file/111.txt");
        writeObject(f);//串行化
        readObject(f);//反串行化
    }
    public static void readObject(File f) throws Exception{
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(f));
        Student s = (Student)in.readObject();
        System.out.println(s);
        in.close();
        
    } 
    public static void writeObject(File f) throws Exception {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
        out.writeObject(new Student("小伙子",19,"12345"));
        out.close();
    }
}
class Student implements Serializable {
    /**
     * 做反串行化操作必须存在串行化的字节码文档,由于随着版本升级肯定字节码会发生改变,java提供了串行化版本ID,
     * 可以固定这一份字节码文档,解决了这个问题
     */
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    //密码一般不会做串行化的,静态的字段和瞬态的字段不能做串行化,所以给密码加上瞬态transient修饰符
    //反串行化的结果就为null
    transient private String password;
    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + ", password=" + password + "]";
    }
    public Student(String name, int age, String password) {
        super();
        this.name = name;
        this.age = age;
        this.password = password;
    }
}

可以看到里面有两个重要的问题,一个是反串行化对象需要对象的字节码文档,需要提供串行化ID固定这份字节码文档,还有一个是transient瞬息字段是不能串行化.

八、打印流

1.字节打印流

import java.io.File;
import java.io.PrintStream;

/**
 * @author 15626
 * 字节打印流
 *  可以自动刷新的,不想API说的那样,必须用换行符或者println才可以刷新缓冲区
 */
public class PrintStreamDemo {
    public static void main(String[] args) throws Exception {
        File f = new File("file/123.txt");
        PrintStream p = new PrintStream(f);
        p.print("aa");
        p.println(121);
        p.append("a");
        p.close();
        
//      System.out.println();  //这行代码等价于下面的两行代码
//      PrintStream ps = System.out;
//      ps.println("a");
    }
}

2.字符打印流

/**
 * @author 15626
 *  字符打印流
 *  当启用自动刷新后,即new PrintWriter(new FileOutputStream(f),true);   true
 *  调用println方法或者printf或者\n后进行刷新。
 *
 *  当不启动自动刷新,需要手动刷新,调用flush方法,或者close方法会进行刷新
 */
public class PrintWriterDemo {
    public static void main(String[]args) throws Exception{
        File f = new File("file/123.txt");
        PrintWriter p = new PrintWriter(new FileOutputStream(f),true);
        p.print("wqe");
        p.println();
        p.close();
    }
}

关键词:new 字节 文档 字符 file out len buffer int public

相关推荐:

-1-4 java io java流 常用流 分类 File类 文档 字节流 字符流 缓冲流 内存操作流 合并串行流

Java IO流详解

Java学习日记-----IO流

Java源码解析——Java IO包

File类--输入流和输出流

IO---缓冲流

IO流大总结

Java 持久化操作之 --io流与串行化

Java IO流对象、多线程

java学习--基础知识阶段性总结--IO流