IO 流

IO 概述


什么是 IO:磁盘与内存之间的数据交互,可以看做是一种数据的流动,按照流动方向,以内存为基准,分为输入 input 和输出 output,即流向内存的是输入流,流出内存的是输出流。Java IO操作主要是指使用 java.io包下的内容,进行输入、输出操作。输入也叫读取数据,输出也叫写出数据。
IO 流

IO 的分类:根据数据的流向分为:输入流输出流。根据数据的类型分为:字节流(以字节为单位)和字符流(以字符为单位)。

 输入流输出流
字节流InputStreamOutputStream
字符流ReaderWriter

字节流


一切文件数据(文本、图片、视频)在存储时,都是以二进制数字的形式保存,传输时也是如此。所以,字节流可以传输任意文件数据,在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输始终为二进制数据。

字节输入流【InputStream】:java.io.InputStream 抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存。它定义了字节输入流的基本共性。

public abstract int read() 

从输入流读取数据的下一个字节

public int read(byte b[] b) 

从输入流中读取一些字节数,并将它们存储到字节数组b中

public int read(byte b[] b, int off, int len) 

off 写入数据的位置,数组中的起始偏移量。从输入流读取多达len字节的数据到字节数

public void close() 

关闭此输入流并释放与此流相关联的任何资源

字节输出流【OutputStream】 :java.io.OutputStream 抽象类是表示字节输出流的所有类的超类,它定义了字节流的基本共性功能方法。

public abstract void write(int b) 

将指定的字节写入此输出流

public void write(byte b[]) 

将 b.length字节从指定的字节数组写入此输出流

public void write(byte b[], int off, int len) 

从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流

public void flush() 

刷新此输出流并强制任何缓冲的输出字节被写出

public void close() 

关闭此输出流并释放此流相关联的任务系统资源

文件流


FileInputStream 把硬盘中的数据读取到内存中

/**
 * java.io.InputStream: 字节输入流。 此抽象类是表示字节输入流的所有类的超类。
 * java.io.FileInputStream extends InputStream: 文件输入流
 * 作用:把硬盘文件中的数据,读取到内存中
 *
 * 构造方法:public FileInputStream(String name)   文件路径
 *          public FileInputStream(File file)    文件
 * 构造方法的作用:
 *      1、创建一个 FileInputStream 对象;
 *      2、会把 FileInputStream对象指向构造方法中创建的文件
 *  java程序--JVM--OS读取方法--读取文件
 *  1个中文 GBK占两个字节 UTF-8占3个字节
 */
public class MyInputStream {
    public static void main(String[] args) {

        FileInputStream fis = null;
        /**
         * JDK7 新特性【了解】  在 try 后边可以增加一个() ,在括号中定义流对象,那么这个流对象的作用域就在 try中有效,
         * try 中的代码执行完毕,会自动把流对象释放,不用写 finally。多个流用 ;号隔开
         * 例如:  try(FileInputStream fis = new FileInputStream("test.txt");
         *            FileOutputStream fos = new FileOutputStream("test.txt");){}
         *
         * JDK9 新特性【了解】
         *      在 try前边定义流对象
         *      在 try 括号中引入流对象名称 ,不用写 finally
         * 例如:
         *       FileInputStream fis = new FileInputStream("test.txt");
         *       FileOutputStream fos = new FileOutputStream("test.txt");
         *      try(fis ; fos){}
         */
        try {
            //1、创建 FileInputStream对象,参数为绑定的数据源
            fis = new FileInputStream("test.txt");
            //2、读取文件 read ,当读到结尾的时候返回 -1 , 注意需要用一个变量来接收 read返回值,不要读两次
            /*int len = 0;
            while((len = fis.read())!= -1){
                System.out.println((char)len);
            };*/
            /**
             *  一次读取多个字节的方法,byte[] 起到缓冲作用,存储每次读取到的字节个数。数组的长度一般定义为1024(1kb)或整数倍
             */
            byte[] bytes = new byte[2];
            int lens = fis.read(bytes);
            System.out.println(lens);//2 每次读取字节的个数
            System.out.println(Arrays.toString(bytes)); //[97, 98]
            System.out.println(new String(bytes));//ab
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(fis != null){
                //3、关闭流
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

IO 流

FileOutputStream 把内存中的数据写入到硬盘的文件中

/**
 * FileOutputStream: 文件输出流,将内存数据写入到文件中
 * 构造方法: FileOutputStream(String name) 传入一个文件的路径
 *           FileOutputStream(File file)  传入一个文件
 *           FileOutputStream(String name,boolean append) 当 append为true 的时候,就不会覆盖文件,继续在文件的末尾追加写数据
 * 构造方法的作用:
 *     1、创建一个 FileOutputStream 对象
 *     2、会根据构造方法中传递的文件/文件路径,创建一个空的文件
 *     3、会把 FileOutputStream 对象指向创建好的文件
 * 写数据的原理
 *      java程序---JVM--- OS(操作系统)---将数据写入文件中
 *  写换行:换行符
 *      windows rn
 *      linux /n
 *      max /r
 */
public class MyOutputStream {
    public static void main(String[] args) {
        FileOutputStream  fos = null;
        {
            try {
                //1、创建一个 FileOutputStream对象
                fos = new FileOutputStream("test.txt");
                /**
                 * 2、调用 FileOutputStream对象的 writer方法,将数据写入文件
                 *  会把十进制的整数,转为二进制存入硬盘.任意的记事本,打开时都会查询编码表,把字节转为字符表示。查询 ASKII码
                 *  所以 test.txt中存的是 d
                 */
                fos.write(100);
                //写出多个字节
                byte[] b = "你好".getBytes();
                fos.write(b);
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if(fos != null){
                    //3、释放资源,流使用会占用一定的内存,因此需要释放
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

字符流


当使用字节流读取文本时,可能会有一个问题。就是遇到中文字符时,可能不会显示完整的字符,因为一个中文字符可能占用多个字节存储。所以 Java提供一些字符流,以字符为单位读写数据,专门用于处理文本文件。

字符输入流【Reader】:java.io.Reader 抽象类是读取字符流所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法:

public int read() 

从输入流读取一个字符

public int read(char cbuf[]) 

从输入流中读取一些字符,并将它们存储到字符数组 cbuf中

abstract public void close() 

关闭此流并释放与此流相关联的任何系统资源

FileReader 文件字符输入流,继承 InputStreamReader 。

/**
 * 字符输入流 Reader 输入流的最顶层父类
 * FileReader 构造方法:
 *      FileReader(String fileName)
 *      FileReader(File file)
 * 作用:
 *      1、创建一个 FileReader 对象
 *      2、会把 FileReader 对象指向要读取的文件
 *
 *
 */
public class MyReader {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            //1、创建 FileReader对象,参数中绑定读取的资源
            fr = new FileReader("test.txt");
            //2、使用 read读取当个字符并返回
            int len = 0;
            /*while((len = fr.read()) != -1){
                System.out.print((char)len); //你好!
            }*/
            //读取多个字符
            char[] cs = new char[1024];
            while((len = fr.read(cs)) != -1){
                System.out.printf(new String(cs,0,len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(fr != null){
                //3、释放资源
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

字符输出流【Writer】:java.io.Writer 抽象类是所有输出字符流的最顶层父类。它定义了字符输入流的基本共性功能方法:

public void write(int c) 

写入单个字符

public void write(char cbuf[]) 

写入字符数组

abstract public void write(char cbuf[], int off, int len) 

写入字符数组的某一部分

public void write(String str) 

写入字符串

public void write(String str, int off, int len) 

写入字符串的某一部分

abstract public void flush() 

刷新流的缓冲

abstract public void close() 

关闭此流,但先要刷新它

FileWriter 文件字符输出流,继承 OutputStreamReader

/**
 * FileWriter 文件字符输出流
 * 作用:把内存中的数据写入到文件中
 * 构造方法:
 *       public FileWriter(String fileName)  传入文件的路径
 *       public FileWriter(File file)  传入一个文件
 *       public FileWriter(String fileName , boolean append)  传入文件的路径   append=true 的时候不覆盖文件,在文件后面 append的输出
 *  *    public FileWriter(File file , boolean append)  传入一个文件
 * 构造方法的作用:
 *      1、会创建一个 FileWriter对象;
 *      2、会创建一个文件,根据传入的参数
 *      3、将 FileWriter对象指向创建好的文件
 */
public class MyWriter {
    public static void main(String[] args) {
        FileWriter fw = null;
        try {
            //1、创建 FileWriter对象,绑定要写入数据的目的地
            fw = new FileWriter("test.txt");
            //2、使用 writer 将数据写入到内存缓冲区中(字符转化为字节的过程)
            char[] cs = {'a','b','c'};
            fw.write(cs);
            fw.write("你好!");
            //3、使用 flush 将缓冲区中的数据刷新到文件中
            //fw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(fw != null){
                //4、关闭流,会触发 flush方法
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Properties 集合


/**
 * java.util.Properties 集合 extends Hashtable<k,v>
 *     Properties 类表示一个持久的属性集。Properties 可保存在流中或从流中加载。
 *     Properties 集合是一个唯一和 IO流相结合的集合,
 *          可以使用集合中的方法 sotre() ,把集合中的临时数据,持久化到硬盘中
 *                             load(), 把硬盘中的数据读取到集合中
 *     Properties 集合是一个双列集合,key 和 value 都是字符串
 *
 */
public class MyProperties {
    public static void main(String[] args) {
        // 使用 Properties 集合存储数据,遍历取出数据
        //1、创建 Properties 对象
        Properties prop = new Properties();
        //2、添加数据
        prop.setProperty("you","小三");
        prop.setProperty("mine","正房");
        prop.setProperty("she","小四");
        /**
         * 3、将数据写入到磁盘中保存
         * store(OutputStream out, String comments)
         * store(Writer writer, String comments)   可以写中文
         */
        FileReader fr = null;
        FileWriter fw = null;
        try {
            fw = new FileWriter("test.txt");
            fr = new FileReader("test.txt");
            /**
             * 文件中的内容如下:
             * #save data
             * #Sun Nov 01 16:26:13 CST 2020
             * you=小三
             * she=小四
             * mine=正房
             */
            prop.store(fw, "save data");  //第二个参数是注释

            /**
             * load 将文件中的数据读取:
             *     存储键值对的文件中,键与值默认的连接符号可以是 =,空格(其它符号)
             *     # 表示注释
             *     键和值默认都是字符串
             */
            Properties propRead = new Properties();
            propRead.load(fr);//将在数据到 Properties
            Set<String> strings = propRead.stringPropertyNames();//获取 key列表
            /**
             * 输入为:
             *      you = 小三
             *      she = 小四
             *      mine = 正房
             */
            for(String key: strings){
                System.out.println(key + " = " + propRead.get(key));
            }

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

缓冲流


缓冲流也叫高效流,是对4个基本的 Filexxx流的增强,所以也是4个流,按照数据类型分为:
【1】字节缓冲流:BufferedInputStream,BufferedOutputStream
【2】字符缓冲流:BufferedReader,BufferedWriter
缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲数组,通过缓冲区读写,减少系统IO次数,从而提高效率。

字节缓冲输出流:BufferedOutputStream

/**
 *  继承 OutputStream 提高写入效率
 *  构造方法:
 *      BufferedOutputStream(OutputStream out) 创建一个新的缓冲区输出流,以将数据写入指定的底层输出流,默认缓冲区大小= 8192
 *      BufferedOutputStream(OutputStream out, int size)  创建一个新的缓冲区输出流,以将具体指定缓冲区大小的数据写入底层输出流
 */
public class MyBufferedOutputStream {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        BufferedOutputStream bos = null;
        try {
            //1、创建字节输出流 FileOutputStream
            fos = new FileOutputStream("test.txt");
            //2、创建缓冲流 BufferedOutputStream
            bos = new BufferedOutputStream(fos);
            //3、使用 writer 方法写入内部缓冲区中
            bos.write("使用内部缓冲区".getBytes());
            //4、flush 将内部缓冲区的数据刷新到文件中
            bos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(bos != null){
                //5、close 关闭流,会先执行 flush后关闭
                try {
                    bos.close(); //会自动关闭 fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

字节缓冲输入流:BufferedInputStream

/**
 * 继承 InputStream 称为 字节缓冲输入流
 * 构造方法:
 *      BufferedInputStream(InputStream in)
 *      public BufferedInputStream(InputStream in, int size)
 */
public class MyBufferedInputStream {
    public static void main(String[] args) {
        //1、创建 FileInputStream对象
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        try {
            fis = new FileInputStream("test.txt");
            //2、创建 BufferedInputStream 对象,提高 FileInputStream对象读取的效率
            bis = new BufferedInputStream(fis);
            //3、使用 read方法读取数据
            int len = 0;
            byte[] bytes = new byte[1024];
            while((len = bis.read(bytes)) != -1){
                System.out.println(new String(bytes,0,len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //4、关闭流
            if(bis != null){
                try {
                    bis.close(); //自动关闭 fis
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

BufferedWriter :新方法  newLine() 表示换行,替换了 “rn" BufferedReader:新方法 readLine() 读取一个文本行,读取一行数据。如果到达流末尾,则返回 null

 转换流


字符编码 Character Encoding:就是一套自然语言的字符与二进制数之间的对应规则;
字符集 Charset:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。常见字符集有 ASCII、GBK、Unicode字符集。
IO 流

在 IDEA中,使用 FileReader读取项目中的文本文件,由于 IDEA 默认使用 UTF-8编码,所有没有任何问题。但是当读取 Windows系统中创建的文本文件时,由于 Windows系统的默认是 GBK编码,就会出现乱码。

转化流  InputStreamReader是 Reader的子类,是从字节流到字符流的桥梁,他读取字节,并使用指定的字符集将其解码为字符,它的字符集可以由名称指定,也可以接收平台默认的字符集。

/**
 * InputStreamReader:是 Reader的子类,是从字节流到字符流的桥梁,他读取字节,并使用指定的字符集将其解码为字符,它的字符集可以由名称指定,也可以接收平台默认的字符集。
 * 构造方法:
 *      InputStreamReader(InputStream in)
 *      InputStreamReader(InputStream in, String charsetName)
 */
public class MyInputStreamReader {
    public static void main(String[] args) {
        //1、创建 InputStreamReader对象,构造方法中传入字节输入流和指定的编码表名称  指定的编码要与文件的编码相同,否则会发生乱码;
        InputStreamReader isr = null;
        try {
            isr = new InputStreamReader(new FileInputStream("text.txt"), "GBK");
            //2、使用 read 方法读取文件
            int len = 0;
            while((len = isr.read()) != -1){
                System.out.println((char)len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(isr != null){
                //3、释放资源
                try {
                    isr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

OutputStreamWriter是 Writer的子类,是从字符流到字节流的桥梁,他写入数据,并使用指定的字符集将其编码为字符,它的字符集可以由名称指定,也可以接收平台默认的字符集。

/**
 * java.io.OutputStreamWriter 继承了 Writer
 * OutputStreamWriter 是字符流通向字节流的桥梁,可使用指定的 charset 将要写入流中的字符编码成字节。
 * 构造方法:
 *      OutputStreamWriter(OutputStream out) 使用默认的字符编码
 *      OutputStreamWriter(OutputStream out, Charset cs)  可以指定字符编码,不区分大小写
 *
 */
class MyOutputStreamWriter {
    public static void main(String[] args) {
        //1、创建一个 OutputStreamWriter 对象
        OutputStreamWriter osw = null;
        try {
            osw = new OutputStreamWriter(new FileOutputStream("text.txt"), "GBK");
            //2、使用 writer 写出数据
            osw.write("你好");
            //3、flush 刷新数据
            osw.flush();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //4、关闭流
            if(osw != null){
                try {
                    osw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

序列化流


把对象以流的方式写入到文件中保存,对象中不仅仅是字符,也有字节。就有了 ObjectOutputStream[对象的序列化 writeObject(p)] 和 ObjectInputStream[对象的反序列化 readObject()]

ObjectOutputStream 对象序列化流

/**
 * java.io.ObjectOutputStream extends OutputStream
 * 构造器:
 *      ObjectOutputStream(OutputStream out) 创建写入指定 OutputStream 的 ObjectOutputStream
 *      特有的成员方法:writeObject
 */
public class MyObjectOutputStream {
    public static void main(String[] args) {
        //1、创建 ObjectOutputStream 对象,构造方法中传递字节输出流
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream("text.txt"));
            //2、通过 writeObject 把对象写入到文件中
            oos.writeObject(new Person("张三",1));//通过二进制序列化流存储的,无法打开查看。
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //3、关闭流
            try {
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

ObjectInputStream 对象反序列化流

/**
 * java.io.ObjectInputStream 继承了 InputStream 将对象从文件中读取使用
 * 构造方法:
 *      ObjectInputStream(InputStream in) 创建从指定 InputStream 读取的 ObjectInputStream
 * 特有的方法:
 *      readObject() 返回一个 object对象
 *
 * transient 关键字:
 *       static 关键字:静态关键字,被 static修饰的成员变量不能被序列化的,序列化的都是对象的默认值。transient 与 static的作用是一样的,但却没有 static的含义
 * Person对象需要实现 Serializable 并且要定义序列化ID
 * 集合类的存储和对象相同
 */
public class MyObjectInputStream {
    public static void main(String[] args) {
        //1、创建 ObjectInputStream对象,构造方法中传递字节输入流
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream("text.txt"));
            //2、使用 readObject 方法读取对象
            Person o = (Person)ois.readObject();
            //4、对象使用

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(ois != null){
                //3、释放资源
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

打印流


PrintStream:我们常用的 System.out.println 就是打印流。

/**
 * 可以打印各种数据值表示形式,不会抛出 IOException,有特有的方法 print println
 * 构造方法:
 *      PrintStream(File file) 输出的是一个文件
 *      PrintStream(OutputStream out) 输出的是一个字节输出流
 *      PrintStream(String fileName) 输出的是一个文件路径
 * 继承了 OutputStream 拥有该成员中的方法
 * 注意事项:如果使用继承自父类中的 write方法写数据,那么会查询编码表。如果使用 print 就会原样使用。
 * 可以改变输出语句的目的地:System.setOut() 目的地通过参数传递
 */
public class MyPrintStream {
    public static void main(String[] args) throws FileNotFoundException {
        //创建打印流,绑定要输出的目的地
        PrintStream ps = new PrintStream("text.txt");
        //使用 write 和 print写数据
        ps.write(97);//文件中为 a
        ps.print(97);//文件中为 97
        //设置输出流打印的目的地
        System.setOut(ps);
        System.out.println("此打印信息会在 text.txt文件中显示");
        //释放资源
        ps.close();
    }
}
未分类
匿名

发表评论

匿名网友