Java的I/O功能通过
java.io
包下的类和接口来支持,在java.io
包下主要包括输入/输出
两种IO流,每种输入/输出流
又可分为字节流
和字符流
两大类.字节流支持以字节(8位)为单位的IO操作,而字符流则以字符(16位-Java中)为单位进行IO操作.
除此之外,Java的IO流还使用装饰者模式,将IO流分成底层节点流
和上层处理流
,节点流直接和底层的物理存储节点关联,虽然不同的物理节点获取的节点流可能存在差异,但程序可以把不同的物理节点包装成统一的处理流,从而允许程序使用统一的IO代码来操作不同的物理节点.
File
Java使用
java.io.File
来提供对底层文件的抽象,File
能够新建/删除/重命名
文件和目录,但不能访问文件内容本身(需要使用IO流).
File
类提供了很多实用的方法,我们可以将其分为以下几类:
- 文件名相关方法
getAbsoluteFile()
getAbsolutePath()
getName()
getParent()
getParentFile()
getPath()
renameTo(File dest)
-
文件状态相关方法
exists()
canExecute()
canRead()
canWrite()
isFile()
isDirectory()
isAbsolute()
(UNIX/Linux中是否以/
开头)isHidden()
lastModified()
length()
-
文件操作
createNewFile()
createTempFile(String prefix, String suffix)
delete()
deleteOnExit()
setExecutable(boolean executable)
setReadOnly()
-
目录操作
如:mkdir()
mkdirs()
list()
list(FilenameFilter filter)
listFiles()
listFiles(FileFilter filter)
listRoots()
这儿只是大致介绍File
类提供的功能,细节请参考JDK文档.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
I/O流体系
InputStream / Reader
InputStream
与Reader
是所有输入流的抽象基类, 虽然本身不能创建实例来执行输入, 但作为所有输入流的模板, 他们的方法是所有输入流都通用的.
InputStream
包含如下三个方法:
int read()
int read(byte[] b)
int read(byte[] b, int off, int len)
Reader
包含如下三个方法:
int read()
int read(char[] cbuf)
int read(char[] cbuf, int off, int len)
对比InputStream
和Reader
所提供的方法, 这两个类的功能基本相同, 只是一个提供了字节byte
读, 一个提供了字符char
读.
除此之外, InputStream
和Reader
还支持几个方法来移动记录指针以实现跳读, 重复读等操作.
方法 | 释义 |
---|---|
void mark(int readlimit) | Marks the current position in this input stream. |
boolean markSupported() | Tests if this input stream supports the mark and reset methods. |
void reset() | Repositions this stream to the position at the time the mark method was last called on this input stream. |
long skip(long n) | Skips over and discards n bytes of data from this input stream. |
OutputStream / Writer
两个流都提供了如下三个方法:
void write(byte[]/char[] b)
void write(byte[]/char[] b, int off, int len)
void write(int b)
因为字符流直接以字符作为操作单位, 所以Writer
可以用字符串来代替字符数组(以String
对象作为参数). Writer
还包含如下方法:
void write(String str)
void write(String str, int off, int len)
Writer append(char c)
Writer append(CharSequence csq)
Writer append(CharSequence csq, int start, int end)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
小结:
- 使用Java IO流执行完IO后,不能忘记关闭流,关闭流才可以保证物理资源会被回收(关闭输出流还可以保证将缓冲区中的数据
flush
到物理节点中,因为在执行close()
方法之前,会自动执行输出流的flush()
方法). - Java 1.7改写了所有的IO资源类,他们都实现了
AutoCloseable
接口,因此都可通过自动关闭的try
语句来自动关闭这些流. - 通常来说,字节流的功能比字符流的功能强大,因为计算机里所有的数据都是二进制的,因此字节流可以处理所有所有的二进制文件.
- 如果使用字节流来处理文本文件,则需要将这些字节转换成字符,增加了编程的复杂度.所以有一个规则:如果进行IO是文本内容,则应该考虑使用字符流;如果进行IO的是二进制内容, 则应该考虑使用字节流.
节点流/处理流
节点流的的构造参数是物理IO节点,而处理流的构造参数是已经存在的流.
常用节点流:
\ | InputStream | OutputStream | Reader | Writer |
---|---|---|---|---|
文件 | FileInputStream | FileOutputStrean | FileReader | FileWriter |
数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
字符串 | * | * | StringReader | StringWriter |
管道 | PipedInputStream | PipedOutputStream | PipedReader | PipedWriter |
常用处理流:
\ | InputStream | OutputStream | Reader | Writer |
---|---|---|---|---|
缓冲流 | BufferedInputStrean | BufferedOutputStream | BufferedReader | BufferedWriter |
转换流 | InputStreamReader | OutputStreamWriter | * | * |
数据流 | DataInputStream | DataOutputStream | * | * |
对象流 | ObjectInputStream | ObjectOutStream | * | * |
合并流 | SequenceInputStream | * | * | * |
回退流 | PushbackInputStream | * | PushbackReader | * |
打印流 | PrintStream | * | PrintWriter | * |
- 处理流与节点流相比可以隐藏底层节点流的差异,对外提供更加统一的IO方法,让开发人员只需关心高级流的操作(使用更加简单, 执行效率更高).
- 使用处理流的思路:使用处理流来包装节点流,程序通过处理流执行IO,让节点流与底层IO设备/文件交互.
在使用处理流包装了节点流之后, 关闭输入/输出流资源时, 只要关闭最上层的处理流即可.关闭最上层的处理流时, 系统会自动关闭该处理流包装的节点流.
数组作为IO节点
上表中列出了一种以数组为物理节点的节点流,字节流以字节数组为节点ByteArrayInputStream
/ByteArrayOutputStream
,字符流以字符数组为节点CharArrayReader
/CharArrayWriter
, 这种数组流除了在创建节点流需要传入数组外,用法上与其他节点流完全类似.数组流类似还可以使用字符串作为物理节点,用于实现从字符串中读取字符串(String)java.io.StringReader
, 或将内容写入字符串(StringBuffer)java.io.StringWriter
.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
缓冲流
BufferedInputStream
BufferedOutputStream
BufferedReader
BufferedWriter
四个缓冲流增加了缓冲功能, 可以提高IO效率, 但是需要使用flush()
才可以将缓冲区的内容写入实际的物理节点.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
转换流
Java I/O流体系提供了两个转换流, 用于实现将字节流转换成字符流:
java.io.InputStreamReader
将字节输入流转换成字符输入流java.io.OutputStreamWriter
将字节输出流转换成字符输出流
回退流
在IO流体系中, 有两个特殊的流与众不同: PushbackInputStream
PushbackReader
他们都提供了如下三个方法:
方法 | 释义 |
---|---|
void unread(byte[]/char[] buf) | Pushes back an array of bytes/characters by copying it to the front of the pushback buffer. |
void unread(byte[]/char[] buf, int off, int len) | Pushes back a portion of an array of bytes/characters by copying it to the front of the pushback buffer. |
void unread(int b/c) | Pushes back a single byte/character by copying it to the front of the pushback buffer. |
这两个推回输入流都带有一个推回缓冲区, 当程序调用者两个的unread()
方法时,系统将会把指定的数据推回到该缓冲区中, 而这两个流在每次调用read()
方法时,都会先从推回缓冲区读取,只有完全读取了推回缓冲区的内容后, 才会到原输入流中获取.
这样, 当程序创建该流时, 需要指定推回缓冲区大小(默认为1),如果在程序中推回的数据超出了推回缓冲区的大小, 则会抛出java.io.IOException: Pushback buffer overflow
.
RandomAccessFile
java.io.RandomAccessFile
与普通的java I/O流不同的是, 他可以支持随机访问
文件, 程序可以直接跳转到文件的任意地方读写数据(因此如果只是访问文件的部分内容,或向已存在的文件追加数据, RandomAccessFile
是更好的选择).但RandomAccessFile
也有一个局限就是只能读写文件, 不能读写其他IO节点.
RandomAccessFile
对象包含了一个记录指针, 用以标识当前读写位置, 当程序新建一个RandomAccessFile
对象时, 记录指针位于文件头, 当读/写n个字节后, 指针将会向后移动n个字节.除此之外RandomAccessFile
还可以(前/后)自由移动该记录指针,RandomAccessFile
提供了如下方法来操作文件记录指针:
方法 | 释义 |
---|---|
long getFilePointer() | Returns the current offset in this file. |
void seek(long pos) | Sets the file-pointer offset, measured from the beginning of this file, at which the next read or write occurs. |
RandomAccessFile
既可以读文件, 也可以写文件, 所以他提供了完全类似于InputStream
的read()
方法, 也提供了完全类似于OutputStream
的write()
方法, 此外他还包含了一系列的readXxx()
writeXxx()
来完成IO(其实这几个操作是DataInput
DataOutput
接口来提供的,DataInputStream
/DataOutputStream
ObjectInputStream
/ObjectOutputStream
也实现了这两个接口).
在构造一个RandomAccessFile
时, 都需要提供一个String mode
参数, 用于指定RandomAccessFile
的访问模式, 该参数有如下取值:
mode | 模式 |
---|---|
"r" | 只读 |
"rw" | 读写, 如果文件不存在, 则创建 |
"rws" | 读写,相对于”rw”,还要求对文件内容 或文件元数据 的更新都同步写入底层存储数据 |
"rwd" | 读写,相对于”rw”,只 要求对文件内容 的更新同步写入底层存储数据 |
注意:
RandomAccessFile
不能直接向文件的指定位置插入数据,不然新插入的数据会覆盖文件的原内容.如果需要向指定的位置插入数据,程序需要先把指定插入点后的内容读入缓冲区, 等把需要插入的数据写入完成后, 再将缓冲区的内容追加到文件后面.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
对象序列化
序列化机制使得对象可以脱离程序的运行环境而独立存在: 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流, 从而可以把这种二进制流持久的保存在磁盘上, 或通过网络将这种二进制流传输到另一个网络节点. 其他程序一旦获取到了这个二进制流, 都可以将他恢复成原先的Java对象.
如果需要让某个对象支持序列化机制, 则必须让他的类是可序列化的(serializable). 该类必须实现如下接口之一:
java.io.Serializable
java.io.Externalizable
注意: 对象的类名, 实例变量(基本类型/数组/引用对象)都会被序列化; 而方法/类变量(static)/transient实例变量都不会序列化.
Serializable
使用Serialiable
实现对象序列化只需实现这个接口即可(而这个接口只是一个Tag
接口).
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
注意:
- 反序列化读取的仅仅是Java
对象
的数据, 而不是Java类, 因此采用反序列化恢复Java对象时,必须提供对象所属的类的class文件, 否则将会引发ClassNotFoundException
. - 从上例看到:
Bean
类并没有提供无参构造器, 因此可以证明反序列化机制无须通过构造器来初始化对象
. - 如果使用序列化机制向文件中写入了多个Java对象, 反序列化时必须按实际写入的顺序读取.
根据经验: 像
Date
BigInteger
这样的值类应该实现Serializable
接口,大多数的集合也应该如此. 但代表活动实体的类, 如线程池(Thread Pool), 一般不应该实现Serializable
.
对象引用序列化
- 如果某个类的成员变量不是基本类型或String类型,而是另一个引用类型, 那么这个引用类必须是可序列化的, 否则拥有该类型成员变量的类也是不可序列化的.
- Java序列化算法
- 所有保存到磁盘(或传输到网络中)的对象都有一个序列化编号.
- 当程序试图序列化一个对象时, 程序将先检查该对象是否已经被序列化过, 只有该对象(在本次虚拟机的上下文
Context
中)从未被序列化过, 系统才会将该对象转换成字节序列并输出. - 如果某个对象已经序列化过(即使该对象的实例变量后来发生了改变), 程序将不再重新序列化该对象.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
序列化版本
Java序列化机制允许为序列化的类提供一个private static final long serialVersionUID = xxxL;
值, 该值用于标识该Java类的序列化版本; 一个类升级之后, 只要他的serialVersionUID
值不变, 序列化机制也会把它们当成同一个序列化版本(由于提供了serialVersionUID
之后JVM不需要再次计算该值,因此还有个小小的性能好处).
可以通过JDK提供的serialver工具来提取类的serialVersionUID
值.
- 1
- 2
- 1
- 2
如果不显式定义serialVersionUID
的值,可能会造成以下问题:
- 该值将由JVM根据类的相关信息计算,而修改后的类的计算结果与修改前的类的计算结果往往不同,从而造成对象的反序列化因为类的版本不兼容而失败.
- 不利于程序在不同JVM之间移植, 因为不同的编译器对该变量的计算策略可能不同, 而从造成类虽然没有改变, 但因为JVM的不同, 也会出现序列化版本不兼容而导致无法正确反序列化的现象.
自定义序列化
在一些的场景下, 如果一个类里面包含的某些变量不希望对其序列化(或某个实例变量是不可序列化的,因此不希望对该实例变量进行递归序列化
,以避免引发java.io.NotSerializableException
异常); 或者将来这个类的实现可能改动(最大程度的保持版本兼容), 或者我们需要自定义序列化规则, 在这种情景下我们可选择实用自定义序列化.
transient
通过在实例变量前面加transient
关键字以指定Java序列化时无需理会该实例变量(注意: transient
关键字只能用于修饰实例变量, 不可修饰Java其他成分).
被
transient
修饰的实例变量被称为瞬时变量.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
readObject/writeObject
使用transient
关键字修饰实例变量虽然简单, 但被transient
修饰的实例变量将完全被隔离在序列化机制之外, 这样导致反序列化恢复Java对象时无法取得该实例变量的值. 因此, Java还提供了另一种自定义序列化机制,通过提供writeObject()
readObject()
readObjectNoData()
等方法可以让程序控制如何序列化各实例变量, 甚至完全不序列化某些实例变量.
-
private void writeObject(java.io.ObjectOutputStream output) throws IOException;
负责序列化类实例,以便readObject()
可以恢复它;通过重写该方法, 可以实现完全控制该类的序列化机制,自主决定哪些实例变量需要实例化,需要怎样序列化. 在默认情况下, 该方法会调用output.defaultWriteObject()
来保存Java对象的各实例变量, 从而可以实现序列化对象的目的. -
private void readObject(java.io.ObjectInputStream input) throws IOException, ClassNotFoundException;
负责反序列化类实例,通过重写该方法,可以完全控制该类的反序列化机制,自主决定需要反序列化哪些实例变量,以及如何进行反序列化.在默认情况下, 该方法会调用input.defaultReadObject()
来恢复Java对象的非瞬时变量.
一般readObject()
应与writeObject()
方法对应,如果writeObject()
对Java对象的实例变量进行了一些处理, 则应该在readObject()
方法中对其实例变量进行相应的反处理,以便正确恢复该对象.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
由于我们可以自己定制序列化规则, 因此, 在网络传输中, 可以对对象实例进行加密, 在读出时自动解密, 这样即使在传输过程被截获, 获取到的也是加密后的值.但writeObject()
方法的加密规则必须与readObject()
的解密规则一致.
建议
readObject()
writeObject()
的方法内首先调用defaultReadObject()
defaultWriteObject()
;
-
private void readObjectNoData() throws ObjectStreamException;
当序列化流不完整时,readObjectNoData()
方法可以用来正确地反序列化对象.例如, 接收方使用的反序列化类的版本不同于发送方,或接收方版本扩展的类不是发送方版本扩展的类时,系统都会调用readObjectNoData()
方法来初始化反序列化的对象. -
private Object writeReplace() throws ObjectStreamException;
Java序列化机制保证在序列化某个对象之前, 先调用对象的writeReplace()
方法, 如果该方法返回另一个Java对象, 则系统转化为序列化另一个对象.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
与writeReplace()
对应, 序列化还有一个readResolve()
方法
private Object readResolve() throws ObjectStreamException;
可以实现保护性复制整个对象, 这个方法会紧接着readObject()
之后调用, 该方法的返回值将会替换原来反序列化的对象, 而原来readObject()
反序列化的对象将会被立即丢弃.
readResolve()
方法在序列化单例类, 枚举类时尤其有用(细节请参考static, enum, 内部类与单例模式), 因此所有的单例类, 枚举类在实现序列化时都应该提供readResolve()
方法.
readResolve()
与writeReplace()
还可以使用其他的访问修饰符, 但建议使用private
修饰.
Externalizable
实现Externalizable
接口以实现对象序列化, 这种序列化方式完全由程序员决定存储和恢复对象数据的机制.该接口提供了如下两个方法:
public void writeExternal(ObjectOutput out) throws IOException;
序列化public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
反序列化
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
实现Externalizable
接口序列化可以带来一定的性能提升,而且还可以完全自己的自定义序列化规则, 因此只有当默认的序列化形式(Serializable
)能够合理地描述对象的逻辑状态时,才能实用默认的序列化形式(详见Effective Java第11/12章).
Java虚拟机读写其他进程数据
使用Runtime
对象的exec()
方法可以运行操作系统平台上的其他程序, 该方法返回一个Process
对象, 代表由该Java程序启动的子进程.Process
提供如下方法, 用于主进程和子进程进行通信:
方法 | 释义 |
---|---|
InputStream getErrorStream() | Returns the input stream connected to the error output of the subprocess. |
InputStream getInputStream() | Returns the input stream connected to the normal output of the subprocess. |
OutputStream getOutputStream() | Returns the output stream connected to the normal input of the subprocess. |
注意: Input/Output是站在主进程角度来看的.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
注意: 上面程序使用到了Guava的CharStreams
, 其详细用法请参考我的下一篇博客Java I/O 扩展
.暂时可在pom中添加如下依赖: