Java学习笔记(十五):IO流

简介: Java学习笔记(十五):IO流

@[toc]
  
  
  

  

Java IO流原理

  

  

I/O 是 Input/Output 的缩写,I/O技术非常实用,用于处理设备之间的数据传输。如:读/写文件,网络通讯等。

Java程序中,对于数据的输入/输出操作以 的方式进行。

java.io包下提供了各种 “流” 类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。

  

输入input :读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。

输出output :将程序(内存)中数据输出到磁盘、光盘等存储设备中,能实现永久存储。

  
  
  

  

流的分类

  

  

按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)

按数据流的流向不同分为:输入流输出流

按流的角色不同分为:节点流处理流

  

抽象基类 字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer

Java的IO流共涉及40多个类,看着多但实际上非常规则,都是从上面4个抽象基类派生出来的。

由这4个类派生出来的子类名称都是以其父类名作为子类名后缀。

  

如何选择:

对于 文本文件(.txt,.java,.c,.cpp),使用 字符流处理
对于 非文本文件(.jpg,.mp3,.avi,.doc,.ppt,...),使用 字节流处理

  
  
  

  

IO流体系

  

  

只写常用的几个
  

分类 字节输入流 字节输出流 字符输入流 字符输出流
抽象基类 InputStream OutputStream Reader Writer
文件流 (节点流) FileInputStream FileOutputStream FileReader FileWriter
缓冲流 (处理流) BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter
转换流 (处理流) InputStreamReader OutputStreamWriter

  

处理流是包裹在节点流外层的一种流,作用于已有流的基础之上。

  
  
  

  

节点流

  

  


先明确IO流的操作步骤,这就是模板,下面所有都是这样写的:

  1. 实例化File类的对象,指明要操作的文件
  2. 提供具体的流
  3. 数据的具体操作(读或写)
  4. 流的关闭

  

  

FileReader

  

分布来看:

// 1.实例化File类的对象,指明要操作的文件
File file = new File("hello.txt");
// 2.FileReader流的实例化
FileReader fr = new FileReader(file);
// 3.数据的读入
// read(char[] cbuf):返回每次读入cbuf数组中的字符个数。如果达到文件末尾,返回-1
char[] cbuf = new char[5];
int len;
while((len = fr.read(cbuf)) != -1){

    // 方式一:循环遍历每次读到的cbuf
//    for (int i = 0; i < len; i++) {
//        System.out.print(cbuf[i]);
//    }

    // 方式二:把每次读到的cbuf变成字符串输出
    String str = new String(cbuf,0,len);
    System.out.print(str);
}
// 4.资源关闭
fr.close();

  

但此时并不完整,有编译时异常需要异常处理,加上 try-catch-finally,完整代码:

package IO.FileReaderWriterTest;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class FileReaderTest2 {
    public static void main(String[] args) {
    
        FileReader fr = null;

        try {
        
            // 1.File类的实例化,指明要操作的文件
            File file = new File("hello.txt");

            // 2.FileReader流的实例化
            fr = new FileReader(file);

            // 3.数据的读入
            // read(char[] cbuf):返回每次读入cbuf数组中的字符个数。如果达到文件末尾,返回-1
            char[] cbuf = new char[5];
            int len;
            while((len = fr.read(cbuf)) != -1){

                // 方式一:循环遍历每次读到的cbuf
    //            for (int i = 0; i < len; i++) {
    //                System.out.print(cbuf[i]);
    //            }

                // 方式二:把每次读到的cbuf变成字符串输出
                String str = new String(cbuf,0,len);
                System.out.print(str);

            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // 4.资源关闭
                assert fr != null;
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

  

补充一点:
try-catch-finally 快捷键:Ctrl+Alt+T

在这里插入图片描述

Ctrl+Alt+T 不仅仅能用于生成 try-catch-finally 结构,还能一键生成各种结构体,非常好用!

  

  

FileWriter

  

// 1.实例化File类的对象,指明写到的文件
File file = new File("hello1.txt");
// 2.提供FileWriter流的对象
FileWriter fw = new FileWriter(file);

注意:
这里的 FileWriter构造器 new FileWriter(file,append) 可以选参数

  1. 不写默认为false或写上false:意思是在原文件上直接覆盖新的数据。
  2. 写true:在文件后加新的数据,就不覆盖原有的了。
// 3.数据写入
fw.write("我有一个梦想\n");
fw.write("你也得有一个梦想\n");
// 4.资源关闭
fw.close();

  
最后别忘了 try-catch-finally!

  

完整代码:

package IO.FileReaderWriterTest;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class FileWriterTest {
    public static void main(String[] args) {
        FileWriter fw = null;  // 追加模式写入

        try {
        
            // 1.实例化File类的对象,指明写到的文件
            File file = new File("hello1.txt");

            // 2.提供FileWriter流的对象
//        FileWriter fw = new FileWriter(file);   // append默认为false,直接覆盖
            fw = new FileWriter(file,true);

            // 3.数据写入
            fw.write("我有一个梦想\n");
            fw.write("你也得有一个梦想\n");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // 4.资源关闭
                assert fw != null;
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

  

最后补充一点 read() 及其重载方法的使用:

在这里插入图片描述
读取单个字符。

返回值:读取的字符,如果已到达流的结尾,则为-1

抛出:IOException–如果发生I/O错误

  

在这里插入图片描述
将字符读入数组。此方法将一直阻塞,直到有一些输入可用、发生I/O错误或到达流的结尾。

参数:cbuf–目标缓冲区

返回值:读取的字符数,如果已到达流的结尾,则为-1

抛出:IOException–如果发生I/O错误

  

  

FileReader和FileWriter一起使用实现文件的复制

  

package IO.FileReaderWriterTest;

import java.io.*;

public class TestAll {
    public static void main(String[] args) {

        FileReader fr = null;
        FileWriter fw = null;
        try {
        
            // 1.造文件
            File file1 = new File("hello.txt");
            File file2 = new File("hello2.txt");

            // 2.造流
            fr = new FileReader(file1);
            fw = new FileWriter(file2,true);

            // 3.数据操作
            char[] cbuf = new char[5];
            int len;
            while((len = fr.read(cbuf)) != -1){
                // 方法一:
                for (int i = 0; i < len; i++) {
                    fw.write(cbuf[i]);
                }

                //方法二:  每次写出0到len的字符
//                fw.write(cbuf,0,len);

            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // 4.关闭流
                assert fr != null;
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                assert fw != null;
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

  

  

  

注意⭐细节:

在第3步数据操作时,字符流和字节流有所区别:
  

  1. 字符流:用 char[](char型数组)

  

  1. 字节流:用 byte[](byte型数组)

  

  

  

FileInputStream和FileOutputStream一起使用实现图片的复制

  

由于字节流和字符流使用方式几乎一模一样,所以直接写个总的了

package IO.FileInputOutputStreamTest;

import java.io.*;

public class TestAll {
    public static void main(String[] args) {

        FileInputStream fws = null;
        FileOutputStream fos = null;
        try {
            File file1 = new File("a3141ac538443882b3aca9f5e462b31.jpg");
            File file2 = new File("picture.jpg");

            fws = new FileInputStream(file1);
            fos = new FileOutputStream(file2);

            byte[] b = new byte[10];
            int len;
            while((len = fws.read(b)) != -1){
                fos.write(b,0,len);
            }
            System.out.println("复制成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                assert fws != null;
                fws.close();
                assert fos != null;
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

  
  
  

  

处理流

  

就是“套接”在已有流的基础上,进一步加工,实现更多功能。

  

  

  

缓冲流

  

缓冲流是处理流,是包裹在节点流上的流。
  

具体做法:

造流的时候多加一步。造完节点流后,造缓冲流即可。

之后就是按部就班的操作数据和关闭资源。

  

用的类有:

  • BufferedInputStream:字节缓冲输入流

  

  • BufferedOutputStream:字节缓冲输出流

  

  • BufferedReader:字符缓冲输入流

  

  • BufferedWriter:字符缓冲输出流

  

作用: 提高流的读取、写入速度。

能提高读写速度的原因: 内部提供了一个缓冲区。

  

  

缓冲流(字节型)实现非文本文件的复制(具体步骤)

  
1、造文件

File srcFile = new File("D:\\Java\\StudyPlus\\test.txt");
File destFile = new File("D:\\Java\\StudyPlus\\test2.txt");

  
2、造流 —— 先造里层的流,后造外层的流。就像穿衣服:先穿里层,后穿外层。

2.1、造节点流

FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);

2.2、造缓冲流

BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);

  
3、数据操作:读取,写入

byte[] b = new byte[10];
int len;
while((len = bis.read(b)) != -1){
    bos.write(b,0,len);
}

  
4、关资源

要求:先关闭外层的流,再关闭内层的流。即,先关外面后关里面。就像脱衣服:先脱外层,后脱里层。

偷懒: 关闭外层流的同时,内层流也会自动关闭。所以我们只需关外层即可,内层流的关闭可以省略。

bis.close();
bos.close();

  

总的代码:

package IO.Buffered;

import java.io.*;

public class BufferedInputOutputStream {
    public static void main(String[] args) {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
            // 1.造文件
            File srcFile = new File("D:\\Java\\StudyPlus\\a3141ac538443882b3aca9f5e462b31.jpg");
            File destFile = new File("D:\\Java\\StudyPlus\\picture1.jpg");

            // 2.造流     先造里层的流,后造外层的流。   穿衣服先穿里层,后穿外层。
            // 2.1造节点流
            FileInputStream fis = new FileInputStream(srcFile);
            FileOutputStream fos = new FileOutputStream(destFile);
            // 2.2造缓冲流(处理流)
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);

            // 3.复制的细节:读取、写入
            byte[] b = new byte[10];
            int len;
            while((len = bis.read(b)) != -1){
                bos.write(b,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // 4.关资源
                // 要求:先关闭外层的流,再关闭内层的流。  先关外面后关里面。   脱衣服先脱外层,后脱里层。
                assert bis != null;
                bis.close();
                assert bos != null;
                bos.close();
                // 说明:关闭外层流的同时,内层流也会自动关闭。所以我们只需关外层即可,内层流的关闭可以省略。
                // fis.close();
                // fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

  

  

缓冲流(字符型)实现文本文件的复制

  

package IO.Buffered;


import java.io.*;

public class BufferedReaderWriter {
    public static void main(String[] args) {

        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            File srcFile = new File("D:\\Java\\StudyPlus\\test.txt");
            File destFile = new File("D:\\Java\\StudyPlus\\test2.txt");

            FileReader fr = new FileReader(srcFile);
            FileWriter fw = new FileWriter(destFile);

            br = new BufferedReader(fr);
            bw = new BufferedWriter(fw);

            // 方法一:使用char型数组
//            char[] cbuf = new char[1024];
//            int len;
//            while((len = br.read(cbuf)) != -1){
//                bw.write(cbuf,0,len);
//            }

            // 方法二:使用String
            String data;
            while((data = br.readLine()) != null){
                // 换行的方式一: 手动添加 \n 换行
//                bw.write(data + "\n");  // data中不包含换行符
                // 换行的方式二: 调用方法
                bw.write(data);
                bw.newLine();   // 提供换行的操作
            }            
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                assert br != null;
                br.close();
                assert bw != null;
                bw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

  

  

  

转换流

  

在这里插入图片描述
  

转换流: 属于字符流

  • InputStreamReader :将一个字节的输入流转换为字符的输入流
  • OutputStreamWriter :将一个字符的输出流转换为字节的输出流

  

作用: 提供字节流与字符流之间的转换

应用: 能改变文件的编码方式。如:utf-8 与 gbk间的相互转换

  

解码:字节、字节数组 ——→ 字符串、字符数组
编码:字符串、字符数组 ——→ 字节、字节数组

  
  
好记的口诀:
  
1、转化顺序和方向:

  • 先是 InputStreamReader :从左到右看 InputStream ——→ Reader 字节转字符
  • 然后 OutputStreamWriter :从右到左看 Writer ——→ OutputStream 字符转字节

2、用的流:

  • InputStreamReaderFileInputStream
  • OutputStreamWriterFileOutputStream

  
  

注意:

因为要实现编码格式的转换,所以在 new转换流时,在构造器里写转换前后的参数。

InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");

转换前是 utf-8,转换后为 gbk

还有一点:

InputStreamReader 构造器中默认编码方式为 utf-8

  

  

转换流实现文件的读入和写出

  

package IO.转换流;

import java.io.*;
import java.nio.charset.StandardCharsets;

public class Test {
    public static void main(String[] args) {

        InputStreamReader isr = null;
        OutputStreamWriter osw = null;
        try {
            FileInputStream fis = new FileInputStream("test.txt");
            FileOutputStream fos = new FileOutputStream("test_gbk.txt");

            isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
            osw = new OutputStreamWriter(fos,"gbk");

            char[] cbuf = new char[20];
            int len;
            while((len = isr.read(cbuf)) != -1){
                osw.write(cbuf,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                assert isr != null;
                isr.close();
                assert osw != null;
                osw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

此时,新文件的编码方式就变了,变成 gbk了。

相关文章
|
1月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
8天前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(基础篇)
从Java环境的搭建到实际代码的编写,从基本用法的讲解到底层原理的剖析,深度解析Java基础知识。本文是《Java学习路线》专栏的起始文章,旨在提供一套完整的Java学习路线,覆盖Java基础知识、数据库、SSM/SpringBoot等框架、Redis/MQ等中间件、设计模式、架构设计、性能调优、源码解读、核心面试题等全面的知识点,并在未来不断更新和完善,帮助Java从业者在更短的时间内成长为高级开发。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(基础篇)
|
7天前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
8天前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)
本文是Java基础的进阶篇,对异常、集合、泛型、Java8新特性、I/O流等知识进行深入浅出的介绍,并附有对应的代码示例,重要的地方带有对性能、底层原理、源码的剖析。适合Java初学者。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)
|
22小时前
|
Java 大数据 API
Java 流(Stream)、文件(File)和IO的区别
Java中的流(Stream)、文件(File)和输入/输出(I/O)是处理数据的关键概念。`File`类用于基本文件操作,如创建、删除和检查文件;流则提供了数据读写的抽象机制,适用于文件、内存和网络等多种数据源;I/O涵盖更广泛的输入输出操作,包括文件I/O、网络通信等,并支持异常处理和缓冲等功能。实际开发中,这三者常结合使用,以实现高效的数据处理。例如,`File`用于管理文件路径,`Stream`用于读写数据,I/O则处理复杂的输入输出需求。
|
8天前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(高级篇)
本文是“Java学习路线”中Java基础知识的高级篇,主要对多线程和反射进行了深入浅出的介绍,在多线程部分,详细介绍了线程的概念、生命周期、多线程的线程安全、线程通信、线程同步,并对synchronized和Lock锁;反射部分对反射的特性、功能、优缺点、适用场景等进行了介绍。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(高级篇)
|
17天前
|
数据采集 Java 数据挖掘
Java IO异常处理:在Web爬虫开发中的实践
Java IO异常处理:在Web爬虫开发中的实践
|
30天前
|
Java 数据处理
Java IO 接口(Input)究竟隐藏着怎样的神秘用法?快来一探究竟,解锁高效编程新境界!
【8月更文挑战第22天】Java的输入输出(IO)操作至关重要,它支持从多种来源读取数据,如文件、网络等。常用输入流包括`FileInputStream`,适用于按字节读取文件;结合`BufferedInputStream`可提升读取效率。此外,通过`Socket`和相关输入流,还能实现网络数据读取。合理选用这些流能有效支持程序的数据处理需求。
25 2
|
1月前
|
XML 存储 JSON
【IO面试题 六】、 除了Java自带的序列化之外,你还了解哪些序列化工具?
除了Java自带的序列化,常见的序列化工具还包括JSON(如jackson、gson、fastjson)、Protobuf、Thrift和Avro,各具特点,适用于不同的应用场景和性能需求。
|
23天前
|
jenkins Java Shell
jenkins学习笔记之十三:配置SonarScanner扫描Java项目
jenkins学习笔记之十三:配置SonarScanner扫描Java项目