从零学Java IO流

发布时间:2024年01月17日

Java I/O流


在这里插入图片描述

1 什么是流?

概念:内存与存储设备之间传输数据的通道。

在这里插入图片描述

  • 水借助管道传输;数据借助流传输。

1.1 流的分类

按方向【重点】 :

  • 输入(读取)流:将<存储设备>中的内容读取到<内存>中。
  • 输出(写入)流:将<内存>中的内容写入到<存储设备>中。

在这里插入图片描述

按处理单位:

  • 字节流:以字节为单位,可以读写所有数据 。
  • 字符流:以字符为单位,只能读写文本数据 。

按功能:

  • 节点流:具有实际传输数据的读写功能。
  • 过滤流:在节点流的基础之上增强功能。

2 字节流

字节流的父类(抽象类)

  • InputStream:字节输入流
    • public int read(){}
    • public int read(byte[] b){}
    • public int read(byte[] b,int off,int len){}
  • OutputStream:字节输出流
    • public void write(int n){}
    • public void write(byte[] b){}
    • public void write(byte[] b,int off,int len){}

2.1 文件字节流

FileInputStream

  • public int read(byte[] b) //从流中读取多个字节,将读到内容存入b数组,返回实际读到的字节数;如果达到文件的尾部,则返回-1。

代码演示:

public class TestFileInputStream {
    public static void main(String[] args) throws IOException {
        //1 创建对象, 指定文件位置
        FileInputStream fis = new FileInputStream("e:\\javaee2308.txt");
        //2 读取
        int s1,s2;
        //2.1 单个字节读取
        /*s1 = fis.read();
        System.out.println((char) s1);

        while ((s2=fis.read())!=-1) {
            System.out.println((char) s2);
        }*/
        //2.2 多个字节读取
        byte[] bytes = new byte[1024 * 4];
        int len;
        while ((len=fis.read(bytes))!=-1) {
            System.out.println(new String(bytes,0,len));
        }
        //3 关闭流
        fis.close();
    }
}

FileOutputStream

  • public void write(byte[] b) //一次写多个字节,将b数组中所有字节,写入输出流。

代码演示:

public class TestFileOutputStream {
    public static void main(String[] args) throws Exception{
        //1 创建对象, 指定文件的位置
        //如果文件不存在, 则自动创建, 如果文件存在, 默认覆盖内容, 可设置追加方式
        FileOutputStream fos = new FileOutputStream("e:\\aaa.txt");
        //2 写入 换行转义符: Win: \r\n; Linux: \n; Mac: \r;
        String s = "Hello World\r\n";
        for (int i = 0; i < 10; i++) {
            fos.write(s.getBytes());
        }
        //3 关闭
        fos.close();
        System.out.println("写入成功");
    }
}

2.2 字节缓冲流

缓冲流:BufferedOutputStream/BufferedInputStream

  • 提高IO效率,减少访问磁盘的次数;
  • 数据存储在缓冲区中,flush是将缓存区的内容写入文件中,也可以直接close。

在这里插入图片描述

代码演示:

BufferedInputStream:

public class TestBufferInputStream {
    public static void main(String[] args) throws Exception {
        //1 创建字节缓冲流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("e:\\javaee2308.txt"));
        //2 读取
        int d;
        while ((d= bis.read())!=-1) {
            System.out.println((char) d);
        }
        //3 关闭(关闭缓冲流时,会自动关闭字节流)
        bis.close();
    }
}

BufferedOutputStream:

public class TestBufferOutputStream {
    public static void main(String[] args) throws Exception{
        //1 创建字节缓冲流
//        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("e:\\ccc.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(Paths.get("e:\\ccc.txt")));
        //2 写入
        String s = "HelloWorld";
        for (int i = 0; i < 10; i++) {
            bos.write(s.getBytes()); //写入缓冲区
        }
        //3 刷新缓冲区
        bos.flush();
        //4 关闭缓冲流(如果缓冲区中有数据会自动刷新)
        bos.close();
    }
}

课堂案例
使用字节流实现复制图片

  • 创建两个流,一个字节输入流,一个字节输出流。
  • 字节输入流读取文件,字节输入流写入文件。

代码演示:

public class CopyFile {
    public static void main(String[] args) throws Exception{
        //1 创建
        BufferedInputStream bis = new BufferedInputStream(
                Files.newInputStream(Paths.get("D:\\Users\\胡昊龙\\Pictures\\美图\\微信图片_20240116191011.jpg"))
        );
        BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(Paths.get("e:\\copy.jpg")));
        //2 读取 和 写入
        byte[] bytes = new byte[1024 * 8];
        int len;
        while ((len= bis.read(bytes))!=-1) {
            bos.write(bytes,0,len);
            bos.flush();
        }
        //3 关闭
        bis.close();
        bos.close();
        System.out.println("复制成功");
    }
}

2.3 对象流

对象流:ObjectOutputStream/ObjectInputStream

  • 增强了缓冲区功能
  • 增强了读写8种基本数据类型和字符串功能
  • 增强了读写对象的功能
    • readObject() 从流中读取一个对象
    • writeObject(Object obj) 向流中写入一个对象

使用流传输对象的过程称为序列化、反序列化。

  • 序列化:把对象写入到硬盘或网络中的过程。
  • 反序列化:把硬盘或网络的二进制文件读取到内存形成对象的过程。

对象序列化的细节:

  • 序列化类及其对象属性必须实现Serializable接口。
  • transient修饰为临时属性不参与序列化,静态属性也不参与序列化。
  • 读取到文件尾部的标志:java.io.EOFException。
  • 使用serialVersionUID属性保证序列化的类和反序列化的类是同一个类。

代码演示:

ObjectOutputStream:

public class TestObjectOutputStream {
    public static void main(String[] args) throws Exception{
        //序列化
        Student stu = new Student("张三", 20);
        //1 创建
        ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("e:\\张三.bin")));
        //2 写入
        oos.writeObject(stu);
        //3 刷新
        oos.flush();
        //4 关闭
        oos.close();
        System.out.println("序列化完成");
    }

    /**
     * 标记接口 Serializable 序列化接口
     * 静态内部类 Student对象
     */
    static class Student implements Serializable {
      代码块...
    }
}

ObjectInputStream:

public class TestObjectInputStream {
    public static void main(String[] args) throws Exception{
        //反序列化
        //1 创建
        ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get("e:\\张三.bin")));
        //2 读取
        TestObjectOutputStream.Student student = (TestObjectOutputStream.Student) ois.readObject();
        System.out.println(student.toString());
        //3 关闭
        ois.close();
        System.out.println("反序列化完成");
    }
}

3 字符流

思考

文本文件和图片、音频、视频文件在硬盘中有区别吗?

  • 本质上没有区别,都是二进制数据。
  • 文本文件有字符编码。

文件的后缀名作用?修改后缀名是否会改变文件内容?

  • 后缀名的作用:标识文件的类型,从而决定使用什么软件打开文件。
  • 修改后缀名不会改变文件内容。

字符编码: 当编码方式和解码方式不一致时,会出现乱码。

名称说明
ISO-8859-1收录除ASCII外,还包括西欧、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号。
UTF-8针对Unicode的可变长度字符编码
GB2312简体中文
GBK简体中文、扩充
BIG5台湾,繁体中文

字符流的父类(抽象类)

  • Reader:字符输入流

    • public int read(){}

    • public int read(char[] c){}

    • public int read(char[] b,int off,int len){}

  • Writer:字符输出流

    • public void write(int n){}

    • public void write(String str){}

    • public void write(char[] c){}

3.1 文件字符流

FileWriter

  • public void write(String str) //一次写多个字符,将b数组中所有字符,写入输出流。

代码演示:

public class TestFileWriter {
    public static void main(String[] args) throws Exception {
        //1 创建文件字符流
        FileWriter fw = new FileWriter("e:\\aaa.txt");
        //2 写入
        for (int i = 0; i < 10; i++) {
            fw.write("好好学习, 天天向上\r\n");
        }
        //3 刷新
        fw.flush();
        //4 关闭
        fw.close();
        System.out.println("写入完成");
    }
}

FileReader

  • public int read(char[] c) //从流中读取多个字符,将读到内容存入c数组,返回实际读到的字符数;如果达到文件的尾部,则返回-1。

代码演示:

public class TestFilerReader {
    public static void main(String[] args) throws Exception{
        //1 创建文件字符流
        FileReader fr = new FileReader("e:\\aaa.txt");
        //2 读取
        char[] chars = new char[1024 * 4];
        int len;
        while ((len=fr.read(chars))!=-1) {
            System.out.println(new String(chars,0,len));
        }
        //3 关闭
        fr.close();
    }
}

3.2 转换流

桥转换流:InputStreamReader/OutputStreamWriter

  • 可将字节流转换为字符流。
  • 可设置字符的编码方式。

代码演示:

InputStreamReader

public class TestInputStreamReader {
    public static void main(String[] args) throws Exception{
        //1 创建
        InputStreamReader isr = new InputStreamReader(Files.newInputStream(Paths.get("e:\\ddd.txt")), "GBk");
        //2 读取
        char[] chars = new char[1024 * 4];
        int len;
        while ((len=isr.read(chars))!=-1) {
            System.out.println(new String(chars,0,len));
        }
        //3 关闭
        isr.close();
        System.out.println("读取成功...");
    }
}

OutputStreamWriter

public class TestOutputStreamWriter {
    public static void main(String[] args) throws Exception{
        //1 创建
        OutputStreamWriter osw = new OutputStreamWriter(Files.newOutputStream(Paths.get("e:\\ddd.txt")),"GBK");
        //2 写入
        for (int i = 0; i < 10; i++) {
            osw.write("好好学习,天天向上\r\n");
        }
        //3 刷新
        osw.flush();
        //4 关闭
        osw.close();
        System.out.println("写入成功...");
    }
}

3.3 字符缓冲流

缓冲流:BufferedWriter/BufferedReader

  • 支持输出换行符。
  • 可读取一行数据。

BufferedWriter

public class TestBufferWriter {
    public static void main(String[] args) throws Exception{
        //1 创建
        BufferedWriter bw = new BufferedWriter(new FileWriter("e:\\BBB.txt"));
        //2 写入
        for (int i = 0; i < 10; i++) {
            bw.write("好好学习,天天向上");
            bw.newLine();
        }
        //3 刷新
        bw.flush();
        //4 关闭
        bw.close();
    }
}

BufferedReader

public class TestBufferReader {
    public static void main(String[] args) throws Exception{
        //1 创建
        BufferedReader br = new BufferedReader(new FileReader("e:\\BBB.txt"));
        //2 读取
        String data;
        while ((data=br.readLine())!=null) {
            System.out.println(data);
        }
        //3 关闭
        br.close();
    }
}

4 打印流

打印流:把数据原样打印。

  • 字节打印流: PrintStream
  • 字符打印流: PrintWriter
  • 封装了print() / println()方法,支持写入后换行。

代码演示

PrintStream

public class PrintStream {
    public static void main(String[] args) throws Exception {
        //1 创建
        PrintWriter ps = new PrintWriter("e:\\ppp.txt");
        //2 打印
        ps.println(97);
        ps.println(true);
        ps.println(3.12412446);
        ps.println("Hello World");
        //3 刷新
        ps.flush();
        //4 关闭
        ps.close();
    }
}

System.out

  • PrintStream类型
  • 默认打印到控制台
  • 重定向标准输出流
public class TestSystemOut {
    public static void main(String[] args) throws Exception{
        System.out.println("helloworld");//控制台输出"helloworld"
        //重定向标准输出流
        System.setOut(new PrintStream("d:\\console.txt"));
        System.out.println("helloworld");
    }
}

System.in

  • InputStream
public class TestSystemIn {
    public static void main(String[] args) throws Exception{
        //Scanner input=new Scanner(System.in);
        //字节输入流InputStream
        //1 读取一个字节
//        int d= System.in.read();
//        System.out.println((char)d);
        //2 读取一个字符
//        InputStreamReader isr=new InputStreamReader(System.in);
//        int d=isr.read();
//        System.out.println((char)d);
        //3 读取一行
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        String d = br.readLine();
        System.out.println(d);
    }
}

5 RandomAccessFile

RandomAccessFile即可以读取文件内容,也可以向文件中写入内容。
但是和其他输入/输入流不同的是,程序可以直接跳到文件的任意位置来读写数据。

读写模式 r:只读 rw:读写

作用: 快速定位数据,支持并发读写 方便的操作二进制文件

代码演示:

public class TestRandomAccessFile {
    public static void main(String[] args) throws Exception{
        write();
        read();
    }
    //写入文件
    public static void write() throws Exception{
        //1 创建RandomAccessFile对象
        RandomAccessFile raf=new RandomAccessFile("d:\\random.txt","rw");
        //2 写入(把内存的二进制数据写入硬盘)
        raf.writeUTF("张三");//2+6
        raf.writeInt(20); //4个字节
        raf.writeDouble(180.5);//8个字节
        raf.writeBoolean(true);//1个字节

        raf.writeUTF("李四");//2+6
        raf.writeInt(22); //4个字节
        raf.writeDouble(185.5);//8个字节
        raf.writeBoolean(false);//1个字节
        //3关闭
        raf.close();
    }
    //读取文件
    public static void read() throws Exception{
        //1 创建RandomAccessFile对象
        RandomAccessFile raf=new RandomAccessFile("d:\\random.txt","r");
        //2读取
        //2.1 skipByte()跳过指定的字节个数
//        raf.skipBytes(21);
//        raf.skipBytes(21);
        //2.2 seek() 设置文件指针的偏移位置
        raf.seek(21);
//        raf.seek(21);
        String name = raf.readUTF();
        int age = raf.readInt();
        double height = raf.readDouble();
        boolean gender = raf.readBoolean();
        System.out.println(name+"..."+age+"..."+height+"..."+gender);
        //3关闭
        raf.close();
    }
}

6 Properties集合

特点:存储属性名和属性值。属性名和属性值都是字符串类型。没有泛型。和流有关

代码演示:

public class TestProperties {
    public static void main(String[] args) throws Exception {
        //创建集合
        Properties properties=new Properties();
        //1添加元素
        properties.setProperty("username","张三");
        properties.setProperty("age","20");
        properties.setProperty("gender","男");
        System.out.println(properties);
        //2遍历
        //2.1 keySet();
        //2.2 entrySet();
        //2.3 stringPropertyNames();
        Set<String> strings = properties.stringPropertyNames();
        for (String key : strings) {
            System.out.println(key+"..."+properties.getProperty(key));
        }
        //3和流有关的方法
        //3.1 list() 列表输出
        System.out.println("-----------list() 列表输出----------");
        properties.list(System.out);
        //3.2 store() 存储到属性文件(属性文件:后缀名以.properties结尾的文件)
        //属性文件的特点:
        //1 每行包含一个属性名和属性值,属性名和属性值之间使用等号隔开
        //2 属性名和属性值不要使用双引号包裹
        //3 属性文件不推荐使用中文, 可以unicode编码表示
        //4 属性文件的注释使用#开头
        FileOutputStream fos = new FileOutputStream("d:\\stu.properties");
        properties.store(fos,"注释内容");
        fos.close();
        //3.3 load() 加载属性文件 【重点】
        Properties properties2=new Properties();
        FileInputStream fis=new FileInputStream("d:\\stu.properties");
        properties2.load(fis);
        fis.close();
        System.out.println(properties2);
    }
}
文章来源:https://blog.csdn.net/weixin_50858647/article/details/135658887
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。