Java中IO类扫盲篇

文章目录

  • 一、简介
  • 二、字节流与字符流
    • 1. 字节流(InputStream、OutputStream)介绍与用法
    • 2. 字符流(Reader、Writer)介绍与用法
  • 三、文件操作与目录遍历
    • 1. File类的基本使用
    • 2. 目录遍历与递归操作
  • 四、序列化与反序列化
    • 1. 序列化与反序列化概念和作用
    • 2. 实现Serializable接口的对象序列化
    • 3. 自定义序列化与反序列化
  • 五、NIO与异步IO
    • 1. NIO概述及与传统IO的区别
    • 2. 缓冲区(Buffer)的使用
    • 3. 通道(Channel)的基本使用
    • 4. 选择器(Selector)的使用

一、简介

  1. Java IO概述:
    Java的IO(输入/输出)是用于处理与外部设备、文件和网络资源之间数据传输的机制。它提供了一套丰富的类和方法,用于读取和写入数据,并提供了灵活的处理方式。

  2. IO模型和IO流的概念:
    IO模型是指描述程序与外部设备进行数据交换的方式,主要有阻塞IO模型和非阻塞IO模型。阻塞IO模型是指程序在进行IO操作时会阻塞等待返回结果,而非阻塞IO模型是指程序在进行IO操作时可以继续执行其他任务,不需要等待返回结果。

IO流是Java中用来处理输入和输出的抽象概念。它将数据的输入和输出视为连续的数据流,通过流的方式进行读取和写入操作。Java中的IO流分为字节流和字符流两种类型,分别对应处理字节数据和字符数据。

  1. Java IO类库的分类和用途:
    Java的IO类库可以分为两个大类:字节流和字符流。
  • 字节流:以字节为单位进行读写操作,主要包括InputStream和OutputStream两个类,用于处理二进制数据,例如图片、视频等。
  • 字符流:以字符为单位进行读写操作,主要包括Reader和Writer两个类,用于处理文本数据,例如文本文件等。

除了字节流和字符流,Java的IO类库还提供了许多其他类和接口,用于处理特定类型的数据或提供更高级别的功能,例如处理文件、网络通信等。

总结起来,Java的IO类库提供了丰富的工具和方法来进行各种输入和输出操作,能够满足不同场景下的需求。

二、字节流与字符流

1. 字节流(InputStream、OutputStream)介绍与用法

// 1.1 输入字节流的基本使用(FileInputStream、ByteArrayInputStream等)

// 使用FileInputStream读取文件内容
try (InputStream inputStream = new FileInputStream("file.txt")) {
    int data;
    while ((data = inputStream.read()) != -1) {
        // 处理读取到的字节数据
        System.out.print((char) data);
    }
} catch (IOException e) {
    e.printStackTrace();
}

// 1.2 输出字节流的基本使用(FileOutputStream、ByteArrayOutputStream等)

// 使用FileOutputStream写入数据到文件
try (OutputStream outputStream = new FileOutputStream("file.txt")) {
    String data = "Hello, World!";
    outputStream.write(data.getBytes());
} catch (IOException e) {
    e.printStackTrace();
}

// 1.3 使用缓冲字节流提高IO性能(BufferedInputStream、BufferedOutputStream等)

// 使用BufferedInputStream读取文件内容
try (InputStream inputStream = new BufferedInputStream(new FileInputStream("file.txt"))) {
    int data;
    while ((data = inputStream.read()) != -1) {
        // 处理读取到的字节数据
        System.out.print((char) data);
    }
} catch (IOException e) {
    e.printStackTrace();
}

// 使用BufferedOutputStream写入数据到文件
try (OutputStream outputStream = new BufferedOutputStream(new FileOutputStream("file.txt"))) {
    String data = "Hello, World!";
    outputStream.write(data.getBytes());
} catch (IOException e) {
    e.printStackTrace();
}

2. 字符流(Reader、Writer)介绍与用法

// 2.1 输入字符流的基本使用(FileReader、StringReader等)

// 使用FileReader读取文件内容
try (Reader reader = new FileReader("file.txt")) {
    int data;
    while ((data = reader.read()) != -1) {
        // 处理读取到的字符数据
        System.out.print((char) data);
    }
} catch (IOException e) {
    e.printStackTrace();
}

// 2.2 输出字符流的基本使用(FileWriter、StringWriter等)

// 使用FileWriter写入数据到文件
try (Writer writer = new FileWriter("file.txt")) {
    String data = "Hello, World!";
    writer.write(data);
} catch (IOException e) {
    e.printStackTrace();
}

// 2.3 使用缓冲字符流提高IO性能(BufferedReader、BufferedWriter等)

// 使用BufferedReader读取文件内容
try (Reader reader = new BufferedReader(new FileReader("file.txt"))) {
    int data;
    while ((data = reader.read()) != -1) {
        // 处理读取到的字符数据
        System.out.print((char) data);
    }
} catch (IOException e) {
    e.printStackTrace();
}

// 使用BufferedWriter写入数据到文件
try (Writer writer = new BufferedWriter(new FileWriter("file.txt"))) {
    String data = "Hello, World!";
    writer.write(data);
} catch (IOException e) {
    e.printStackTrace();
}

三、文件操作与目录遍历

1. File类的基本使用

// 1.1 创建、删除和重命名文件
File file = new File("file.txt");

try {
    // 创建文件
    if (file.createNewFile()) {
        System.out.println("文件创建成功");
    }

    // 删除文件
    if (file.delete()) {
        System.out.println("文件删除成功");
    }

    // 重命名文件
    File newFile = new File("newFile.txt");
    if (file.renameTo(newFile)) {
        System.out.println("文件重命名成功");
    }
} catch (IOException e) {
    e.printStackTrace();
}

// 1.2 获取文件信息(大小、路径、修改时间等)
File file = new File("file.txt");

System.out.println("文件大小:" + file.length() + "字节");
System.out.println("文件路径:" + file.getAbsolutePath());
System.out.println("最后修改时间:" + new Date(file.lastModified()));

// 1.3 文件遍历与过滤
File dir = new File("directory");

// 遍历文件夹下的所有文件和子文件夹
for (File file : dir.listFiles()) {
    if (file.isFile()) {
        System.out.println("文件:" + file.getName());
    } else if (file.isDirectory()) {
        System.out.println("文件夹:" + file.getName());
    }
}

2. 目录遍历与递归操作

// 2.1 递归遍历目录下的文件
public static void listFiles(File directory) {
    File[] files = directory.listFiles();
    if (files != null) {
        for (File file : files) {
            if (file.isFile()) {
                System.out.println("文件:" + file.getName());
            } else if (file.isDirectory()) {
                System.out.println("文件夹:" + file.getName());
                listFiles(file);
            }
        }
    }
}

// 调用递归遍历目录
listFiles(new File("directory"));

// 2.2 搜索指定类型的文件
public static void searchFiles(File directory, String extension) {
    File[] files = directory.listFiles();
    if (files != null) {
        for (File file : files) {
            if (file.isFile() && file.getName().endsWith(extension)) {
                System.out.println("符合条件的文件:" + file.getName());
            } else if (file.isDirectory()) {
                searchFiles(file, extension);
            }
        }
    }
}

// 调用搜索指定类型的文件
searchFiles(new File("directory"), ".txt");

// 2.3 统计目录大小
public static long calculateDirectorySize(File directory) {
    long size = 0;
    File[] files = directory.listFiles();
    if (files != null) {
        for (File file : files) {
            if (file.isFile()) {
                size += file.length();
            } else if (file.isDirectory()) {
                size += calculateDirectorySize(file);
            }
        }
    }
    return size;
}

// 获取目录大小
long size = calculateDirectorySize(new File("directory"));
System.out.println("目录大小:" + size + "字节");

四、序列化与反序列化

1. 序列化与反序列化概念和作用

序列化是将对象转换为字节序列的过程,用于对象的持久化或网络传输。反序列化是将字节序列转换回对象的过程。

2. 实现Serializable接口的对象序列化

// 2.1 序列化对象到文件
try (OutputStream outputStream = new FileOutputStream("object.ser")) {
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
    MyObject myObject = new MyObject();
    objectOutputStream.writeObject(myObject);
    System.out.println("对象序列化成功");
} catch (IOException e) {
    e.printStackTrace();
}

// 2.2 从文件反序列化对象
try (InputStream inputStream = new FileInputStream("object.ser")) {
    ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
    MyObject myObject = (MyObject) objectInputStream.readObject();
    System.out.println("对象反序列化成功");
} catch (IOException | ClassNotFoundException e) {
    e.printStackTrace();
}

// 2.3 序列化对象到字节数组
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
    MyObject myObject = new MyObject();
    objectOutputStream.writeObject(myObject);
    byte[] bytes = byteArrayOutputStream.toByteArray();
    System.out.println("对象序列化成功,字节数组长度:" + bytes.length);
} catch (IOException e) {
    e.printStackTrace();
}

3. 自定义序列化与反序列化

// 3.1 实现Externalizable接口
public class MyObject implements Externalizable {
    private String data;

    // 必须提供默认构造方法
    public MyObject() {
    }

    // 实现writeExternal方法,手动控制序列化字段
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(data);
    }

    // 实现readExternal方法,手动控制反序列化字段
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        data = (String) in.readObject();
    }
}

// 3.2 控制序列化的过程(writeObject、readObject方法)
public class MyObject implements Serializable {
    private String data;

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        // 手动调用其他操作
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        // 手动调用其他操作
    }
}

// 3.3 解决序列化版本兼容性问题
public class MyObject implements Serializable {
    private static final long serialVersionUID = 1L;
    ...
}

五、NIO与异步IO

1. NIO概述及与传统IO的区别

NIO(New I/O)是Java提供的一种基于通道和缓冲区的IO模型,相比传统的IO模型,NIO具有更高的效率和灵活性。

2. 缓冲区(Buffer)的使用

// 2.1 ByteBuffer、CharBuffer等缓冲区类型
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
CharBuffer charBuffer = CharBuffer.allocate(1024);

// 2.2 缓冲区的读写操作
byteBuffer.put((byte) 'H');
byteBuffer.put((byte) 'e');
byteBuffer.put((byte) 'l');
byteBuffer.put((byte) 'l');
byteBuffer.put((byte) 'o');
byteBuffer.flip();

while (byteBuffer.hasRemaining()) {
    System.out.print((char) byteBuffer.get());
}

3. 通道(Channel)的基本使用

// 3.1 文件Channel的读写操作
try (RandomAccessFile file = new RandomAccessFile("file.txt", "rw");
     FileChannel channel = file.getChannel()) {

    ByteBuffer buffer = ByteBuffer.allocate(1024);
    int bytesRead = channel.read(buffer);
    while (bytesRead != -1) {
        buffer.flip();
        while (buffer.hasRemaining()) {
            System.out.print((char) buffer.get());
        }
        buffer.clear();
        bytesRead = channel.read(buffer);
    }
} catch (IOException e) {
    e.printStackTrace();
}

// 3.2 Socket Channel和Server Socket Channel的使用
try (SocketChannel socketChannel = SocketChannel.open()) {
    socketChannel.connect(new InetSocketAddress("example.com", 80));
    if (socketChannel.isConnected()) {
        System.out.println("连接成功");

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        socketChannel.read(buffer);

        buffer.flip();
        while (buffer.hasRemaining()) {
            System.out.print((char) buffer.get());
        }
    }
} catch (IOException e) {
    e.printStackTrace();
}

4. 选择器(Selector)的使用

选择器(Selector)是Java NIO(New IO)中的一个重要组件,用于实现非阻塞IO操作。它可以通过单个线程处理多个通道(Channel),从而提高系统的IO处理效率。

下面是使用选择器的基本步骤:

  1. 创建选择器:使用Selector.open()方法创建一个Selector对象。

    Selector selector = Selector.open();
    
  2. 注册通道:将需要监听IO事件的通道注册到选择器上。通道可以是一些可读、可写或可接受连接的网络通道,例如SocketChannelServerSocketChannel等。

    channel.configureBlocking(false); // 设置通道为非阻塞模式
    SelectionKey key = channel.register(selector, interestOps);
    

    其中,interestOps参数表示对该通道感兴趣的事件类型,包括SelectionKey.OP_READ(可读事件)、SelectionKey.OP_WRITE(可写事件)、SelectionKey.OP_ACCEPT(可接受连接事件)等。

  3. 选择就绪通道:使用selector.select()方法来选择已经准备好进行IO操作的通道。该方法会阻塞直到至少有一个通道准备好,或者超时时间到达。

    int readyChannels = selector.select();
    
  4. 处理就绪通道:使用selector.selectedKeys()方法获取选择器中已经就绪的键集合,然后遍历处理每个键对应的通道和事件。

    Set<SelectionKey> selectedKeys = selector.selectedKeys();
    for (SelectionKey key : selectedKeys) {
        if (key.isReadable()) {
            // 处理可读事件
        } else if (key.isWritable()) {
            // 处理可写事件
        } else if (key.isAcceptable()) {
            // 处理可接受连接事件
        }
        // 其他事件处理...
    }
    
  5. 取消选择键:在处理完通道后,一般需要将其对应的选择键取消,防止重复处理。

    key.cancel();
    
  6. 关闭选择器:使用selector.close()方法关闭选择器,释放资源。

    selector.close();
    

使用选择器可以实现单个线程管理多个通道,提高了系统的IO处理效率。它适用于需要同时处理多个IO操作、提高系统吞吐量的场景,例如网络编程中的服务器端。

// 4.1 注册通道到选择器
try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
     Selector selector = Selector.open()) {

    serverSocketChannel.bind(new InetSocketAddress(8080));
    serverSocketChannel.configureBlocking(false);
    serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

    while (true) {
        selector.select();

        Set<SelectionKey> selectedKeys = selector.selectedKeys();
        for (SelectionKey key : selectedKeys) {
            if (key.isAcceptable()) {
                // 处理接收事件
                ServerSocketChannel channel = (ServerSocketChannel) key.channel();
                SocketChannel socketChannel = channel.accept();
                // ...
            } else if (key.isReadable()) {
                // 处理读取事件
                SocketChannel channel = (SocketChannel) key.channel();
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                int bytesRead = channel.read(buffer);
                // ...
            }
            selectedKeys.remove(key);
        }
    }
} catch (IOException e) {
    e.printStackTrace();
}

// 4.2 监听通道事件
try (DatagramChannel datagramChannel = DatagramChannel.open();
     Selector selector = Selector.open()) {

    datagramChannel.bind(new InetSocketAddress(8080));
    datagramChannel.configureBlocking(false);
    datagramChannel.register(selector, SelectionKey.OP_READ);

    while (true) {
        selector.select();

        Set<SelectionKey> selectedKeys = selector.selectedKeys();
        for (SelectionKey key : selectedKeys) {
            if (key.isReadable()) {
                // 处理读取事件
                DatagramChannel channel = (DatagramChannel) key.channel();
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                SocketAddress sender = channel.receive(buffer);
                // ...
            }
            selectedKeys.remove(key);
        }
    }
} catch (IOException e) {
    e.printStackTrace();
}

// 4.3 多路复用(IO多路复用)
try (Selector selector = Selector.open()) {
    SocketChannel channel1 = SocketChannel.open(new InetSocketAddress("example.com", 80));
    SocketChannel channel2 = SocketChannel.open(new InetSocketAddress("example.org", 80));

    channel1.configureBlocking(false);
    channel2.configureBlocking(false);

    channel1.register(selector, SelectionKey.OP_READ);
    channel2.register(selector, SelectionKey.OP_READ);

    while (true) {
        selector.select();

        Set<SelectionKey> selectedKeys = selector.selectedKeys();
        for (SelectionKey key : selectedKeys) {
            if (key.isReadable()) {
                // 处理读取事件
                SocketChannel channel = (SocketChannel) key.channel();
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                channel.read(buffer);
                // ...
            }
        }
    }
} catch (IOException e) {
    e.printStackTrace();
}

简介:本文介绍了Java中的IO类库,包括字节流、字符流的基本使用,文件操作与目录遍历,序列化与反序列化,以及NIO与异步IO的概念与使用。通过学习本文,读者能够掌握Java IO类的基本用法和核心概念,并且理解NIO的特点和优势。

你可能感兴趣的:(java,python,开发语言,IO)