目录
一、分类
二、字节流InputStream和OutputStream
FileInputStream和FileOutputStream
BufferedInputStream和BufferedOutputStream
ByteArrayInputStream和ByteArrayOutputStream
DataInputStream和DatOutputStream
打印流PrintStream
ObjectInputStream和ObjectOutputStream
标准输入输出流
三、字符流Reader和Writer
字符流操作文件FileReader和FileWriter
字符缓冲流BufferedReader和BufferedWriter
跟踪行号的缓冲字符输入流LineNumberReader
转换流InputStreamReader和OutputStreamWriter
在java程序中,对于数据的输入/输出操作以“流”的方式进行。
Java.io包中定义了多个流类型(类或抽象类)来实现输入/输出功能。
从数据流的方向分类:输入流、输出流
按处理数据单位分类:字节流、字符流
按功能分类:节点流、处理流
Jdk所提供的所有流类型都分别继承自以下四种抽象流类型:
|
字节流 |
字符流 |
输入流 |
InputStream |
Reader |
输出流 |
OutputStream |
Writer |
InputStream常用方法:
abstract int read() |
从输入流中读取一个8位的字节,把它转换为0~255之间的int值并返回。 |
如果因为已经到达流末尾而没有可用的字节,则返回-1。 在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。 抛出IOException - 如果发生 I/O 错误。 |
int read(byte[] b) |
从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中,以int形式返回实际读取的字节数。 |
|
int read(byte[] b, int off, int len) |
将输入流中最多 len 个字节读入字节数组b中。以整数形式返回实际读取的字节数。将读取的第一个字节存储在元素 b[off] 中,下一个存储在 b[off+1] 中,依次类推。 |
|
void close() |
关闭此输入流并释放与该流关联的所有系统资源。 |
抛出IOException |
outputStream常用方法:
abstract void write(int b) |
将指定的字节写入此输出流。 |
抛出IOException - 如果发生 I/O 错误。 |
void write(byte[] b) |
将 b.length 个字节从指定的 byte 数组写入此输出流。 |
|
void write(byte[] b, int off, int len) |
将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。 |
|
void close() |
关闭此输出流并释放与此流有关的所有系统资源。 |
|
void flush() |
刷新此输出流并强制写出所有缓冲的输出字节。 |
字节流的相关类(灰色:节点流;白色:处理流)
例如,通过FileInputStream获取文件中的数据,FileOutputStream将数据写入文件来实现文件的拷贝。
import java.io.*;
public class CopyFile {
public static void main(String[] args) {
// TODO Auto-generated method stub
int tmp = 0;
long time = 0;
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("D:\\测试20171218.png");
out = new FileOutputStream("D:\\Desktop\\test.png");
} catch (FileNotFoundException e) {
System.out.println("文件找不到:" + e.getMessage());
}
try {
long begTime = System.currentTimeMillis();
while ((tmp = in.read()) != -1) {
out.write(tmp);
}
time = System.currentTimeMillis() - begTime;
in.close();
out.close();
} catch (IOException e) {
System.out.println("文件拷贝失败:" + e.getMessage());
}
System.out.println("文件拷贝成功!用时" + time + "ms");
}
}
运行结果:
上面的例子采用缓冲流可以修改为:
…
BufferedInputStream in = null;
BufferedOutputStream out = null;
try {
in = new BufferedInputStream(new FileInputStream("D:\\测试20171218.png"));
out = new BufferedOutputStream(new FileOutputStream("D:\\Desktop\\test.png"));
} …
运行结果:
可见,使用字节缓冲流在效率上有了很大的提升。
构造方法摘要 |
|
ByteArrayInputStream(byte[] buf) |
|
ByteArrayInputStream(byte[] buf, int offset, int length) |
ByteArrayInputStream |
|
返回可从此输入流读取(或跳过)的剩余字节数。 |
ByteArrayOutputStream |
|
返回缓冲区的当前大小。 |
|
创建一个新分配的 byte 数组。其大小是此输出流的当前大小,并且缓冲区的有效内容已复制到该数组中。 返回:以 byte 数组的形式返回此输出流的当前内容。 |
|
|
使用平台默认的字符集,通过解码字节将缓冲区内容转换为字符串。 |
例,使用字节数组输入/输出流将一个文本文档中的内容复制到另一个中去
import java.io.*;
public class ByteArrStream {
public static void main(String[] args) {
// TODO Auto-generated method stub
int b = 0;
FileOutputStream out = null;
FileInputStream in = null;
//创建一个字节数组缓冲区
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
in = new FileInputStream("D:\\test.txt");
out = new FileOutputStream("D:\\target.txt");
while ((b = in.read()) != -1) {
baos.write(b);//将读取到的字节全部写入到该对象的缓冲区中
}
in.close();
baos.close();
out.write(baos.toByteArray());//将缓冲区的数据一次性写入文件
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
|
读取一个输入字节,如果该字节不是零,则返回 true,如果是零,则返回 false。 |
|
读取并返回一个输入字节。 |
|
读取两个输入字节并返回一个 char 值。 |
|
读取八个输入字节并返回一个 double 值。 |
|
读取四个输入字节并返回一个 float 值。 |
|
从输入流中读取一些字节,并将它们存储在缓冲区数组 b 中。 |
|
从输入流中读取 |
|
读取四个输入字节并返回一个 |
|
读取八个输入字节并返回一个 |
|
读取两个输入字节并返回一个 |
|
读入一个已使用 UTF-8 修改版格式编码的字符串。 |
例
import java.io.*;
public class DataStream {
public static void main(String[] args) {
// TODO Auto-generated method stub
ByteArrayOutputStream out = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(out);
try {
dos.writeDouble(Math.random());
dos.writeBoolean(true);
dos.writeUTF("测试");
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
DataInputStream dis = new DataInputStream(in);
System.out.println(dis.available());// 输出可从此输入流读取的剩余字节数
System.out.println(dis.readDouble());
System.out.println(dis.readBoolean());
System.out.println(dis.readUTF());
System.out.println("测试".getBytes("UTF-8").length);// 输出“测试”二字在UTF-8编码中占用的字节数
dis.close();
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果:
需要注意的是,读取数据的顺序需与写入数据的顺序保持一致,先写入的先读取(先进先出)。
例子中,“测试”以UTF-8的格式存储占用6个字节(UTF-8编码中的汉字可能占3个或四个字节),8+1+6=15≠17,这是因为在writeUTF()时该方法会通过 writeShort 方法将两个字节写入输出流,表示实际写出的字节数。之后在 readUTF()会首先读取两个字节,并使用它们构造一个无符号 16 位整数,该整数值被称为 UTF 长度,它指定要读取的额外字节数。然后成组地将这些字节转换为字符。每组的长度根据该组第一个字节的值计算。紧跟在某个组后面的字节(如果有)是下一组的第一个字节。所以,17=8+1+(2+6)。
import java.io.*;
/**
* @Title TestPrintStream.java
* @Description TODO 将运行时输入的main方法参数文件名对应的文件打印在控制台上
* @Author 15643
* @Time 2018年8月11日 下午3:47:42
*/
public class TestPrintStream {
public static void main(String[] args) {
String fileName = args[0];
if (fileName != null) {
play(fileName, System.out);// out关键字是PrintStream类型
}
}
public static void play(String f, PrintStream p) {
String b = null;
try {
BufferedReader bfis = new BufferedReader(new FileReader(f));
while ((b = bfis.readLine()) != null) {
p.println(b);
}
bfis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.*;
public class ObjectStream {
public static void main(String[] args) {
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("D:\\test.txt"));
Stu sw = new Stu();
oos.writeObject(sw);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\test.txt"));
Stu sr = (Stu) ois.readObject();
System.out.println(sr.name+sr.sex+sr.age);
ois.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class Stu implements Serializable {
String name = "张三";
String sex = "男";
int age = 0;
}
字符流的相关类(灰色:节点流;白色:处理流)
在程序开发中,经常需要对文本文件的内容进行读取,如果想从文件中直接读取字符便可以使用字符输入流FileReader,通过此流可以从关联的文件中读取一个或一组字符。若需要向文件中写入字符,可以使用Writer的一个子类FileWriter。
字符流提供了带缓冲区的包装流,分别是BufferedReader和BufferedWriter,其中BufferedReader用于对字符输入流进行包装,BufferedWriter用于对字符输出流进行包装。
特有方法:
BufferedReader |
String |
逐个读取字符,当读到回车"\r" 或 换行"\n" 时会将读到的字符作为一行的内容返回。返回包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null |
抛出: IOException - 如果发生 I/O 错误
|
BufferedWriter |
void newLine()
|
写入一个行分隔符。行分隔符字符串由系统属性 line.separator 定义,并且不一定是单个新行 ('\n') 符。 |
例:使用字符缓冲流将Unicode编码在20000以内的字符写入文本文档
import java.io.*;
public class BufferedReaderWriter {
public static void main(String[] args) {
// TODO Auto-generated method stub
BufferedWriter out = null;
BufferedReader in = null;
String b = null;
try {
out = new BufferedWriter(new FileWriter("D:\\test.txt"));
in = new BufferedReader(new FileReader("D:\\test.txt"));
for (int i = 0; i <= 20000; i++) {//写入Unicode编码在0-20000之间的字符
out.write(i);
if (i % 100 == 0)
out.newLine();
}
while ((b = in.readLine()) != null) {
System.out.println(b);
}
in.close();
out.close();
} catch (IOException e) {
System.out.println("文件写入失败!");
e.printStackTrace();
}
}
}
JDK中提供了一个可以跟踪行号的缓冲字符输入流——LineNumberReader,此类定义了方法 setLineNumber(int)
和 getLineNumber()
,它们可分别用于设置和获取当前行号。
例,从控制台读取一行字母,并将其转换成大写字母输出文本文档
import java.io.*;
public class TransformStream {
public static void main(String[] args) {
// TODO Auto-generated method stub
String b = null;
BufferedWriter out = null;
System.out.println("请输入英文字母或输入exit以退出程序");
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader in = new BufferedReader(isr);
try {
out = new BufferedWriter(new FileWriter("D:\\test.txt"));
while((b = in.readLine()) != null && !b.equalsIgnoreCase("exit")) {//输入不区分大小写的exit即退出程序
out.write(b.toUpperCase());//将英文字母转换成大写并输出
out.newLine();
}
out.close();
in.close();
}catch(IOException e) {
System.out.println("程序出错!");
e.printStackTrace();
}
}
}
运行结果: