Java把不同类型的输入、输出,这些输入输出有些是在屏幕上、有些是在电脑文件上, 都抽象为流(Stream)
按流的方向,分为输入流与输出流,注意这里的输出输出是相对于程序而言的,如:如对于一个Java程序创建了一个输入流(Input),此时应该是进行读取操作将数据读到程序中。
流既然是用来进行输入输出的,那么在所有的流中可以按每次输入输出的量分成两类流:字节流、字符流。顾名思义字节流每次操作为一个一个字节、字符流则每次读取一个一个字符。
额外的,在字符流中对于一个字符,不同编码方式他也有不同的表现形式,比如汉字,到底是这些字符采用UTF-8(多个字节),还是国标码(2个字节),不同编码对应不同的字节数。所以使用字符流时需要额外指定编码方式。
分成四个超类(很重要):
字节流 | 字符流 | |
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
InputStream类
read(),逐字节地以二进制的原始方式读取数据
- public int read(); 读入一个字节,-1表示无
- public int read(byte b[]); 返回读入的字节数
- public int read(byte[] b, int off, int len);
OutputStream类
- write()方法,它的功能是将字节写入流中
- public void write (int b);// 将参数b的低位字节写入到输出流
- public void write (byte b[]);// 将字节数组b[]中的全部字节顺序写入到输出流
- public void write(byte[] b, int off, int len);// 将字节数组b[]中从off开始的len个字节写入到流中
Output的另外两个方法是flush()及close()。
- public void flush (); 刷新缓存,实际写入到文件、网络
- public void close(); 关闭流
Reader类
与InputStream类相似,都是输入流,但差别在于Reader类读取的是字符(char),而不是字节。
Reader的重要方法是read()
- public int read(); //返回:作为整数读取的字符(需要将int转char),如果已到达流的末尾,则返回 -1
- public int read(char b[]); //返回:读取的字符数
- public int read(char[] b, int off, int len);
Writer类
与OutputStream类相似,都是输出流,但差别在于Writer类写入的是字符(char),而不是字节。
Writer的方法有:
- public void write (int b);// 将参数b的低两字节写入到输出流
- public void write (char b[]);// 将字符数组b[]中的全部字节顺序写入到输出流
- public void write(char[] b, int off, int len);// 将字节数组b[]中从off开始的len个字节写入到流中
- public void write( String s);// 将字符串写入流中
- public void write( String s, int off, int len);// 将字符串写入流中, off为位置,len为长度
- public void flush ();// 刷新流
- public void close();// 关闭流
既然上面Reader、Writer、InputStream、OutputStream四类是根据每次操作的量以及输入输出划分出来的,那么就有其他的划分方式,划分为:节点流、处理流
节点流:可以向特定的地方(如文件)读写数据,如文件流FileInputStream、内存流ByteArrayInputStream.
处理流:可以对以及存在的流进行操作,也被称为过滤流(Filter),如缓冲处理流BufferedReader
上图是就是使用节点流和处理流的例子,先通过FileReader读入文件中的字符串,然后使用处理流对这个流进行处理,有了这个处理流后就不必通过节点流提供的read()一个一个进行读取,BufferedReader这个处理流提供了readLine()方法可以一次读出节点流中的一行。这样的两者结合起来的操作实现了对流的包装(链接)。
同时使用节点流和处理流的实际使用例子:
BufferedReader in =
new BufferedReader(new FileReader(file));
BufferedReader in2 =
new BufferedReader(
new InputStreamReader( //该处理流是字节流通向字符流的桥梁
new FileInputStream(file), “utf-8”));
s = in2.readLine();
从上图和例子看出,处理流构造时总是需要带一个其他流对象作为参数,一个流对象经过其他流的多次包装,称为流的链接。
通过节点流、处理流这样的划分方式,当我们进行I/O操作时,只需使用对应的数据类型对应的流,然后通过处理流进行链接,对流进行层层操作,便可以实现所需要的功能。
常用的节点流:
节点类型 | 字节流 | 字符流 |
File 文件 |
FileInputStream FileOutputStream |
FileReader FileWriter |
Memory Array 内存数组 |
ByteArrayInputStream ByteArrayOutputStream |
CharArrayReader CharArrayWriter |
Memory String 字符串 |
StringReader StringWriter |
|
Pipe 管道 |
PipedInputStream PipedOutputStream |
PipedReader PipedWriter |
常用的处理流:
处理类型 | 字节流 | 字符流 |
Buffering 缓冲 |
BufferInputStream BufferOutputStream |
BufferedReader BufferedWriter |
Filtering 过滤 |
FilterInputStream FilterOutputSteam |
FilterReader FilterWriter |
Converting between bytes and character 字节流转换为字符流 |
InputStreamReader OutputStreamWriter 与字符编码有关 |
|
Object Serialization 对象序列化 |
ObjectInputStream ObjectOutputStream |
|
Data conversion 基本数据类型转化 |
DataInputStream DataOutputStream |
|
Counting 行号处理 |
LineNumberInputStream | LineNumberReader |
Peeking ahead 可回退流 | PushbackInputStream | PushbackReader |
Pinting 可显示流 | PrintStream | PrintWriter |
标准输入和输出
平时我们常用的System.in实际上是InputStream类型,而System.out、System.err为PrintStream类型,是一个字符输出流。平时用到的就是流,所以在使用的时候我们经常将System.in用各种处理流封装起来使用。如:
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
br.readLine();
在jdk1.5后增加了Scanner类,使用起来方便了些。
实例!常见内容(二进制、文本、对象)的读写:
二进制流的读写:
tip:IO中“二进制”指的是除了文本、对象之外的其他内容,在这里是一个比较笼统的概念
import java.io.*;
public class Dump {
public static void main(String[]args) {
try
{
dump( new FileInputStream("aaa.bmp"), //FileInputStream节点流
new FileOutputStream("bbb.bmp"));
}
catch(FileNotFoundException fex)
{
fex.printStackTrace();
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
public static void dump(InputStream src, OutputStream dest) //这里使用InputStream、OutputStream类型作为参数
throws IOException
{
InputStream input = new BufferedInputStream(src); //使用处理流进行包装
OutputStream output = new BufferedOutputStream(dest); //向上转型
byte[] data = new byte[1024];
int length = -1;
while ((length = input.read(data)) != -1) { //read()将流中数据读取到data中
output.write(data, 0, length);
}
input.close();
output.close();
}
}
字符的读写:
import java.io.*;
public class CopyFileAddLineNumber {
public static void main (String[] args) {
//将文件的每一行注释去掉,然后加上行号
String infname = "CopyFileAddLineNumber.java";
String outfname = "CopyFileAddLineNumber.txt";
if( args.length >= 1 ) infname = args[0];
if( args.length >= 2 ) outfname = args[1];
try {
File fin = new File(infname);
File fout = new File(outfname);
BufferedReader in = new BufferedReader(new FileReader(fin));
PrintWriter out = new PrintWriter(new FileWriter(fout));
int cnt = 0; // 行号
String s = in.readLine();
while ( s != null ) {
cnt ++;
s = deleteComments(s); //去掉以//开始的注释
out.println(cnt + ": " + s ); //写出
s = in.readLine(); //读入
}
in.close(); // 关闭缓冲读入流及文件读入流的连接.
out.close();
} catch (FileNotFoundException e1) {
System.err.println("File not found!" );
} catch (IOException e2) {
e2.printStackTrace();
}
}
static String deleteComments( String s ) //去掉以//开始的注释
{
if( s==null ) return s;
int pos = s.indexOf( "//" );
if( pos<0 ) return s;
return s.substring( 0, pos );
}
}
序列化与反序列化(要求对象实现Serializable接口)
import java.io.*;
class Person implements Serializable
{
String name;
int age;
Person(String name, int age) {
this.name=name;
this.age=age;
}
public String toString() {
return name + "(" + age + ")";
}
}
public class SerializeDemo {
public static void main (String[] args)
throws IOException
{
Person [] ps = {
new Person("Li",18),
new Person("Wang",19)
};
String fileName = "s.temp";
//Serialize
ObjectOutputStream output = new ObjectOutputStream(
new FileOutputStream(fileName) );
for(Person p : ps) output.writeObject(p);
output.close();
//deserialize
ObjectInputStream input = new ObjectInputStream(
new FileInputStream(fileName) );
Person p = null;
try {
while( (p=(Person)input.readObject()) != null ) {
System.out.println(p);
}
} catch(ClassNotFoundException ex) {}
catch(EOFException eofex) {}
input.close();
}
}