流(Stream)的概念源自UNIX中管道的概念,管道是一条不间断的字节流,用来实现程序或进程之间的通信。一个流必有源端和目的端(可以是内存、磁盘文件等。)流的源端和目的端可以简单的看成字节的生产者和消费者。
根据 读写位置 流分为:
根据 流的方向 分为:
根据 操作对象的类型是字符还是字节 分为两类:
在讲解流之前,先讲解一下之后和流操作紧密相关的文件操作类 File类
File类是一个与文件本身操作相关的类——文件的创建、删除、重命名、取得文件大小和修改日期等。
No. | 方法 | 类型 | 描述 |
---|---|---|---|
1 | public File(String pathName) | 构造 | 给定一个要操作文件的完整路径 |
2 | public boolean exists() | 普通 | 判定给定路径是否存在 |
3 | public File getParentFile() | 普通 | 找到一个指定路径的父路径 |
4 | public boolean mkdirs() | 普通 | 创建指定目录 |
5 | public boolean createNewFile() | 普通 | 创建文件 |
6 | public boolean delete() | 普通 | 删除文件 |
7 | public boolean isDirectory() | 普通 | 判断给定路径是否是文件夹 |
8 | public boolean isFile() | 普通 | 判断给定路径是否是文件 |
9 | public boolean isHidden() | 普通 | 判断是否隐藏 |
10 | public boolean renameTo(File dest) | 普通 | 为文件重命名 |
11 | public long lastModified() | 普通 | 文件的最后一次修改时间 |
12 | public long length() | 普通 | 取得文件的大小 |
13 | public String getName() | 普通 | 取得文件名称 |
14 | public [] File listFiles() | 普通 | 将目录中的文件以File对象数组的方式返回 |
import java.util.Date;
import java.text.SimpleDateFormat;
import java.io.File;
public class TestDemo{
public static void main(String [] args)throws Exception{
File file = new File("/home/linyimin/DaSi/java/demo.txt");
// 如果文件存在,获取并输出文件信息
if(file.exists()){
getInfo(file);
}
// 如果不存在,先判断文件的父路径是否存在,不存在先创建目录在创建文件,否则直接创建文件
else if(file.getParentFile().exists()){
// 创建文件
file.createNewFile();
// 获取文件信息
getInfo(file);
}
else{
// 创建目录
file.getParentFile().mkdirs();
// 创建文件
file.createNewFile();
// 获取文件信息
getInfo(file);
}
// 为文件重命名,并取得重命名后的文件信息
File f = new File("/home/linyimin/DaSi/java/TestDemo.txt");
file.renameTo(f);
File newFile = new File("/home/linyimin/DaSi/java/TestDemo.txt");
System.out.println("崇重命名之后的文件信息:");
getInfo(newFile);
// 删除文件
newFile.delete();
}
/*
* 获取文件的各种信息
* @param file 目标操作文件
**/
public static void getInfo(File file){
// 判断指定路径是否是目录
System.out.println(file.getName() + (file.isDirectory()?"是一个目录":"不是一个目录"));
// 判断是否是文件
System.out.println(file.getName() + (file.isFile()?"是一个文件":"不是一个文件"));
// 判断是否为隐藏文件
System.out.println(file.getName() + (file.isHidden()?"是隐藏文件":"不是隐藏文件"));
// 获取文件的最后修改时间
long temp = file.lastModified();
// 将long型数据转换称Date型数据
Date date = new Date(temp);
// 将Date型数据转换称时间字符串
String str = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(date);
System.out.println(str);
// 输出文件大小
System.out.println(file.getName() + ":" + file.length() + "B");
}
}
所有的 字节输入流都直接或间接继承自InputStream抽象类 ,输入流作为数据的来源,我们可以通过输入流的read方法读取字节数据;
所有的 输出流都直接或间接继承自OutputStream抽象类 ,输出流接收数据,可以通过write方法写入字节数据。
对于InputStream、OutputStream类而言,本身定义的是一个抽象类(abstract class),按照抽象类的使用原则来讲,需要定义抽象类的子类,根据功能的不同,使用不同的子类完成。 按照面向对象的开发原则,子类要为抽象类进行对象的实例化,而后调用的方法以父类中定义的方法为主,方法的具体实现由实例化这个父类的子类完成的。所以对于子类用户最关心的是构造方法 下面主要讲解几种常用的IO操作和其对应定义的子类。
文件操作流的常用方法
类名称 | No. | 方法名称 | 类型 | 描述 |
---|---|---|---|---|
FileInputStream | 1 | public FileInputStream(File file) | 构造 | 实例化FileInputStream,用于从指定文件中读取数据 |
FileInputStream | 2 | public FileInputStream(Stirng name) | 构造 | 实例化FileInputStream,用于从指定文件路径中读取数据 |
InputStream | 3 | public void close() | 普通 | 关闭输入流 |
InputStream | 4 | public int read() | 普通 | 从指定输入流中读取一个字节数据 |
InputStream | 5 | public int read(byte [] data) | 普通 | 从指定流中读取多个字节 |
InputStream | 6 | public int read(byte [] data, int off, int len) | 普通 | 从指定流中读取指定多个字节 |
FileOutputStream | 7 | public FileOutputStream(File file) | 构造方法 | 实例化FileOuputStream,用于新建数据 |
FileOutputStream | 8 | public FileOutputStream(File file,boolean append) | 构造方法 | 实例化FileOutputStream,用于追加数据 |
OutputStream | 9 | public void close() | 普通 | 关闭输出流 |
OutputStream | 10 | public abstract void write(int b) | 普通 | 向文件输出单个字节 |
OutputStream | 11 | public void write(byte [] b) | 普通 | 向文件输出一组字节数据 |
OutputStream | 12 | public void write(byte [] b, int off, int len) | 普通 | 向文件输出部分字节数据 |
import java.io.File;
import java.io.OutputStream;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class TestDemo{
public static void main(String [] args)throws Exception{
// 指定操作文件
File fileOut = new File("/home/linyimin/DaSi/java/OutputStream.txt");
File fileIn = new File("/home/linyimin/DaSi/java/InputStream.txt");
// 实例化FileOutputStream,向文件添加数据
OutputStream out = new FileOutputStream(fileOut);
// 实例化FileInputStream,从文件中读取数据
InputStream in = new FileInputStream(fileIn);
String str = "Hello World.";
// 将字符串转换称字节数组
byte [] data = str.getBytes();
// 向文件中写如数据
out.write(data);
// 从文件中读取数据并输出
int len = 0;
byte [] temp = new byte[1024];
while(len != -1){
len = in.read(temp);
if(len != -1){
System.out.println(new String(temp, 0, len));
}
}
// 关闭输出流
out.close();
// 关闭输入流
in.close();
}
}
在以上的例子中,通过FileInputStream的read()方法从/home/linyimin/DaSi/java/InputStream.txt文件中读取信息,通过FileOutputStream的write()方法向/home/linyimin/DaSi/java/InputStream.txt文件写入“Hello World.”。在此操作中如果InputStream.txt已经存在,会先清空内容在写入信息,如果不存在,会自动创建InputStream.txt文件后在写入数据。如果希望在已存在的文件之后添加数据,应使用public FileOutputStream(fileOut,true)构造方法进行实例化对象,表示向已有文件中追加写入数据而不是覆盖已有数据。
在某个操作需要发生IO操作,但又不希望有一些临时文件产生,可以使用内存操作流完成,即以内存为操作的终端,以发生IO操作关系。用于以IO流的方式来完成对字节数组内容的读写,来支持类似内存虚拟文件或者内存映射文件的读写。
内存操作类的常用方法
类名称 | No. | 方法 | 类型 | 描述 |
---|---|---|---|---|
ByteArrayInputStream | 1 | public ByteArrayInputStream(byte [] buf) | 构造 | 将字节流转换称输入流 |
ByteArrayInputStream | 2 | public int read() | 普通 | 从输入流中读取一个字节数据 |
ByteArrayInputStream | 3 | public int read(byte [] data) | 普通 | 从指定流中读取多个字节 |
ByteArrayInputStream | 4 | public int read(byte [] data, int off, int len) | 普通 | 从指定流中读取指定多个字节 |
ByteArrayOutputStream | 5 | public ByteArrayOutputStream() | 构造 | 创建一个32个字节 |
ByteArrayOutputStream | 6 | public ByteArrayOutputStream(int size) | 构造 | 根据参数指定大小创建缓冲区 |
ByteArrayOutputStream | 7 | public void write(int b) | 普通 | 往输出流中写入一个字节数据 |
ByteArrayOutputStream | 8 | public void write(byte [] b) | 普通 | 往输出流中写入一个字节数组数据 |
ByteArrayOutputStream | 9 | public void write(byte [] b, int off, int len) | 普通 | 往输出流中写入指定多个字节数据 |
ByteArrayOutputStream | 10 | public String toString() | 普通 | 使用默认编码将缓冲区数据编码成字符串并返回 |
ByteArrayOutputStream | 11 | public byte [] toByteArray() | 普通 | 将缓冲区数据通过字节数组返回 |
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
public class TestDemo{
public static void main(String [] args)throws Exception{
String str = "abcdefghijklmnopqrstuvwsyz";
// 将字符串转换成字节数组
byte [] data = str.getBytes();
// 将字节数组转换成输入流,即将数据输出到内存
InputStream in = new ByteArrayInputStream(data);
// 字节输出流的实例化,即从内存读取数据
OutputStream out = new ByteArrayOutputStream();
// 从ByteArrayInputStream中读取多个字节数据
byte [] b = new byte[1024];
int len = in.read(b);
System.out.println(new String(b, 0, len));
// 重置标志位置,从第一个字节开始读取字节数据
in.reset();
// 从ByteArrayInputStream中一个字节接一个字节读取数据
int temp = in.read();
while(temp != -1){
// 往输出流写入从输入流中读取的数据,并把数据变为大写
out.write(Character.toUpperCase(temp));
System.out.print((char)temp + " ");
temp = in.read();
}
System.out.println();
// 从输出流中获取数据,并转换成字符数据
System.out.println(out.toString());
}
}
程序运行结果:
abcdefghijklmnopqrstuvwsyz
a b c d e f g h i j k l m n o p q r s t u v w s y z
ABCDEFGHIJKLMNOPQRSTUVWSYZ
在上面的例子中,我们通过字符串获取字节数组将其作为ByteArrayInputStream的数据流来源,然后通过读取ByteArrayInputStream的数据,将读到的数据写入到ByteArrayOutputStream中。
System.in是System类中的一个InputStream类型的常量,用于系统输入。系统输入针对标准的输入设备——键盘,也就是俗称的键盘输入数据,由于System.in是InputStream类型数据,所以接收的数据是字节型。通过调用read()函数完成键盘输入数据操作。
import java.io.IOException;
import java.io.InputStream;
public class TestDemo{
public static void main(String [] args) throws IOException{
InputStream in = System.in;
byte [] data = new byte[1024];
// 从键盘输入多个数据,以回车键结束输入
System.out.println("输入多个字节数据:");
int len = in.read(data);
System.out.println(new String(data, 0, len));
System.out.println("一个字节接一个字节输入数据:");
int temp = in.read();
while(temp != -1){
// 键入回车键,则退出输出
if(temp == '\n'){
break;
}
// 将输入的字母变为大写字母
System.out.print((char)Character.toUpperCase(temp) + " ");
// 从键盘输入一个字节数据
temp = in.read();
}
}
}
程序运行结果:
输入多个字节数据:
Hello 中国
Hello 中国
一个字节接一个字节输入数据:
Hello 中国
H E L L O Ä ¸ Å › ½
在上面的例子中,可以发现从键盘输入数据时,既可以多个字节数据同时输入,也可以一个接一个字节输入,但应注意的是, 在单个字节输入时,中文输出会发生乱码 ,一般在进行中文操作时,应使用字符流完成。
BufferedInputStream是缓冲输入流,继承于FilterInputStream。作用是为其它输入流提供缓冲功能。BufferedInputStream会将输入流数据分批读取,每次读取一部分数据到缓冲中,操作完缓冲中的数据之后,在从输入流中读取下一部分的数据。即BufferedInputStream内部有一个字节数组缓冲区,每次执行read操作的时候就从这buf中读取数据,从buf中读取数据没有多大的开销。如果buf中已经没有了要读取的数据,那么就去执行其内部绑定的InputStream的read方法,而且是一次性读取很大一块数据,以便填充满buf缓冲区。于我们在执行BufferedInputStream的read操作的时候,很多时候都是从缓冲区中读取的数据,这样就大大减少了实际执行其指定的InputStream的read操作的次数,也就提高了读取的效率。
BufferedOutputStream是输出缓冲流,与BufferedInputStream相对,继承于FilterOutputStream。作用是为输出流提供缓冲功能。BufferedOutputStream内部有一个字节缓冲区buf,在执行write操作时,将要写入的数据先一起缓存在一起,将其存入字节缓冲区buf中,当buf被填充完毕的时候会调用BufferedOutputStream的flushBuffer方法,该方法会通过调用其绑定的OutputStream的write方法将buf中的数据进行实际的写入操作并将buf的指向归零(可以看做是将buf中的数据清空)。如果想让缓存区buf中的数据理解真的被写入OutputStream中,可以调用flush方法,flush方法内部会调用flushBuffer方法。由于buf的存在,会大大减少实际执行OutputStream的write操作的次数,优化了写的效率。
缓冲操作的常用方法
类名称 | No. | 方法 | 类型 | 描述 |
---|---|---|---|---|
BufferedInputStream | 1 | public BufferedInputStream(InputStream in) | 构造 | 实例化BufferedInputStream,使用默认缓冲区大小 |
BufferedInputStream | 2 | public BufferedInputStream(InputStream in, int size) | 构造 | 实例化BufferedInputStream,指定缓冲区大小 |
BufferedInputStream | 3 | public int read() | 普通 | 从输入流中读取一个字节数据 |
BufferedInputStream | 4 | public int read(byte [] data) | 普通 | 从指定流中读取多个字节 |
BufferedInputStream | 5 | public int read(byte [] data, int off, int len) | 普通 | 从指定流中读取指定多个字节 |
BufferedOutputStream | 6 | public BufferedOutputStream(OutputStream out) | 构造 | 实例化BufferedOutputStream,使用默认缓冲区大小 |
BufferedOutputStream | 7 | public BufferedOutputStream(OutputStream out, int size) | 构造 | 实例化BufferedOutputStream,指定缓冲区大小 |
BufferedOutputStream | 8 | public void write(int b) | 普通 | 向输出缓冲区写入一个字节数据 |
BufferedOutputStream | 9 | public void write(byte [] data) | 普通 | 向输出缓冲区写入多个字节数据 |
BufferedOutputStream | 10 | public void write(byte [] data, int off, int len) | 普通 | 向输出缓冲取写入指定多个字节 |
BufferedOutputStream | 11 | public void flush() | 普通 | 清空缓冲区,缓存区buf中的数据理解真的被写入OutputStream中 |
代码示例
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class TestDemo{
public static void main(String [] args) throws IOException{
File fio = new File(File.separator + "home" + File.separator + "linyimin" + File.separator + "DaSi" +
File.separator + "java" + File.separator + "InputStream.txt");
File fout = new File(File.separator + "home" + File.separator + "linyimin" + File.separator + "DaSi" +
File.separator + "java" + File.separator + "OutputStream.txt");
// InputStream.txt不存在则创建文件
if(!fio.exists()){
fio.createNewFile();
}
// OutputStream.txt不存在则创建文件
if(!fout.exists()){
fout.createNewFile();
}
// 实例化BufferedInputStream
BufferedInputStream ibuf = new BufferedInputStream(new FileInputStream(fio));
// 实例化BufferedOutputStream
BufferedOutputStream obuf = new BufferedOutputStream(new FileOutputStream(fout));
byte [] data = new byte[1024];
// 从BufferedInputStream中多个字节数据
int len = ibuf.read(data);
while(len != -1){
// 向BufferedOutputStream中写入多个数据
obuf.write(data, 0, len);
// 继续从BufferedInputStream中读取多个字节数据
len = ibuf.read(data);
}
// 清空输出缓存区
obuf.flush();
// 关闭输出输入流
ibuf.close();
obuf.close();
}
}
程序运行结果:
程序运行前:OutputStream.txt文件不存在,InputStream.txt文件的内容为:
Hello World.
1234567890
程序运行后:OutputStream.txt文件的内容为:
Hello World.
1234567890
在上面的例子中,实现了将InputStream.txt拷贝到OutputStream.txt。其实不通过BufferedInputStream 和 BufferedOutputStream也可以完成这样的工作,使用这个两个类的好处是提升了文件拷贝的效率。下面进行验证:
代码
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
public class TestDemo{
public static void main(String [] args) throws IOException{
File fio = new File("/home/linyimin/Downloads/linux_11gR2_database_2of2.zip");
File fout = new File("/home/linyimin/Downloads/linux_11gR2_database_2of2-copy.zip");
InputStream in = null;
OutputStream out = null;
// 创建文件
try{
fout.createNewFile();
in = new FileInputStream(fio);
out = new FileOutputStream(fout);
}
catch(Exception e){
e.printStackTrace();
}
// 获取复制文件大小,并转换成单位M,保留两位小数
double length = new BigDecimal(fio.length() / 1024.0 / 1024).divide(new BigDecimal(1), 2, BigDecimal.ROUND_HALF_UP).doubleValue();
// 文件复制开始时间
long start = System.currentTimeMillis();
// 从输入流中读取多个字节数据
byte [] data = new byte[1024];
int len = in.read(data);
while(len != -1){
// 将从输入流中读出的数据写入输出流中
out.write(data);
// 继续从输入流中读取数据
len = in.read(data);
}
// 文件复制结束时间
long end = System.currentTimeMillis();
// 将文件复制时间转换成秒并保留三位小数
double time = new BigDecimal((end - start) / 1000.0).divide(new BigDecimal(1), 3, BigDecimal.ROUND_HALF_UP).doubleValue();
System.out.println("复制大小为:" + length + "M的文件共花费时间:" + time + "s");
}
}
程序运行结果:
复制大小为:949.25M的文件共花费时间:49.048s
代码
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
public class TestDemo{
public static void main(String [] args) throws IOException{
File fio = new File("/home/linyimin/Downloads/linux_11gR2_database_2of2.zip");
File fout = new File("/home/linyimin/Downloads/linux_11gR2_database_2of2-copy.zip");
InputStream in = null;
OutputStream out = null;
// 创建文件
try{
fout.createNewFile();
in = new BufferedInputStream(new FileInputStream(fio));
out = new BufferedOutputStream(new FileOutputStream(fout));
}
catch(Exception e){
e.printStackTrace();
}
// 获取复制文件大小,并转换成单位M,保留两位小数
double length = new BigDecimal(fio.length() / 1024.0 / 1024).divide(new BigDecimal(1), 2, BigDecimal.ROUND_HALF_UP).doubleValue();
// 文件复制开始时间
long start = System.currentTimeMillis();
// 从输入流中读取多个字节数据
byte [] data = new byte[1024];
int len = in.read(data);
while(len != -1){
// 将从输入流中读出的数据写入输出流中
out.write(data);
// 继续从输入流中读取数据
len = in.read(data);
}
// 文件复制结束时间
long end = System.currentTimeMillis();
// 将文件复制时间转换成秒并保留三位小数
double time = new BigDecimal((end - start) / 1000.0).divide(new BigDecimal(1), 3, BigDecimal.ROUND_HALF_UP).doubleValue();
System.out.println("复制大小为:" + length + "M的文件共花费时间:" + time + "s");
}
}
程序运行结果:
复制大小为:949.25M的文件共花费时间:32.93s
比较以上两个程序的执行结果,使用缓冲区操作的效率确实要提高不少。
管道流操作主要用于两个线程之间进行管道通信。PipedInputStream和PipedOutputStream一般是结合使用的,一般在一个线程中执行PipedOutputStream 的write操作,而在另一个线程中执行PipedInputStream的read操作。单独使用PipedInputStream或单独使用PipedOutputStream时没有任何意义的,必须将二者通过connect方法(或在构造函数中传入对应的流)进行连接绑定,如果单独使用其中的某一个类,就会触发IOException: PipeNotConnected.
代码示例
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
public class TestDemo{
public static void main(String [] args)throws Exception{
// 实例化PipedOutputStream
final PipedOutputStream out = new PipedOutputStream();
final PipedInputStream in = new PipedInputStream(out);
// 使用匿名内部类实现线程t1
Thread t1 = new Thread(new Runnable(){
@Override
public void run(){
try {
out.write("Hello Pipe.".getBytes());
}
catch (IOException e) {
e.printStackTrace();
}
}
});
// 使用匿名内部类实现线程t2
Thread t2 = new Thread(new Runnable(){
@Override
public void run(){
int len = 0;
try {
// 从线程t1中的输出流中读取数据
while((len = in.read()) != -1){
System.out.print((char)len + " ");
}
}
catch (IOException e) {
}
System.out.println();
}
});
// 启动线程
t1.start();
t2.start();
}
}
程序运行结果
H e l l o P i p e .
在上面的程序中,我们创建了两个线程,并通过构造方法将PipedInputStream流和PipedOutputStream绑定。线程t1在运行时往输出流中写入字节数据,而线程t2在运行时阻塞式的执行read操作,等待获取数据,并输出。
注: 要实现序列化,对象所在类必须实现java.io.Serializable接口.Serializable接口没有方法,属于一种标识接口,表示一种一种能力.
如果一个类已经实现了序列化接口,那么此类的对象就可以经过二进制数据流进行传输,但是还需要对象输出流ObjectOutputStream和对象输入流ObjectInputStream完成对象的输入和输出.
ObjectOutputStream和ObjectInputStream常用方法
类名称 | No. | 方法 | 类型 | 描述 |
---|---|---|---|---|
ObjectOutputStream | 1 | public ObjectOutputStream(OutputStream out) | 构造 | 实例化指定输出流的对象 |
ObjectOutputStream | 2 | public void writeObject(Object obj) | 普通 | 向指定输流写入数据 |
ObjectInputStream | 3 | public ObjectInputStream(InputStream in) | 构造 | 实例化指定输入流的对象 |
ObjectInputStream | 4 | public void readObject(Object obj) | 普通 | 从指定输入流读出数据 |
代码示例-序列化
import java.io.File;
import java.io.Serializable;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
// 定义可以被序列化的类
class Person implements Serializable{
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
}
public class TestDemo{
public static void main(String [] args) throws FileNotFoundException, IOException{
// 实例化序列化对象
Person per = new Person("张三", 20);
File file = new File("/home/linyimin/DaSi/java/serializable.txt");
// 实例化ObjectOutputStream对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
// 序列化对象
oos.writeObject(per);
// 关闭输出流
oos.close();
}
}
程序运行结果:
文件serializable.txtx中的内容:
\AC\ED\00sr\00Person:H3\BC\B0\FC\00I\00ageL\00namet\00Ljava/lang/String;xp\00\00\00t\00张三
本程序使用ObjectOutputStream将一个已经实例化的对象序列化到文件中.
代码示例-反序列化
import java.io.File;
import java.io.Serializable;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
//定义可以被序列化的类
class Person implements Serializable{
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
@Override
public String toString(){
return "姓名:" + this.name + " 年龄:" + this.age;
}
}
public class TestDemo{
public static void main(String [] args) throws FileNotFoundException, IOException, ClassNotFoundException{
// 实例化ObjectInputStream对象
File fio = new File("/home/linyimin/DaSi/java/serializable.txt");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fio));
// 实现反序列化
Person per = (Person) ois.readObject();
ois.close();
System.out.println(per);
}
}
程序运行结果:
姓名:张三 年龄:20
本程序通过ObjectInputStream类将之前已经被序列化的数据反序列化为对象.
字符流和字节流十分相似,主要区别在于:
下面主要介绍字符流较于字节流的区别之处:
使用Writer类进行输出的最大方便之处在于:可以直接输出字符串数据,而不像OutputStream类一样需要调用getBytes()函数将字符串转换成字节数组.
代码示例
import java.io.File;
import java.io.Writer;
import java.io.FileWriter;
public class TestDemo{
public static void main(String [] args)throws Exception{
File file = new File("/home/linyimin/DaSi/java/out.txt");
// 如果文件不不存在,创建文件
if(!file.exists()){
file.createNewFile();
}
// 实例化FileWriter对象
Writer out = new FileWriter(file);
String str = "Hello World.";
// 直接向文件中输出字符串
out.write(str);
// 需要关闭输出流,数据才会从缓存区写到文件中
out.close();
}
}
程序运行结果
创建out.txt文件,并写入”Hello World."
本程序通过FileWriter完成直接将字符串写入指定文件中.
所谓转换流指的是字节流向字符流转换的操作流.由InputStreamReader和OutputStreamWriter两个类完成.
代码示例
import java.io.File;
import java.io.Writer;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class TestDemo{
public static void main(String [] args) throws IOException{
File file = new File("/home/linyimin/DaSi/java/out.txt");
// 实例化字节输出流
OutputStream out = new FileOutputStream(file);
// 将字节输出流转换成字符输出流
Writer wout = new OutputStreamWriter(out);
String str = "使用OutputStreamWriter完成输出流转换操作";
// 直接向文件输出字符串
try {
wout.write(str);
} catch (IOException e) {
e.printStackTrace();
}
// 关闭输出流,完成IO操作
wout.close();
}
}
程序运行结果
向文件out.txt中输出字符串"使用OutputStreamWriter完成输出流转换操作"
注: 处理各种数据都可以通过字节流完成,而在处理中文时使用字符流会更好