IO流——名字带“流”就感觉好高级的亚子,学完之后才发现,所谓的“流”,就是一根管道接到数据源上,让数据像水一样在管道中流淌。
一、流的分类:
Java中IO流的类非常多,但各自之间联系紧密,如果按照输入输出流划分,这些类都是由下面4个抽象基类派生出来的:
InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流
OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
针对字节流与字符流我们可以把它想象成管道中流淌的两种数据类型,前者是以字节为单位,后者则是字符为单位。
如图是IO流体系中的一些常用操作:
其中一些是处理流,所谓处理流就是套在其他管道上的管道,用于修饰其他流。在下面的程序演示中会体现出来。
二、常用IO流使用
1.文件字节/字符流
文件字节流FileInputStream/FileOutputStream,用于操控文件,因此指定文件就是数据源。
操作如下:
public class testFileStream {
public static void main(String[] args) {
//定义两根接入文件的字节输入/出流
FileOutputStream fos=null;
FileInputStream fis=null;
String s="yangyang";
try {
//接入文件
fos=new FileOutputStream("D:\\study\\test_Java\\testFileStream.txt");
fis=new FileInputStream("D:\\study\\test_Java\\testFileStream.txt");
//将数组以字节形式流入文件
fos.write(s.getBytes());
//读取一个字节
s+=(char)fis.read();
System.out.println(s);
fos.close();
fis.close();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
//操作结果是FileStream.txt文件写入了字符串"yangyang",输出语句为"yangyangy".
文件字符流,上述的字节流是以字节为单位,但如果数据的最小单位不是一个字节,就会出现乱码的现象,因此我们就需要处理字符流。
操作与文件字节流类似:
public class testFileR_W {
public static void main(String[] args) {
// 创建
FileReader fr = null;
FileWriter fw = null;
int len = 0;
try {
//接入文件
fr = new FileReader("d:/a.txt");
fw = new FileWriter("d:/d.txt");
//缓冲用的字符数组
char[] buffer = new char[1024];
//边读边写
while ((len = fr.read(buffer)) != -1) {
fw.write(buffer, 0, len);
}
fr.close();
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//操作结果是a.txt下的"qwert杨杨杨"复制到了d.txt文件中
2、缓冲流
有时候如果我们只是用上方的文件流,每次访问单位是字节/字符,会对磁盘进行频繁的访问,这时候我们就需要一个用于缓冲数据的流——缓冲流,其作用就是把流入流出的数据暂时放入缓冲区,再处理。
BufferedInputStream与BufferedOutputStream完成高效率复制:
public class testBufferedStream01 {
public static void main(String[] args) {
try {
FileOutputStream fos=new FileOutputStream("D:\\study\\test_Java\\testBuffered01.txt");
FileInputStream fis=new FileInputStream("D:\\study\\test_Java\\testBuffered.txt");
//bis缓冲
BufferedInputStream bis=new BufferedInputStream(fis);
BufferedOutputStream bos=new BufferedOutputStream(fos);
int c=0;
//标记读取位置
bis.mark(3);
for(int i=0; i<=10 && (c=bis.read())!=-1; i++) {
bos.write((char)c);
}
System.out.println();
//mark对应恢复
bis.reset();
bis.close();
bos.close();
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
BufferedWriter与BufferedReader则是用来对字符进行缓冲的管道:与字节缓冲流操作类似
public class testBufferedstream02 {
public static void main(String[] args) {
try {
//两根字符缓冲流接入c
BufferedWriter bw=new BufferedWriter(new FileWriter("D:\\c.txt"));
BufferedReader br=new BufferedReader(new FileReader("D:\\c.txt"));
String s=null;
//数字一次写入
for(int i=1; i<100; i++) {
s=String.valueOf(i);
bw.write(s);
bw.newLine();
}
//缓冲区数据全部倒出
bw.flush();
while((s=br.readLine())!=null) {
System.out.println(s);
}
bw.close();
br.close();
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
3、转换流
转换流有两种,InputStreamReader/OutputStreamWriter用来实现将字节流转化成字符流,这里不做多说了,用它实现模拟键盘输入。
public class testTransForm02 {
public static void main(String[] args) {
//读取
InputStreamReader isr=
new InputStreamReader(System.in);
//读取
BufferedReader br=new BufferedReader(isr);
String s=null;
try {
//写入文件
OutputStreamWriter osw =new OutputStreamWriter(
new FileOutputStream("D:\\study\\test_Java\\testOuputStream.txt"));
osw.write("qwertyuiopasdfghjklzxcvbnm");
osw.close();
//读取一行
s=br.readLine();
while(s!=null) {
if(s.equalsIgnoreCase("exit"))
break;
//转大写
System.out.println(s.toUpperCase());
s=br.readLine();
}
br.close();
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
运行结果是在执行窗口接受并打印键盘的输入,文件”testOuputStream.txt“写入了字符串“qwertyuiopasdfghjklzxcvbnm”。
4、对象流
ObjectOutputStream/ObjectInputStream所谓对象流,可以将一个对象写入文件中,但这里不能硬核写入,需要对对象进行处理成序列后,再写入文件。而读取时则需要将序列转成对象。
程序演示
public class testObjectIO {
public static void main(String[] args) throws Exception {
T t=new T();
t.k=8;
FileOutputStream fos = new FileOutputStream("D:\\study\\test_Java\\testObjectIO.txt");
ObjectOutputStream oos =new ObjectOutputStream(fos);
//序列全流进去
oos.writeObject(t);
//序列全倒出来
oos.flush();
//关闭
oos.close();
FileInputStream fis =new FileInputStream("D:\\study\\test_Java\\testObjectIO.txt");
ObjectInputStream ois =new ObjectInputStream(fis);
//序列以对象形式流出来
T tr=(T)ois.readObject();
System.out.println(tr.i+" "+tr.j+" "+tr.d+" "+tr.k);
ois.close();
}
}
//标记型接口--内部没有方法--标记对象可以序列化
//如果不使用无法对象序列化
class T implements Serializable{
int i=10;
int j=9;
double d=2.3;
transient int k=15; //带transient关键字,对象流处理时会自动忽略
}
在程序中,T类接入了接口Serializable,查询API会发现,这是一个空接口,其目的是起到标记作用,标识这个对象可以进行序列化。
关键字transient修饰后,在序列化处理时会自动忽略。
运行结果:
三、其他流
字节数组流:
ByteArrayInputStream和ByteArrayOutputStream经常用在需要流和数组之间转化的情况,这里在使用时需要一个创建一个数组,随后这个数组可最为数据源用来数据流入流出。
byte[] b = "abcdefg".getBytes();
//此构造方法需要一个数组作为参数,这个字节数组就是数据源
ByteArrayInputStream bais = new ByteArrayInputStream(b);
例如上方代码段构造之后,就可以对数据源进行读取操作
数据流
数据流将“基本数据类型与字符串类型”作为数据源,允许程序以与机器无关的方式从底层输入输出流中操作Java基本数据类型与字符串类型。什么意思呢?一条代码段可以很清楚的展现
DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream("D:/data.txt")));
DataInputStream dis = new DataInputStream(
new BufferedInputStream(new FileInputStream("D:/data.txt")));
//写入数据
dos.writeDouble(Math.random());
dos.writeBoolean(true);
dos.writeUTF("杨杨杨");
//读数据
System.out.println("double: " + dis.readDouble());
System.out.println("boolean: " + dis.readBoolean());
System.out.println("String: " + dis.readUTF());
到此为止:
就把IO中常用的一些流探讨完毕,还有一个打印流PrintStream/PrintWriter,我感觉这个其实也没啥说的,就不再记录了。。。。
这里是我只是初步学习IO流,因此只是粗略记录了基本操作。若发现文章有哪里不妥或出现错误,还请阁下指正。