Java IO5:管道流、对象流

前言

前面的文章主要讲了文件字符输入流FileWriter、文件字符输出流FileReader、文件字节输出流FileOutputStream、文件字节输入流FileInputStream,这些都是常见的流类。当然除了这些流类之外,Java还提供了很多的流类给用户使用,本文就看一下别的流。

管道流

管道流主要用于连接两个线程的通信。管道流也分为字节流(PipedInputStream、PipedOutputStream)和字符流(PipedReader、PipedWriter)。比如一个PipedInputStream必须和一个PipedOutputStream对象进行连接而产生一个通信管道,PipedOutputStream向管道中写入数据,PipedInputStream从管道中读取数据。管道流的工作如下图所示:

下面看一下管道流的用法。既然管道流的作用是用于线程间的通信,那么势必有发送线程和接收线程,两个线程通过管道流交互数据。首先写一个发送数据的线程:

public class Sender implements Runnable
{
    private PipedOutputStream out = new PipedOutputStream();

    public PipedOutputStream getOutputStream()
    {
        return out;
    }

    public void run()
    {
        String str = "Receiver, 你好!";
        try
        {
            out.write(str.getBytes()); // 向管道流中写入数据(发送)
            out.close();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}

用流写数据的时候注意关注一下,该流是否支持直接写String,不可以的话要用String的getBytes()方法获取字符串的字节。既然有一个发送数据的线程了,接下来来一个接收数据的线程:

public class Receiver implements Runnable
{
    private PipedInputStream in = new PipedInputStream();

    public PipedInputStream getInputStream()
    {
        return in;
    }

    public void run()
    {
        String s = null;
        byte b0[] = new byte[1024];
        try
        {
            int length = in.read(b0);
            if (-1 != length)
            {
                s = new String(b0, 0 , length);
                System.out.println("收到了以下信息:" + s);
            }
            in.close();
        } catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}

两个线程都有了,写一个main线程,利用管道输出流的connect方法连接管道输出流和管道输入流:

public static void main(String[] args)
{
    try
    {
        Sender sender = new Sender();
        Receiver receiver = new Receiver();
        Thread senderThread = new Thread(sender);
        Thread receiverThread = new Thread(receiver);
        PipedOutputStream out = sender.getOutputStream(); // 写入
        PipedInputStream in = receiver.getInputStream(); // 读出
        out.connect(in);// 将输出发送到输入
        senderThread.start();
        receiverThread.start();
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }
}

输出结果应该很明显了,大家都知道,接收线程接收到了来自发送线程通过管道流输出流发送的数据:

收到了以下信息:Receiver, 你好!

注意一下,PipedInputStream运用的是一个1024字节固定大小的循环缓冲区,写入PipedOutputStream的数据实际上保存到了对应的PipedInputStream的内部缓冲区。PipedInputStream执行读操作时,读取的数据实际上来自这个内部缓冲区。如果对应的PipedInputStream输入缓冲区已满,任何企图写入PipedOutputStream的线程都将被阻塞。而且这个写操作线程将一直阻塞,直至出现读取PipedInputStream的操作从缓冲区删除数据。

这意味着,向PipedOutputStream写入数据的线程不应该是负责从对应PipedInputStream读取数据的唯一线程(所以这里开了两个线程分别用于读写)。假定t线程试图一次对PipedOutputStream的write()方法的调用中向对应的PipedOutputStream写入2000字节的数据,在t线程阻塞之前,它最多能够写入1024字节的数据(PipedInputStream内部缓冲区的大小)。然而,一旦t被阻塞,读取PipedInputStream的操作就再也不能出现了,因为t是唯一读取PipedInputStream的线程,这样,t线程已经完全被阻塞。

对象流

序列化,在这篇文章中已经讲得比较清楚了,这一部分主要是再次简单过一下对象流的知识而已。

Java中提供了ObjectInputStream、ObjectOutputStream这两个类用于对象序列化操作,这两个类是用于存储和读取对象的输入输出流类,只要把对象中的所有成员变量都存储起来,就等于保存了这个对象,之后从保存的对象之中再将对象读取进来就可以继续使用此对象。ObjectInputStream、ObjectOutputStream可以帮助开发者完成保存和读取对象成员变量取值的过程,但要求读写或存储的对象必须实现了Serializable接口。

看一下例子,先来一个实现了Serializable接口的实体类Person:

public class Person implements Serializable
{
    /**
     * 序列化
     */
    private static final long serialVersionUID = 7827863437931135333L;

    private transient String     name;
    private int                    age;
    private final static String sex = "man";

    public Person(String name, int age)
    {
        this.name = name;
        this.age = age;
    }

    public String toString()
    {
        return "姓名:" + this.name + ", 年龄:" + this.age + ", 性别:" + sex;
    }
}

调用ObjectOutputStream和ObjectInputStream写一个序列化和反序列化的方法,我现在D盘下没有"serializable.txt":

public static void main(String[] args) throws Exception
{
    File file = new File("D:/serializable.txt");
    serializable(file);
    deserializable(file);
}

// 序列化对象方法
public static void serializable(File file) throws Exception
{
    OutputStream outputFile = new FileOutputStream(file);
    ObjectOutputStream oos = new ObjectOutputStream(outputFile);
    oos.writeObject(new Person("张三", 25));
    oos.close();
}

// 反序列化对象方法
public static void deserializable(File file) throws Exception
{
    InputStream inputFile = new FileInputStream(file);
    ObjectInputStream ois = new ObjectInputStream(inputFile);
    Person p = (Person)ois.readObject();
    System.out.println(p);
}

现在运行一下,D盘下多了一个"serializable.txt",文件里面的内容是:

看到乱码,因为序列化之后本身就是按照一定的二进制格式组织的文件,这些二进制格式不能被文本文件所识别,所以乱码也是正常的。

当然,控制台上也是有输出的:

姓名:null, 年龄:25, 性别:man

这证明了被transient修饰的成员变量不会被序列化。

时间: 2024-10-10 00:16:38

Java IO5:管道流、对象流的相关文章

JAVA笔记12__字节、字符缓冲流/打印流/对象流/

/** * !!:以后写流的时候一定要加入缓冲!! * 对文件或其它目标频繁的读写操作,效率低,性能差. * 缓冲流:好处是能更高效地读写信息,原理是将数据先缓冲起来,然后一起写入或读取出来. * * BufferedInputStream:字节缓冲流(有一个内部缓冲区数组,用于缓冲数据) */ public class Main { public static void main(String[] args) { input(); output(); } /** * 使用字节缓冲流进行读取操作

java学习之IO对象流

//注意对象类要打标记实现Serializable接口 1 package com.gh; 2 3 import java.io.FileInputStream; 4 import java.io.FileNotFoundException; 5 import java.io.FileOutputStream; 6 import java.io.IOException; 7 import java.io.InputStream; 8 import java.io.ObjectInputStrea

Java IO-5 序列化与反序列化流

建一个Person类 1 package demo05; 2 3 import java.io.Serializable; 4 5 public class Person implements Serializable { 6 private String name; 7 private int age; 8 //private transient int age; 阻止成员变量序列化 9 10 public Person(String name, int age) { 11 super();

Java序列化与对象流浅谈

今晚又重新回顾了Java中序列化与对象流的知识,接触了一位有着上亿行有效代码量的大佬之思想,2333. 1.序列化与反序列化 Java中的序列化简而言之就是为了避免要重复使用的实例在每次执行程序过程中都要重新申请堆空间,序列化后直接加载,节约时间.序列化过程中,保存在文件中的对象只记录了对象的状态(属性),包括成员和类类型(名称),而不会存储对象的方法.可看做将一个有着灵魂的人进行灵魂转移,转移的只是他的记忆,他自己的各种行为,比如吃饭.睡觉--,每个人都拥有,且行为表现一样,即只需转移(保存)

java高级特性之IO流

缓冲流 转换流 对象流 打印流 标准输入输出流 随机访问流 数组流 有关flush():所有的处理流的输出流,最外层流需要刷新. javaIO流 1认识File类 File类的对象表示一个文件或者一个文件目录 绝对路径:包含盘符的文件完整路径 相对路径:在当前路径下的文件路径 File类中的方法,涉及到文件或文件目录的新建.删除.获取文件的路径.获取文件的大小.并没有涉及到向文件中写入或读出内容.这样的读取或写入的功能就需要IO流来完成 一般通过将File类的对象作为参数传递到流的构造器中,作为

Java IO7:管道流、对象流

前言 前面的文章主要讲了文件字符输入流FileWriter.文件字符输出流FileReader.文件字节输出流FileOutputStream.文件字节输入流FileInputStream,这些都是常见的流类.当然除了这些流类之外,Java还提供了很多的流类给用户使用,本文就看一下别的流. 管道流 管道流主要用于连接两个线程的通信.管道流也分为字节流(PipedInputStream.PipedOutputStream)和字符流(PipedReader.PipedWriter).比如一个Pipe

黑马程序员——Java基础——IO流(三)—对象的序列化(持久化),管道流,操作基本数据类型的流对象

第一讲 对象序列化(持久化) 概述: 将堆内存中的对象存入硬盘,以包括对象的数据,称为持久化或序列化 使用的类:ObjectInputStream和ObjectOutputStream 特有方法: ObjectInputStream Object readObject():读取 ObjectOutputStream void writeObject(obj):写入 1 /* 2 * 需求:将一个对象序列化,并从硬盘中读取序列化的对象 3 * 4 */ 5 import java.io.*; 6

黑马程序员——Java基础——IO流(三)—序列流、管道流、RandomAccessFile类、操作基本数据类型的流对象、操作数组和字符串、字符编码

第一讲 对象序列化(持久化) 一.概述:就是把对象封存在硬盘,可以保持数据:关键类:ObjectInputStream和ObjectOutpurStream 二. 关键字:ObjectOutputStream:方法有writerObject()读取 ObjectInputStream 方法有readObject() 被序列化的对象需要 implements Serializable关于被序列化的类需要实现Serializable它等于一个撮,标识用的,改变类里面的语句就变了.如果想固定一个撮,可

黑马程序员——java高新技术——IO其他流对象

点击打开链接 点击打开链接 点击打开链接 android培训.<a">点击打开链接 点击打开链接 java培训.期待与您交流!">点击打开链接 点击打开链接 IO其他对象 PrintStream:字节打印流.为其他输出流添加了功能,提供了打印方法,可以将各种数据类型的数据原样打印. 构造函数可以接受的参数类型:file对象,字符串路径,字节输出流. 方法:println():打印各种基本数据类型. PrintWrite:字符打印流.构造函数可以接受的参数类型:file