Java IO和NIO

Java中的IO(Input/Output,输入/输出)是处理数据流的一种方式,它允许我们从文件、网络或其他数据源读取数据,或者将数据写入到文件、网络或其他数据源。从Java 1.0开始,Java IO API就是以流的方式来处理输入和输出。

Java IO分为字节流和字符流两种类型。字节流(Byte Stream)是以字节为单位进行读写的,可以用于读写任意类型的数据;而字符流(Character Stream)是以字符为单位进行读写的,主要用于读写字符数据。

字节流和字符流

字节流类主要包括InputStreamOutputStream,而字符流类主要包括ReaderWriter

InputStreamOutputStream

InputStreamOutputStream是抽象类,分别代表输入流和输出流。常用的实现类有FileInputStreamFileOutputStreamByteArrayInputStreamByteArrayOutputStream等。

// 使用InputStream读取文件
try (InputStream input = new FileInputStream("filePath")) {
    int data;
    while ((data = input.read()) != -1) {
        // 处理读取到的字节数据
    }
} catch (IOException e) {
    // 处理异常
}

// 使用OutputStream写入文件
try (OutputStream output = new FileOutputStream("filePath")) {
    byte[] data = "Hello, Java IO".getBytes();
    output.write(data);
} catch (IOException e) {
    // 处理异常
}

ReaderWriter

ReaderWriter也是抽象类,代表字符输入流和字符输出流。常用的实现类有FileReaderFileWriterBufferedReaderBufferedWriter等。

// 使用Reader读取文件
try (Reader reader = new FileReader("filePath")) {
    int data;
    while ((data = reader.read()) != -1) {
        // 处理读取到的字符数据
    }
} catch (IOException e) {
    // 处理异常
}

// 使用Writer写入文件
try (Writer writer = new FileWriter("filePath")) {
    String data = "Hello, Java IO";
    writer.write(data);
} catch (IOException e) {
    // 处理异常
}

File类

File类是Java表示文件和目录路径的标准类,它提供了一系列方法来管理文件系统中的文件和目录。

// 创建File对象
File file = new File("filePath");

// 判断文件是否存在
if (file.exists()) {
    // 文件存在
} else {
    // 文件不存在
}

// 创建新文件
try {
    boolean success = file.createNewFile();
    if (success) {
        // 文件创建成功
    } else {
        // 文件创建失败
    }
} catch (IOException e) {
    // 处理异常
}

// 删除文件或目录
boolean success = file.delete();
if (success) {
    // 文件或目录删除成功
} else {
    // 文件或目录删除失败
}

// 判断是否是目录
boolean isDirectory = file.isDirectory();
// 判断是否是文件
boolean isFile = file.isFile();

// 获取文件名
String fileName = file.getName();
// 获取文件路径
String filePath = file.getAbsolutePath();

InputStream/OutputStream和Reader/Writer接口

在Java中,IO类的基础是byte和char数据类型,而不是对象数据类型。Java使用InputStream、OutputStream、Reader和Writer接口来实现IO操作。

InputStream和OutputStream

InputStream和OutputStream是Java IO中最基本的输入输出抽象,用于读写字节流。InputStream接口中的read()方法可以读取一个字节,返回值为读取到的字节数据,如果已经到达流的末尾,则返回-1;而OutputStream接口中的write()方法用于写入一个字节,参数为一个byte类型的数据。

// 使用InputStream读取文件
try (InputStream input = new FileInputStream("filePath")) {
    int data;
    while ((data = input.read()) != -1) {
        // 处理读取到的字节数据
    }
} catch (IOException e) {
    // 处理异常
}

// 使用OutputStream写入文件
try (OutputStream output = new FileOutputStream("filePath")) {
    byte[] data = "Hello, Java IO".getBytes();
    output.write(data);
} catch (IOException e) {
    // 处理异常
}

Reader和Writer

Reader和Writer是用于读写字符流的Java IO接口,它们可以自动将字节转换为字符。Reader接口中的read()方法可以读取一个字符,返回值为读取到的字符,如果已经到达流的末尾,则返回-1;而Writer接口中的write()方法用于写入一个字符,参数为一个char类型的数据。

// 使用Reader读取文件
try (Reader reader = new FileReader("filePath")) {
    int data;
    while ((data = reader.read()) != -1) {
        // 处理读取到的字符数据
    }
} catch (IOException e) {
    // 处理异常
}

// 使用Writer写入文件
try (Writer writer = new FileWriter("filePath")) {
    String data = "Hello, Java IO";
    writer.write(data);
} catch (IOException e) {
    // 处理异常
}

NIO

在Java 1.4中引入的NIO(New IO)是一组针对Java IO进行改进的API,主要由ChannelBufferSelector三个主要模块组成。它提供了一种基于缓冲区(Buffer)和通道(Channel)的IO方式,与传统IO流(Stream)完全不同,提供更高效的数据操作和更好的可伸缩性。

Channel

Channel 是 NIO中的一个抽象概念,它代表着数据源或数据目标的连接,类似于传统 IO 中的流。 Channel 接口提供了数据连接的独立性,例如可以打开到 TCP 网络套接字的 Channel,读写此套接字中的数据,而不需要考虑底层操作系统传输数据需要的套接字相关 API、传输速度的限制等。

NIO 中最常使用的 Channel 类型有如下几种:

  • FileChannel:用于文件的读取和写入。
  • DatagramChannel:用于UDP协议进行数据读取和写入。
  • SocketChannel:用于TCP协议中的数据读取和写入。
  • ServerSocketChannel:可以监听新进来的TCP连接,对每个新进来的连接都会创建一个 SocketChannel。
// 创建Channel
try (SocketChannel channel = SocketChannel.open()) {
    channel.configureBlocking(false); // 将Channel设置为非阻塞模式
    channel.connect(new InetSocketAddress("example.com", 80));

    // 检查是否连接成功
    if (channel.finishConnect()) {
        // 连接成功,可以进行读写操作
    }
} catch (IOException e) {
    // 处理异常
}

// 使用Channel读取数据
try (RandomAccessFile file = new RandomAccessFile("filePath", "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) {
    // 处理异常
}

// 使用Channel写入数据
try (RandomAccessFile file = new RandomAccessFile("filePath", "rw")) {
    FileChannel channel = file.getChannel();

    ByteBuffer buffer = ByteBuffer.allocate(1024);
    buffer.put("Hello, Java NIO".getBytes());
    buffer.flip();

    channel.write(buffer);
} catch (IOException e) {
    // 处理异常
}

Buffer

Buffer 是一个对象,包含一些要写入或者要读出的数据。在 NIO 中,所有数据都是通过 Buffer 对象来处理的。它是一个线性的、有限的数据区域,可以用来存储数据。Buffer 类型包括 ByteBuffer、CharBuffer、DoubleBuffer等等。

  • ByteBuffer:用于读写字节数据。
  • CharBuffer:用于读写字符数据,底层实现是使用的是 char 数组,但是在底层,仍然是通过 ByteBuffer 实现的。
  • DoubleBuffer:用于读写 double 类型数据。
// 创建Buffer
CharBuffer buffer = CharBuffer.allocate(1024);

// 写入数据到Buffer
buffer.put("Hello, Java NIO");

// 切换Buffer为读取模式
buffer.flip();

// 从Buffer中读取数据
while (buffer.hasRemaining()) {
    System.out.print(buffer.get());
}

Selector

Selector 是 NIO 的核心组件之一,用于检测多个 Channel 的事件状态。一个单独的线程可以检查多个 Channel 的状态,从而处理多个 Channel 的网络请求。 Java NIO 的 Selector 实现类似于 UNIX/Linux的 epoll 机制。

// 创建Selector
Selector selector = Selector.open();

// 注册Channel到Selector,并监听感兴趣的事件
channel.register(selector, SelectionKey.OP_READ);

while (true) {
    // 阻塞等待就绪的Channel
    int readyChannels = selector.select();

    if (readyChannels == 0) {
        continue;
    }

    // 获取就绪的Channel的Key,进行读写操作
    Set<SelectionKey> selectedKeys = selector.selectedKeys();
    for (SelectionKey key: selectedKeys) {
        if (key.isReadable()) {
            // 可读事件
        } else if (key.isWritable()) {
            // 可写事件
        }
    }
    // 处理完后,记得清空Key集合
    selectedKeys.clear();
}

什么是序列化?

在Java编程中,序列化(serialization)是指将对象转换为字节流的过程,可以将对象在网络中传输或者保存到磁盘中,使得对象的状态能够被保存和恢复。序列化之后的字节流可以在网络上传输,也可以保存到本地的文件系统中。

为什么需要序列化?

Java序列化技术的出现主要是为了解决对象的存储、传输和分布式计算的问题。通过序列化,可以将对象以字节流的形式进行传输,实现对象在网络中的传输。另外,当涉及到分布式系统、持久化存储等场景时,序列化同样具有重要作用。

如何进行序列化?

在Java中,要使一个对象能够被序列化,需要满足两个条件:

  1. 类必须实现 java.io.Serializable 接口;
  2. 所属类的所有属性必须是可序列化的(基本类型和包装类、String等都可以序列化)。

接下来我们通过一个例子来演示序列化的过程:

import java.io.*;

public class SerializationExample implements Serializable {
   private String name;
   private int age;

   public void setName(String name) {
      this.name = name;
   }

   public void setAge(int age) {
      this.age = age;
   }

   public String getName() {
      return name;
   }

   public int getAge() {
      return age;
   }

   public static void main(String[] args) {
      SerializationExample obj = new SerializationExample();
      obj.setName("John");
      obj.setAge(25);

      try {
         FileOutputStream fileOut = new FileOutputStream("object.ser");
         ObjectOutputStream out = new ObjectOutputStream(fileOut);
         out.writeObject(obj);
         out.close();
         fileOut.close();
         System.out.println("对象被序列化并保存到 object.ser 文件");
      } catch (IOException i) {
         i.printStackTrace();
      }
   }
}

在这个例子中,我们创建了一个 SerializationExample 类,它实现了 Serializable 接口。我们创建了一个对象 obj ,设置了其属性值,并且将其序列化并保存到 object.ser 文件中。

什么是反序列化?

反序列化(deserialization)是指将字节流转换为对象的过程,通过反序列化,可以将对象从字节流中重新恢复出来。这样就可以在网络中传输对象,或者从磁盘中读取对象并进行恢复。

如何进行反序列化?

要进行反序列化,需要使用 ObjectInputStream 类,通过调用 ObjectInputStreamreadObject() 方法实现反序列化。

以下是一个反序列化的例子:

import java.io.*;

public class DeserializationExample {
   public static void main(String[] args) {
      SerializationExample obj = null;

      try {
         FileInputStream fileIn = new FileInputStream("object.ser");
         ObjectInputStream in = new ObjectInputStream(fileIn);
         obj = (SerializationExample) in.readObject();
         in.close();
         fileIn.close();
      } catch (IOException i) {
         i.printStackTrace();
         return;
      } catch (ClassNotFoundException c) {
         System.out.println("class not found");
         c.printStackTrace();
         return;
      }

      System.out.println("对象被反序列化:");
      System.out.println("Name: " + obj.getName());
      System.out.println("Age: " + obj.getAge());
   }
}

在上面的例子中,我们使用 FileInputStream 来读取 object.ser 文件的字节流,并使用 ObjectInputStreamreadObject() 方法进行反序列化。最终我们将恢复出的对象的属性值进行打印。

需要注意的是,在进行反序列化时,类的定义必须是可用的,否则会抛出 ClassNotFoundException 异常。

一些额外注意事项

在进行序列化和反序列化时,有一些需要额外注意的事项:

  • 静态变量不会被序列化,因为它们属于类而不是对象;
  • transient 修饰的变量不会被序列化,因此在反序列化时会被赋予默认值;
  • 序列化和反序列化的类必须有相同的 serialVersionUID ,否则会导致反序列化失败。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Input/output (I/O) is not a sexy subject, but it’s an important part of non-trivial applications. This book introduces you to most of Java’s I/O capabilities as of Java 8 update 51. Chapter 1 presents a broad overview of I/O in terms of Java’s classic I/O, New I/O (NIO), and NIO.2 categories. You learn what each category offers in terms of its capabilities, and you also learn about concepts such as paths and Direct Memory Access. Chapters 2 through 5 cover classic I/O APIs. You learn about the File and RandomAccessFile classes along with streams (including object serialization and externalization) and writers/readers. Chapters 6 through 11 focus on NIO. You explore buffers, channels, selectors, regular expressions, charsets, and formatters. (Formatters were not introduced with the other NIO types in Java 1.4 because they depend on the variable arguments capability that was introduced in Java 5.) NIO is missing several features, which were subsequently provided by NIO.2. Chapters 12 through 14 cover NIO.2’s improved file system interface, asynchronous I/O, and the completion of socket channel functionality. Each chapter ends with assorted exercises that are designed to help you master its content. Along with long answers and true/false questions, you are often confronted with programming exercises. Appendix A provides the answers and solutions. Appendix B provides a tutorial on sockets and network interfaces. Although not directly related to classic I/O, NIO, and NIO.2, they leverage I/O capabilities and are mentioned elsewhere in this book.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值