资讯专栏INFORMATION COLUMN

Java IO (三) 读取其他进程数据,RandomAccessFile,序列化和反序列化

zhangfaliang / 909人阅读

摘要:虚拟机读取其他进程的数据对象的方法可以运行平台上的其他程序该方法产生一个对象对象代表由该程序启动启动的子进程类提供如下三个方法用于和其子进程通信获取子进程的错误流获取子进程的输入流获取子进程的输出流这里的输入流输出流容易混淆从程序的角度思考

Java虚拟机读取其他进程的数据

Runtime对象的exec方法可以运行平台上的其他程序,该方法产生一个Process对象,Process对象代表由该Java程序启动启动的子进程,Process类提供如下三个方法,用于和其子进程通信:

InputStream getErrorStream() 获取子进程的错误流
InputStream getInputStream() 获取子进程的输入流
OutputStream getOutputStream() 获取子进程的输出流

  

这里的输入流输出流容易混淆.从程序的角度思考,子进程读取程序的数据,为输出流.子进程读取数据,是让程序把数据输出到子进程中.

java//省略代码

            Process p = Runtime.getRuntime().exec("javac");
            BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream(),"GBK"));
            String buff;
            while ((buff = br.readLine()) != null ) {
                System.out.println(buff);
            }

//省略代码

上面程序使用Runtime启动javac,获取了运行该程序相应的子进程.

javapublic class WriteToProcess {
    public static void main(String[] args) {
        PrintStream ps = null;
        try {
            //返回运行该命令的子进程
            Process p = Runtime.getRuntime().exec("java ReadStand");
            //p进程的输出流创建PrintStream对象
            //对本程序是输出流,对p进程则是输入流
            ps = new PrintStream(p.getOutputStream());
            BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String content;
            while ((content = br.readLine()) != null) {
                System.out.println(content);
            }
            ps.println("普通字符串");
            ps.println(new WriteToProcess());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (null != ps) {
                ps.close();
            }
        }
    }
}
class ReadStand {
    public static void main(String[] args) {
        PrintStream ps = null;
        Scanner sc = new Scanner(System.in);
        try {
            ps = new PrintStream("src	estout.txt");
            sc.useDelimiter("
");
            while (sc.hasNext()) {
                ps.print("键盘输入的内容是:" + sc.next());
            }
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            sc.close();
            ps.close();
        }
    }
}

上面的程序,运行结束产生了一个out.txt文件,该文件由ReadStand产生,该文件的内容由WriteToProcess类写入到ReadStan进程里,并由ReadStand读取这些数据,并把数据保存在out.txt文件.

RandomAccessFile

RandomAccessFile是Java输入/输出流中功能最丰富的文件内容访问类,提供了众多方法来访问文件,既可以支持读取内容,也可以向文件输出数据.与普通的输入/输出流不同的是,RandomAccessFile支持"随机访问"的方式,程序可以直接跳转到文件的任意位置来读写数据.

  

计算机中"随机访问"是由Random Access两个单词单词翻译而来.Random不仅有随机的意思,还有任意的意思.如果
能这样理解Random,可以更好的理解Random Access:任意访问,也就是说可以自由访问任意存储点的存储器(与磁盘,
磁带等需要寻道,倒带才可访问存储点的存储器区分);而RandomAccessFile的含义是可以自由访问文件的任意地方(与
InputStream,Reader依次向后读取区分).

RandomAccessFile可以自由访问文件的任意位置,所以如果只希望访问文件的部分内容,RandomAccessFile是更好的选择.

RandomAccessFile允许自由定位文件记录指针,所以RandomAccessFile不用从开始地方输出,所以可以向已存在的文件后追加内容.

RandomAccessFile对象包含了一个记录指针,用以标识当前读写处的位置,当程序创建一个新的RandomAccessFile对象时,该对象的的文件记录指针位于文件头(0处),读写了n个字节后文件记录指针将会移动n个字节,除此之外,RandomAccessFile可以自由移动记录指针,可以向前/向后移动.包含两个方法来操作文件记录指针:

void getFilepointer() 返回文件记录指针的当前位置
void seek(long pos) 将文件记录指针定位到pos位置

RandomAccessFile既可以读文件又能写文件,包含了类似于InputStream的三个Read方法,用法和InputStream的三个read方法完全一样.也包含了OutputStream的三个write方法,用法和OutputStreamwrite方法完全一样.此外还包含系列的readXXXwriteXXX方法来完成输入/输出.

RandomAccessFile类有两个构造器,基本相同,区别在于指定的文件形式不同.一个使用String来制定文件名,一个使用File参数来制定文件本身,之外创建RandomAccessFile对象还需要制定一个mode参数,该参数指定RandomAccessFile的访问模式,有如下四个值:

"r" 以只读的方式打开指定文件.如试图对该RandomAccessFile指定写入操作会抛出IOException.

"rw" 以读取,写入的方式打开指定文件.如果该文件不存在,则尝试创建该文件.

"rws" 以读取,写入的方式打开指定文件.相对于"rw"模式,还要求对文件的元数据或内容的每个更新都同步到底层存储设备.

"rwd" 以读取,写入的方式打开指定文件,对于"rw",还要求对文件内容的每个更新都同步写入底层存储设备.

    //省略代码
    RandomAccessFile raf = null;
    //省略代码
    try {
        raf = new RandomAccessFile("out.txt","rw");
        raf.seek(raf.length());
        raf.write("追加内容
".getBytes());
    }

使用RandomAccessFile对指定文件指定位置插入内容:

javapublic static void insert(String fileName,long pos,String insertContent) {
        RandomAccessFile raf = null;
        try {
            raf = new RandomAccessFile(fileName, "rw");
            File tmpFile = File.createTempFile("temp", null);
            tmpFile.deleteOnExit();
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(tmpFile)));
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(tmpFile)));
            //设置文件指针位置
            raf.seek(pos);
            String content;
            /**
             * 将插入点内容保存在临时文件中
             * 读取插入点后所有文件内容
             */
            while ((content = raf.readLine()) != null) {
                    //将数据写入临时文件
                    bw.write(content);
            }
            bw.flush();
            bw.close();
            //重新设置指针位置
            raf.seek(pos);
            //插入内容
            raf.write(insertContent.getBytes());
            //追加临时文件中内容
            while ((content = br.readLine()) != null) {
                raf.write(content.getBytes());
            }
            br.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            try {
                raf.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }


    }
对象的序列化和反序列化

对象序列化是将对象保存在磁盘中,或允许在网络中直接传输对象,对象序列化机制允许把内存中的Java对象转换平台无关的二进制流,从而允许把这种二进制流持久保存在磁盘上,通过网络将这种二进制流传输到另一个网络节点.其他程序获得了这种二进制流,都可以将这种二进制流恢复成原来的Java对象.

序列化的含义和意义

序列化机制允许将实现序列化的Java对象转换为字节序列,这些字节序列可以被保存在磁盘上,或通过网络传输,以备以后重新恢复成原来的对象,序列化机制使得对象可以脱离程序的运行独立存在.

对象的序列化(Serialize)指将一个Java对象写入IO流中,于此对应的是,对象的反序列化(Deserialize)指从IO流中恢复该Java对象.

如果要让某个对象可以支持序列化机制,必须它的类是可序列化的(Serialize).为了让某个类可序列化,必须实现如下两个接口之一:

Serializable

Externalizable

Serializable是一个标记接口,该接口无需实现任何方法,只是表明该类是可序列化的.所有可能在网络上传输的对象的类都应该是可序列化的,否则程序将会出现异常.比如RMI(Remote Method Invoke,远程方法调用)过程中的参数和返回值;所有需要保存到磁盘的对象的类都必须是可序列化.如Web中需要保存到HttpSession和ServletContext属性的Java对象.
通常建议,程序创建的每个JavaBean都实现Serializable接口.

序列化对象实例:

javapublic class ObjectOutputStreamTest {
    public static void main(String[] args) {
        ObjectOutputStream oos = null;

        try {
            //创建一个ObjectOutputStream输出流
            oos = new ObjectOutputStream(new FileOutputStream("src//io//person.txt"));
            Person p1 = new Person("swk", 20);
            //把对象写入输出流
            oos.writeObject(p1);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally { 
                try {
                    if (null != oos) {
                        oos.close();
                    }
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

class Person implements Serializable {

    private static final long serialVersionUID = 1L;
    private String name;
    private int age;

    public Person(String name,int age) {
        // TODO Auto-generated constructor stub    
        System.out.println("Person 构造函数");
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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


}

从二进制流中恢复Java对象:

java//省略代码
    ObjectInputStream ois = null;
        try {
            //创建ObjectInputStream输入流
            ois = new ObjectInputStream(new FileInputStream("src//io//person.txt"));
            //从流中读取对象,并强制转换为Person类型
            Person p = (Person)ois.readObject();
            System.out.println("Age:" + p.getAge() + ",name:" + p.getName());
        } 
//省略代码
  

反序列化读取的只是Java对象的数据,而不是Java类,因此采用反序列化恢复Java对象时,必须提供Java对象所属的class文件,否则引发ClassNotFoundException.

  

Person类只有一个有参数构造器,并没有无参数构造器,而且构造函数内有打印语句.当反序列化读取Java对象时,没有看到程序调用该构造器,这表明反序列化机制无需通过构造器来初始化Java对象.

  

如果向文件中使用序列化多个Java对象,使用反序列化机制恢复时,必须按实际写入顺序读取.

程序创建子类实例时,系统会隐式创建为它所有的父类都创建实例.反序列化某个子类的实例时,反序列化机制需要恢复其关联的父类实例,恢复这些父类实例有;两种方式:

使用反序列化机制

使用父类无参数构造器

在上面两种方式中,反序列化机制优先采用第一种机制.如果某个父类既不可序列化,则不能使用第一种机制;又没有提供无参数构造器,则不可采用第二种机制,反序列化该子类实例将抛出异常.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/64414.html

相关文章

  • Java 输入/输出 I/O流 RandomAccessFile

    摘要:当使用节点流进行输入输出时,程序直接连接到实际的数据源,和时间的输入输出节点连接处理流则用于对一个已存在的流进行连接或封装,通过封装后的流来实现数据读写功能,处理流也被称为高级流。 文件的编码 文本文件就是字节序列,可以是任意编码形式。在中文操作系统上直接创建文本文件,则该文本文件只能识别ANSI编码,其他编码方式会产生乱码 package imooc.io; import java...

    Eirunye 评论0 收藏0
  • Java编程基础23——IO(其他流)&Properties

    摘要:但它融合了和的功能。支持对随机访问文件的读取和写入。的概述和作为集合的使用了解的概述类表示了一个持久的属性集。可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。 1_序列流(了解) 1.什么是序列流 序列流可以把多个字节输入流整合成一个, 从序列流中读取数据时, 将从被整合的第一个流开始读, 读完一个之后继续读第二个, 以此类推. 2.使用方式 整合两个: S...

    vvpale 评论0 收藏0
  • Java 列化和反列化

    摘要:把字节序列恢复为对象的过程称为对象的反序列化。代表对象输入流,它的方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。接口继承自接口,实现接口的类完全由自身来控制序列化的行为,而仅实现接口的类可以采用默认的序列化方式。 把对象转换为字节序列的过程称为对象的序列化。把字节序列恢复为对象的过程称为对象的反序列化。    对象的序列化主要有两种用途:   1) 把...

    jcc 评论0 收藏0
  • Java 列化与反列化

    摘要:一序列化和反序列化的概念把对象转换为字节序列的过程称为对象的序列化把字节序列恢复为对象的过程称为对象的反序列化。代表对象输入流,它的方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。 一、序列化和反序列化的概念 把对象转换为字节序列的过程称为对象的序列化;把字节序列恢复为对象的过程称为对象的反序列化。 对象的序列化主要有两种用途: 1) 把对象的字节序列永久地保...

    I_Am 评论0 收藏0
  • Flink 源码解析 —— 深度解析 Flink 是如何管理好内存的?

    摘要:减少垃圾收集压力因为所有长生命周期的数据都是在的管理内存中以二进制表示的,所以所有数据对象都是短暂的,甚至是可变的,并且可以重用。当然,并不是唯一一个基于且对二进制数据进行操作的数据处理系统。 showImg(https://segmentfault.com/img/remote/1460000020044119?w=1280&h=853); 前言 如今,许多用于分析大型数据集的开源系...

    Edison 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<