Java学习day14

扩展

read()的重载方法

read() 方法将读取的字节存储在 bytes 数组中,而 write() 方法将整个 bytes 数组写入输出流中。这可能会导致最后一次读取的字节被重复写入输出流,因为 read() 方法在读取到文件末尾时返回 -1,但在这种情况下,bytes 数组中的数据仍然保持不变。为了解决这个问题,可以使用 read() 方法的另一个重载形式,指定读取的起始偏移量和读取的最大字节数。

示例

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class FileCopyExample {
    public static void main(String[] args) {
        String sourceFile = "input.txt";
        String destinationFile = "output.txt";

        try (InputStream in = new FileInputStream(sourceFile);
             OutputStream out = new FileOutputStream(destinationFile)) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
            System.out.println("文件复制成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

这样,只有实际读取到的字节数才会被写入输出流中,避免了重复写入最后一次读取的数据。

字符集设置

可以通过使用 Reader 对象和指定字符集来设置 Properties 类的 load() 方法的字符集。

以下是一个示例代码,展示了如何在加载属性配置文件时设置字符集:

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Properties;

public class PropertiesExample {
    public static void main(String[] args) {
        Properties properties = new Properties();

        try (InputStream input = new FileInputStream("config.properties");
             InputStreamReader reader = new InputStreamReader(input, StandardCharsets.UTF_8)) {
            properties.load(reader);

            // 读取属性值
            String username = properties.getProperty("username");
            String password = properties.getProperty("password");

            System.out.println("Username: " + username);
            System.out.println("Password: " + password);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在上述示例中,我们使用 InputStreamReader 将输入流转换为字符流,并指定字符集为 UTF-8。然后,将字符流作为参数传递给 load() 方法。

通过这种方式,load() 方法会使用指定的字符集解析输入流中的内容,并正确地处理属性配置文件中的 Unicode 字符和特殊字符。

在示例中,我们假设属性配置文件 “config.properties” 使用 UTF-8 编码。

I/O流常见类的名称和作用

字节流 - 输入:

  1. InputStream:抽象类,从字节源读取数据。
  2. FileInputStream:从文件中读取字节数据的输入流。
  3. ByteArrayInputStream:从字节数组中读取字节数据的输入流。
  4. PipedInputStream:用于线程间通信的管道输入流。
  5. ObjectInputStream:读取Java对象的输入流。
  6. BufferedInputStream:提供缓冲功能的字节输入流。
  7. SequenceInputStream:按顺序连接多个输入流。
  8. FilterInputStream过滤器输入流的基类。
  9. AudioInputStream:用于音频数据的输入流。
  10. DataInputStream:以特定格式从输入流读取基本数据类型的输入流。

字节流 - 输出:

  1. OutputStream:抽象类,向字节目标写入数据。
  2. FileOutputStream:向文件中写入字节数据的输出流。
  3. ByteArrayOutputStream:向字节数组中写入字节数据的输出流。
  4. PipedOutputStream:用于线程间通信的管道输出流。
  5. ObjectOutputStream:写入Java对象的输出流。
  6. BufferedOutputStream:提供缓冲功能的字节输出流。
  7. FilterOutputStream:过滤器输出流的基类。
  8. AudioOutputStream:用于音频数据的输出流。
  9. DataOutputStream:以特定格式向输出流写入基本数据类型的输出流。
  10. PrintStream:向输出流打印格式化表示形式的文本。

字符流 - 输入:

  1. Reader:抽象类,从字符源读取数据。
  2. FileReader:从文件中读取字符数据的输入流。
  3. InputStreamReader:将字节流转换为字符流的输入流。
  4. BufferedReader:提供缓冲功能的字符输入流。
  5. StringReader:从字符串中读取字符数据的输入流。
  6. PipedReader:用于线程间通信的管道输入流。
  7. CharArrayReader:从字符数组中读取字符数据的输入流。
  8. FilterReader:过滤器字符输入流的基类。
  9. LineNumberReader:读取文本数据,并保持跟踪行号的输入流。
  10. PushbackReader:允许字符读取器将字符推回到流中的输入流。

字符流 - 输出:

  1. Writer:抽象类,向字符目标写入数据。
  2. FileWriter:向文件中写入字符数据的输出流。
  3. OutputStreamWriter:将字节流转换为字符流的输出流。
  4. BufferedWriter:提供缓冲功能的字符输出流。
  5. StringWriter:将字符数据写入字符串的输出流。
  6. PipedWriter:用于线程间通信的管道输出流。
  7. CharArrayWriter:向字符数组中写入字符数据的输出流。
  8. FilterWriter:过滤器字符输出流的基类。
  9. PrintWriter:向输出流写入格式化表示形式的文本。
  10. PushbackWriter:允许字符写入器将字符推回到流中的输出流。

序列化

引言

  • 什么是对象序列化?
    对象序列化是将对象转换为字节序列的过程,以便可以在网络上进行传输或在存储介质上进行持久化存储。序列化后的字节序列可以被传输、存储或在需要时重新创建为原始对象。
  • 序列化的用途和好处
    对象序列化具有广泛的应用,其中一些包括:数据持久化、网络通信、分布式系统、缓存和进程间通信。通过序列化,我们可以方便地在不同的系统之间传递对象,并实现对象的持久化存储。

Java对象序列化基础

  • Serializable接口
    Java中的对象序列化是基于Serializable接口实现的。实现Serializable接口的类可以被序列化和反序列化。Serializable接口是一个标记接口,不包含任何方法。
  • transient关键字
    在某些情况下,我们可能不希望将对象的某些字段序列化,例如敏感信息或临时计算结果。通过使用transient关键字,我们可以将字段标记为瞬态,这样它们就不会被序列化。

实现对象序列化

  • 实现Serializable接口
    要使一个类可序列化,只需实现Serializable接口。这可以通过简单地在类声明中添加"implements Serializable"来实现。
  • 自定义序列化和反序列化方法
    Java提供了默认的序列化和反序列化机制,但我们也可以通过自定义writeObject()和readObject()方法来控制序列化和反序列化的过程。这样我们可以在序列化和反序列化时进行自定义的处理。
  • 版本控制和兼容性
    在进行对象序列化时,我们需要考虑版本控制和兼容性问题。通过显式声明serialVersionUID,并在类发生变化时进行更新,可以确保序列化和反序列化的兼容性。

对象的序列化和反序列化

  • ObjectOutputStream和ObjectInputStream
    Java提供了ObjectOutputStream和ObjectInputStream类来进行对象的序列化和反序列化。我们可以使用这些类的方法将对象写入输出流或从输入流中读取对象。
  • 序列化异常处理
    在进行对象的序列化和反序列化时,可能会出现一些异常情况,如ClassNotFoundException和InvalidClassException。我们可以使用try-catch块来捕获并处理这些异常。

序列化的注意事项和最佳实践

  • 序列化版本UID
    在实现序列化的类中,建议显式声明一个serialVersionUID字段,以确保序列化版本的一致性。这样可以避免在反序列化时出现版本不匹配的问题。
  • 避免序列化敏感信息
    在进行对象序列化时,应注意避免将敏感信息(如密码、密钥等)序列化。敏感信息的序列化可能会导致安全风险。
  • 序列化与继承关系
    在涉及继承关系的类的序列化中,需要注意父类和子类之间的序列化关系。可以通过在父类中实现Serializable接口和使用默认序列化机制来简化继承类的序列化过程。

序列化的应用场景

  • 数据持久化
    对象序列化可以用于将对象存储到文件或数据库中,实现数据的持久化。这使得对象可以在应用程序重启后被重新加载和使用。
  • 网络通信
    序列化可以用于在网络上传输对象。通过将对象序列化为字节流,可以在不同的计算机之间传递对象,实现远程方法调用或构建分布式系统。
  • 分布式系统
    对象序列化在分布式系统中发挥着重要的作用。通过将对象序列化和反序列化,可以在不同的计算节点之间传递和共享数据。

总结

本文介绍了Java中对象序列化的概念、原理和应用。我们了解了如何实现对象的序列化和反序列化,并讨论了一些注意事项和最佳实践。对象序列化是在Java中进行数据持久化和网络通信的重要技术,对于构建可扩展和可靠的应用程序非常有价值。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值