【Java SE学习】——详细学习Java IO体系

IO体系

在这里插入图片描述

4 个抽象基类:InputStream、OutputStream、Reader、Writer

在这里插入图片描述

笔记:字节流与字符流、输入流与输出流、节点流与处理流,是在IO体系基础和抽象基类上加入其他条件做的区分

流的概念

流是一种抽象的概念,是对数据传输的总称,数据在设备间的传输称为流(类比于水管中的水),更具体一点,是内存与数据之间传输数据的通道(包含水管和水)。

Java中的IO流:数据在内存与存储设备之间的传输可以看做是数据在流动,把内存作为参考对象,流向内存方向的是输入流(Input),流出内存方向的是输出流(Output)。

IO的作用

用来处理设备间文件传输问题,常见场景:文件上传、文件下载

使用场景

如果操作的是纯文本文件,优先使用字符流;

如果操作的二进制文件,优先使用字节流;

如果不确定,优先使用字节流。

原因:

  • 字符流:字节流操作的数据单元是8位的字符;以字节为单位,可以读写所有数据
  • 字节流:字节流操作的数据单元是16位的字符(2个字节);以字符为单位,只能读写文本数据

IO流的使用步骤

步骤:

  • 创建节点对象
  • 创建 IO 流对象
  • 具体的 IO 操作(read()、write())
  • 关闭 IO 流对象

相关知识

文件名称分隔符

Linux: /

windows : \

行分隔符

UNIX/LINUX/BSD : \n

Windows: \r

字节流与字符流

区分规则:按单位分

  1. 字符流(Character Stream):字符流主要用于处理文本数据,如字符串、文件等。它以字符为单位进行读写操作,每个字符占用多少字节由编码方式决定。字符流的编码方式可以是UTF-8、GBK等。

    • ASCII编码:ASCII编码中,每个字符占用一个字节,共128个字符(包括英文字母、数字、标点符号和控制字符)。
    • UTF-8编码:UTF-8编码是一种变长的编码方式,它可以根据不同的字符使用1到4个字节进行编码。例如,英文字母、数字和常用标点符号占用一个字节,而中文汉字等复杂字符占用3个或4个字节。
    • GBK编码:GBK编码是一种双字节编码方式,每个字符占用两个字节。GBK编码主要用于中文字符的表示。

    在Java中,常用的字符流类有InputStreamReader、OutputStreamWriter、FileReader、FileWriter等。

  2. 字节流(Byte Stream):字节流主要用于处理二进制数据,如图片、音频、视频等。它以字节为单位进行读写操作,每个字节占用一个字节。字节流的编码方式可以是任意的,例如ISO-8859-1、UTF-16等。在Java中,常用的字节流类有InputStream、OutputStream、FileInputStream、FileOutputStream等。

总结:字符流主要用于处理文本数据,而字节流主要用于处理二进制数据。

节点流与处理流

区分根据:按功能分

  • 节点流:节点流是指直接连接到数据源或目标的流,例如文件、网络连接等。节点流可以读取或写入基本类型的数据或对象。Java中常用的节点流有FileInputStream、FileOutputStream、Socket等。
  • 处理流:处理流是对一个已存在的流进行连接和封装,以便对数据进行处理。处理流提供了一些额外的功能,例如缓冲、编码、解码等。Java中常用的处理流有BufferedReader、BufferedWriter、DataInputStream、DataOutputStream等。

在Java中,可以使用处理流来包装节点流,以实现更高级的功能。例如,可以使用BufferedReader来包装FileReader,以便对文件进行高效的读取操作。

总结:处理流在节点流的基础上进行包装提供了更多功能

输入流与输出流

区分规则:以内存为参考,按数据流的方向分

  • 输入流(InputStream):输入流用于从数据源中读取数据,例如文件、网络连接等。它提供了一系列的读取方法,如read()、read(byte[] b)等。Java中常用的输入流有FileInputStream、ByteArrayInputStream、BufferedInputStream等。
  • 输出流(OutputStream):输出流用于将数据写入到目标位置,例如文件、网络连接等。它也提供了一系列的写入方法,如write(int b)、write(byte[] b)等。Java中常用的输出流有FileOutputStream、ByteArrayOutputStream、BufferedOutputStream等。

输入流:InputStream(字节输入流)和Reader(字符输入流)

  • 创建字节/字符数组作为缓冲区

    ,read in

    • int read():从输入流中读取一个字节/字符,返回所读取的字节数据/字符数据,如果已读到流的末尾,则返回 -1
    • int read(byte[]/char[] buf):从输入流中最多读取 buf.Iength 个字节/字符的数据,并将其存储在字节数组/字符数组 buf 中,返回实际读取的字节数/字符总数,如果已读到流的末尾,则返回 -1
    • int read(byte[]/char[] buf, int off, int len):从输入流中最多读取 len 个字节/字符的数据,并将其存储在字节数组/字符数组 buf 中,从数组的 off 位置开始放入,返回实际读取的字节数/字符总数,如果已读到流的末尾,则返回 -1
  • 标准字节输入流,读取键盘的输入:System.in

输出流:OutputStream(字节输出流)和Writer(字符输出流)

  • write out
    • void write(int c):将指定的字节/字符 c 输出到输出流中
    • void write(byte[]/char[] buf):将字节数组/字符数组 buf 中的数据输出到指定输出流中
    • void write(byte[]/char[] buf,int off,int len):将字节数组/字符数组 buf 中从 off 位置开始,长度为 len 的字节/字符输出到输出流中
    • void flush():刷新此输出流并强制写出所有缓冲的输出字节/字符(仅对缓冲输出流和字符输出流起作用)
  • Writer 类(字符输出流)中还包含如下两个方法
    • void write(String str):将 str 字符串里包含的字符输出到指定输出流中
    • void write(String str, int off, int len):将 str 字符串里从 off 位置开始,长度为 len 的字符输出到指定输出流中

文件

File类

在java.io包下,只能操作文件和目录,不能访问文件本身

全局静态常量

  • 路径分隔符:char pathSeparatorChar、String pathSeparator

  • 文件名称分隔符:char separatorChar、String separator

  • 构造器

    • File(String pathname):将给定的路径名字符串pathname转换为抽象路径名来创建File对象
    • File(String parent, String child):File(File parent, String child):根据 parent 抽象路径名和 child 路径名字符串创建 File 对象

访问文件名

  • File file = new File("C:/abc/123.txt");
  • String getName():返回此 File 对象所表示的文件名目录名,123.txt
  • String getPath():返回此 File 对象所对应的路径名,C:/abc/123.txt
  • File getAbsoluteFile():返回此 File 对象的绝对路径
  • String getAbsolutePath():返回此 File 对象所对应的绝对路径名,C:/abc/123.txt
  • File getParentFile():返回此 File 对象所对应的父路径,如果没有,返回 null,C:/abc
  • String getParent():返回此 File 对象所对应的父路径名

检测文件

  • boolean exists():判断 File 对象所对应的文件或目录是否真实存在
  • boolean isFile():判断 Hie 对象所对应的是否是文件
  • boolean isDirectory():判断 File 对象所对应的是否是目录
  • boolean isAbsolute():判断 File 对象是否是绝对路径
  • boolean canRead():判断 File 对象所对应的文件和目录是否可读
  • boolean canWrite():判断 File 对象所对应的文件和目录是否可写
  • boolean canExecute():判断应用程序是否可以执行此 File 对象所对应的文件或目录

获取文件常规信息

  • long length():获取文件内容的长度(单位为字节)(如果此路径名表示一个目录,则返回值是不确定的)
  • long lastModified():获取文件的最后修改时间

操作文件

  • boolean createNewFile():当此 File 对象所对应的文件不存在但对应的父路径存在时,新建一个该 File 对象所指定的文件
  • static File createTempFile(String prefix, String suffix):在默认的临时文件目录中创建一个临时的文件,使用给定前缀、 系统生成的随机数和给定后缀作为文件名(prefix 参数至少是 3 字节长,suffix 参数为 null 时,将使用默认的后缀“.tmp”)
  • static File createTempFile(String prefix, String suffix, File directory):在 directory 所指定的目录中创建一个临时的文件,使用给定前缀、 系统生成的随机数和给定后缀作为文件名
  • boolean mkdir():当此 File 对象所对应的目录不存在但对应的父目录存在时,新建一个 File 对象所对应的目录
  • boolean mkdirs():新建一个 File 对象所对应的目录包括所有必需但不存在的父目录
  • boolean delete():删除 File 对象所对应的文件或目录
  • void deleteOnExit():注册一个删除钩子,指定当 Java 虚拟机退出时,删除 File 对象所对应的文件或目录
  • boolean renameTo(File dest):用指定的路径名重新命名对 File 对象所对应的文件或路径
  • File[] listFiles():列出 File 对象的所有子文件和子目录的绝对路径,返回 File 数组
  • String[] list():列出 File 对象的所有子文件名和子目录的名称,返回 String 数组
  • static File[] listRoots():列出系统所有的根路径

文件过滤器

  • String[] list(FilenameFilter filter)

  • File[] listFiles(FilenameFilter filter):列出 File 对象的所有符合条件的子文件和目录,返回 File 数组

  • FilenameFilter 接口中的抽象方法:boolean accept(File dir, String name)

  • 底层依次对指定 File 的所有子目录或者文件进行迭代,如果该方法返回 true,则 list() 方法会列出该子目录或者文件

  • Java文件过滤器是一种用于过滤文件的方法,通常与File类的listFiles()方法一起使用。通过实现FilenameFilter接口并重写accept()方法,可以自定义文件过滤规则。

    import java.io.File;
    import java.io.FilenameFilter;
    
    public class FileFilterExample {
        public static void main(String[] args) {
            // 创建一个目录对象
            File directory = new File("D:/example");
    
            // 创建一个文件过滤器对象,过滤出扩展名为.txt的文件
            FilenameFilter filter = new FilenameFilter() {
                @Override
                public boolean accept(File dir, String name) {
                    return name.endsWith(".txt");
                }
            };
    
            // 使用过滤器获取目录下符合条件的文件列表
            File[] files = directory.listFiles(filter);
    
            // 遍历文件列表并打印文件名
            for (File file : files) {
                System.out.println(file.getName());
            }
        }
    }
    
        
    

    在这个示例中,首先创建了一个File对象,表示要过滤的目录。然后,创建了一个实现了FilenameFilter接口的匿名类,并重写了accept()方法。在accept()方法中,检查文件名是否以".txt"结尾,如果是,则返回true,否则返回false。最后,使用listFiles()方法和自定义的过滤器来获取目录下符合条件的文件列表,并遍历打印文件名。

其他补充

节点流

节点流——文件流

访问文件:
FileInputStream和FileReader
  • 构造器(当指定的File对象对应的文件不存在时,会在对应的目录下新建该文件)
    • FileInputStream(File file)
    • FileInputStream(String name)
    • FileInputReader(File file)
    • FileInputReader(String name)
FileOutputStream 和 FileWriter
  • 构造器(当指定File对象对应的文件不存在时,会在对应的目录下新建文件)
    • FileOutputStream(File file, boolean append)
    • FileOutputStream(String name, boolean append)
    • FileWriter(File file, boolean append)
    • FileWriter(String fileName, boolean append)

数组流(内存流)

  • 访问数组:字节流以字节数组为节点,字符流以字符数组为节点ByteArrayInputStream、ByteArrayOutputStreamCharArrayReader、CharArrayWriter
  • 访问字符串StringReader、StringWriter

管道流(线程通讯流)

  • 将 A 线程的管道输出流连和 B 线程的管道输入流连接(connect)来实现线程之间通信功能
  • PipedlnputStream、 PipedOutputStreamPipedReader、 PipedWriter

Scanner 类进行输入操作

  • java.util 包中,可以使用正则表达式来解析基本类型和字符串的简单文本扫描器
  • 构造器Scanner(File source, String charsetName)Scanner(InputStream source, String charsetName)Scanner(String source):创建一个从指定字符串扫描的扫描器
  • 实例方法:(xxx表示数据类型,如byte、int 、boolean等)boolean hasNextXxx():判断此扫描器输入信息中的下一个标记是否可以解释为一个 xxx 值Xxx nextXxx():将输入信息的下一个标记扫描为一个 xxx 值boolean hasNextLine() :如果在此扫描器的输入中存在另一行,则返回 trueString nextLine():此扫描器执行当前行,并返回跳过的输入信息Scanner useDelimiter(Pattern pattern):将此扫描器的分隔模式设置为指定模式,返回 this

Properties 类加载配置文件

  • void load(InputStream inStream)``void store(OutputStream out, String comments)

访问其它进程

  • 以子进程为节点:用于本进程读写其他进程中的数据
  • Process 类中用于让程序和其子进程进行通信的实例方法:InputStream getInputStream():获取子进程的输入流OutputStream getOutputStream():获取子进程的输出流InputStream getErrorStream():获取子进程的错误流

RandomAccessFile

  • 可以自由访问文件的任意地方
  • 构造器RandomAccessFile(File file, String mode)RandomAccessFile(String name, String mode)模式参数:“r”、“rw”、“rws” 或 “rwd”
  • 实例方法void writeXxx(xxx v):按 x 个字节将 v 入该文件void readXxx(xxx v):从此文件读取一个 xxx(注意:writeXxx 和 readXxx 必须要对应起来,writeByte 写出的数据,此时只能使用 readByte 读取回来)void writeUTF(String str):使用 modified UTF-8 编码将一个字符串写入该文件(比 UTF-8 多 2 个字节)String readUTF():从此文件读取一个字符串long getFilePointer():返回此文件中的当前偏移量void seek(long pos):设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作int skipBytes(int n):尝试跳过输入的 n 个字节以丢弃跳过的字节long length():返回此文件的长度void setLength(long newLength):设置此文件的长度

处理流

缓冲流

  • 缓冲区大小为 8192 个字节或字符

BufferedInputStream 和 BufferedReader

  • 构造器BufferedInputStream(InputStream in)、BufferedReader(Reader in)
  • BufferedReader 类(字符缓冲输入流)中特有的方法String readLine():读取一个文本行(换行 ‘\n’ 或回车 ‘\r’),如果已到达流末尾,则返回 null

BufferedOutputStream 和 BufferedWriter

  • 构造器BufferedOutputStream(OutputStream out)、BufferedWriter(Writer out)
  • BufferedWriter 类(字符缓冲输出流)中特有的方法void newLine():写入一个行分隔符(由系统属性 line.separator 定义)

转换流

  • 将字节流转换成字符流:InputStreamReader、OutputStreamWriter
  • 构造器InputStreamReader(InputStream in, String charsetName)OutputStreamWriter(OutputStream out, String charsetName)
  • 转换流特有的方法String getEncoding():返回此流使用的字符编码的名称

顺序流(合并流)

  • 把多个字节输入流合并成一个字节输入流对象
  • SequenceInputStream构造器:SequenceInputStream(InputStream s1, InputStream s2)

对象流

  • ObjectInputStream构造器:ObjectInputStream(InputStream in)实例方法:Object readObject():从 ObjectInputStream 读取对象(可能需要强转
  • ObjectOutputStream构造器:ObjectOutputStream(OutputStream out)实例方法:void writeObject(Object obj):将指定的对象写入 ObjectOutputStream
  • 对象序列化机制允许将内存中实现序列化的 Java 对象转换成字节序列(二进制流),使得对象可以脱离程序的运行而独立存在(保存到磁盘上或者通过网络传输)
  • 对象的序列化(Serialize) :将一个 Java 对象写入二进制流中
  • 对象的反序列化(Deserialize) :从二进制流中恢复该 Java 对象(反序列化时必须存在对象的字节码对象)
  • 支持序列化机制的对象的类必须实现 Serializable 接口(标识接口,无抽象方法)
  • 序列化时,会跳过 transient 或 static 关键字修饰的字段
  • 为了在反序列化时确保序列化版本的兼容性,最好在每个要序列化的类中定义一个 private static final long serialVersionUID 字段,用于标识该 Java 类的序列化版本号(具体数值可以自己定义),如果不显式定义 serialVersionUID 类变量的值,该类变量的值将由 JVM 根据类的相关信息计算

打印流

  • 都是输出流
  • PrintStream 构造器PrintStream(File file, String csn)PrintStream(String fileName, String csn)PrintStream(OutputStream out)
  • PrintWriter 构造器PrintWriter(File file, String csn)PrintWriter(String fileName, String csn)PrintWriter(Writer out, boolean autoFlush)如果 autoFlush 为 true,则在调用 println、printf 或 format 的其中一个方法时将刷新输出缓冲区,否则只会在调用 flush、close 方法时才会写出所有缓冲的输出字符
  • 实例方法void print(Object o):打印任意类型数据值或对象的字符串值void println(Object o):打印任意类型数据或对象的字符串值,然后换行PrintStream printf(String format, Object… args):使用指定格式字符串和参数将格式化的字符串写入此输出流
  • 标准字节打印流,输出到显示器:System.out标准错误字节打印流:System.err
  • System 类中重定向标准输入/输出的类方法void setIn(InputStream in):将 SyStem.in 的输入重定向到其它输入流void setOut(PrintStream out):将 SyStem.out 的输出重定向到其它打印流void setErr(PrintStream err):将 SyStem.err 的输出重定向到其它打印流

数据流

  • DataInputStream:从二进制流中读取字节,并根据所有基本类型数据进行重构
  • DataOutputStream:将数据从任意基本类型转换为一系列字节,并将这些字节写入二进制流
  • 注意:writeXxx 和 readXxx 必须要对应起来,writeByte 写出的数据,此时只能使用 readByte 读取回来

java4的NIO

NIO与传统IO的比较

  • NIO 的三大重要组件:Channel(通道)、Buffer(缓冲区)和 Selector(选择器)
  • 通道(Channel)与传统 IO 中的流(Stream)类似,表示打开到 IO 设备(例如:文件、套接字)的连接,但通道是双向的,而 Stream 是单向的,输入流只负责输入,输出流只负责输出
  • 在 NIO 中,有 FileChannel、SocketChannel、ServerSocketChannel 和 DatagramChannel 4 种 Channel 对象,第一种是针对文件的,后三种是针对网络编程的
  • 唯一能与通道交互的组件是缓冲区(Buffer),通过通道,可以从缓冲区中读写数据
  • Buffer 对象用来缓存读写的内容,在 NIO 中,通过 Channel 对象来进行读写操作,通过 Buffer 对象缓存读写的内容
  • 选择器(Selector)可以同时监控多个 SelectableChannel 的 IO 状况,是非阻塞 IO 的核心
  • FileChannel 不能与 Selector —起使用,因为 FileChannel 不能切换到非阻塞模式

NIO(New I/O)是Java中的一种新输入输出库,它提供了一种更高效的文件读写方式。与传统的IO相比,NIO有以下区别:

  1. 非阻塞性:传统的IO是阻塞性的,即在执行I/O操作时,程序会等待直到操作完成。而NIO是非阻塞性的,它可以在等待I/O操作完成的同时执行其他任务。这使得NIO在处理大量并发请求时具有更高的性能。

  2. 缓冲区:传统的IO使用字节流进行读写,每次读写都会涉及到数据的复制。而NIO使用缓冲区(Buffer)进行读写,可以减少数据复制的次数,提高读写效率。

  3. 通道(Channel):传统的IO使用字节流进行读写,需要通过Socket、FileInputStream等对象进行封装。而NIO使用通道(Channel)进行读写,可以更方便地管理多个连接和数据传输。

  4. 选择器(Selector):传统的IO使用多线程或多进程来处理多个连接,这会导致系统资源的浪费。而NIO使用选择器(Selector)来管理多个通道,可以有效地减少系统资源的消耗。

下面是一个简单的例子,说明Java中NIO与传统IO的区别:

// 传统IO示例
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class TraditionalIOExample {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);
        while (true) {
            Socket socket = serverSocket.accept();
            new Thread(() -> {
                try {
                    InputStream inputStream = socket.getInputStream();
                    byte[] buffer = new byte[1024];
                    int len;
                    while ((len = inputStream.read(buffer)) != -1) {
                        System.out.println("接收到的数据:" + new String(buffer, 0, len));
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

// NIO示例
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NIOExample {
    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.socket().bind(new InetSocketAddress(8080));
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            selector.select();
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectedKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                if (key.isAcceptable()) {
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    SocketChannel socketChannel = (SocketChannel) key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    int bytesRead = socketChannel.read(buffer);
                    if (bytesRead > 0) {
                        buffer.flip();
                        System.out.println("接收到的数据:" + new String(buffer.array(), 0, bytesRead));
                    }
                }
                iterator.remove();
            }
        }
    }
}

在这个例子中,我们分别使用了传统IO和NIO来实现一个简单的服务器。可以看到,NIO在处理多个连接时具有更高的性能,因为它使用了非阻塞性和缓冲区。

Java 7 的 NIO.2

Path 接口、Files 和 Paths 工具类

  • Paths 中的类方法Path get(String first, String… more)
  • Files 中的用于文件拷贝的类方法Path copy(Path source, Path target, CopyOption… options)``long copy(InputStream in, Path target, CopyOption… options)``long copy(Path source, OutputStream out)
  • Files 中其它的类方法:isHidden、readAllLines、size、write、list、walk、find

使用 FileVisitor 遍历文件和目录

  • Files 类遍历文件的类方法Path walkFileTree(Path start, FileVisitor<? super Path> visitor):遍历 start 路径下的所有文件和子目录Path walkFileTree(Path start, Set<FileVisitOption> options, int maxDepth, FileVisitor<? super Path> visitor):与上一个方法的功能类似,该方法最多遍历 maxDepth 深度的文件
  • 通过继承 SimpleFileVisitor(FileVisitor的实现类)来实现自己的 “ 文件访问器”
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值