目录
1. Java 流式输入/输出原理
在 Java 程序中,对于数据的输入/输出操作以 “流” ( stream )方式进行;输入流指向称为它的源,输出流的指向称为它的目的地;虽然 I/O 流经常与磁盘文件存取有关,但是源和目的地也可以是·键盘、内存或显示器窗口。
注意: 输入流虽然指向源,但是输入流的功能是从源中获取数据,也就是一个拉取数据的过程。输出流虽然指向目标,是将数据写入到目标,是一个推送数据的过程。
1.2 输入/输出流的分类
- java.io 包中定义了多个流类型(类或抽象类)来实现输入/输出功能;可以从不同的角度对其进行分类:
- 按数据流的方向不同可以分为输入流和输出流。(在程序角度)
- 按处理数据单位不同可以分为字节流和字符流。
- 按照功能不同可以分为节点流和处理流。
- java.io 包( I/O 流库)提供大量的流类,分别继承自以下四种抽象类型
字节流 | 字符流 | |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
1.3 节点流和处理流
- 节点流为可以从特定一个的数据源(节点)读写数据(如:文件,内存)
- 处理流是 “连接” 在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。
1.4 InputStream
继承自 InputStream 的流都是用于向程序中输入数据,且数据的单位为字节(8 bit );下图中红色框内为节点流,其余为处理流。
基本方法
//读取一个字节并以整数的形式返回( 0~255 ),
//如果返回 -1 已到输入流的末尾。
int read() throws IOException
//读取一系列字节并存储到一个数组 buffer,
//返回实际读取的字节数,如果读取前已到输入流的末尾返回 -1
int read(byte[] buffer) throws IOException
//读取 length 个字节
//并存储到一个字节数组 buffer ,从 offset 位置开始
//返回实际读取的字节数,如果读取前已到输入流的末尾返回 -1
int read(byte[] buffer,int offset,int length) throws IOException
//关闭流释放内存资源
void close() throws IOException
1.5 OutputStream
继承自 OutputStream 的流是用于程序中输入数据,且数据的单位为字节(8 bit );下图中红色框内为节点流,其他为处理流。
基本方法
//向输出流中写入一个字节数据,该字节数据为参数 b 的低 8 位
void write(int b) throws IOException
//将一个字节类型的数组中的数据写入输出流
void write(byte[] b) throws IOException
//将一个字节类型的数组中的从指定位置( off )开始的
// len 个字节写入到输出流
void write(byte[] b,int off,int len) throws IOException
//将输出流中缓冲的数据全部写出到目的地
void flush() throws IOException
//尽量先写 flush() ,再写 close()
1.6 Reader
继承自 Reader 的流都是用于向程序中输入数据,且数据的单位为字符( 16 bit );下图中红色框内为节点流,其余为处理流。
基本方法
//读取一个字符并以整数的形式返回(0~225),
//如果返回 -1 已到输入流的末尾
int read() throws IOException
//读取一系列字符并存储到一个数组 buffer
//返回实际读取的字符数,如果读取前已到输入流的末尾返回 -1
int read(char[] cbuf) throws IOException
//读取 length 个字符
//并存储到一个数组 buffer ,从 offset 位置开始
//返回实际读取的字符数,如果读取前已到输入流末尾返回 -1
int read(char[] cbuf,int offset,int length) throws IOException
//关闭流释放内存资源
void close() throws IOException
1.7 Writer
继承自 Writer 的流都是用于程序中输出数据,且数据的单位为字符(16 bit );下图中红色框内为节点流,其余为处理流。
基本方法
//向输出流中写入一个字符数据,该字节数据为参数 b 的低 16 位
void write(int b) throws IOException
//将一个字符类型的数组中的数据写入输出流
void write(char[] cbuf) throws IOException
//将一个字符类型的数组中的从指定位置( offset )开始的
//length 个字符写入到输出流
void write(char[] cbuf,int offset,int length) throws IOException
//关闭流释放内存资源
void close() throws IOException
//将输出流中缓冲的数据全部写出到目的地
void flush() throws IOException
2. 节点流类型
2.1 访问文件
2.1.1 FileInputStream 和 FileOutputStream
FileInputStream 和 FileOutputStream 分别继承自 InputStream 和 OutputStream 用于向文件中输入和输出字节。
import java.io.*;
public class TestFileInputStream{
public static void main(String[] args) {
int b = 0;
FileInputStream in = null;
try {
in = new FileInputStream("C:\\Users\\LENOVO\\eclipse-workspace\\text\\src\\text\\TestFileInputStream.java");
} catch (FileNotFoundException e) {
System.out.println("找不到指定文件");
System.exit(-1);
}
try {
long num = 0;
while((b=in.read())!=-1) {
System.out.print((char)b);
//汉字输出为乱码,因为汉字以两个字节存储
num++;
}
in.close();
System.out.println();
System.out.println("共读取了"+num+"个字节");
} catch (IOException e1){
System.out.println("文件读取错误");
System.exit(-1);
}
}
}
import java.io.*;
public class TestFileOutputStream{
public static void main(String[] args) {
int b = 0;
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("C:\\Users\\LENOVO\\eclipse-workspace\\text\\src\\text\\Hello.java");
out = new FileOutputStream("C:\\Users\\LENOVO\\eclipse-workspace\\text\\src\\text\\HE.java");
//可自动创建文件
while((b=in.read())!=-1) {
out.write(b);
}
in.close();
out.close();
//注意两个文件都要关闭
}catch (FileNotFoundException e2) {
System.out.println("找不到指定文件");
System.exit(-1);
}catch (IOException e1) {
System.out.println("文件复制错误");
System.exit(-1);
}
System.out.println("文件已复制");
}
}
2.1.2 FileReader 和 FileWriter
import java.io.*;
public class TestFileReader{
public static void main(String[] args) {
FileReader fr = null;
int c = 0;
try {
fr = new FileReader("C:\\Users\\LENOVO\\eclipse-workspace\\text\\src\\text\\TestFileReader.java");
while((c=fr.read())!=-1) {
System.out.print((char)c);
//汉字可正常输出
}
fr.close();
}catch (FileNotFoundException e) {
System.out.println("找不到指定文件");
}catch (IOException e) {
System.out.println("文件读取错误");
}
}
}
import java.io.*;
public class TestFileWriter{
public static void main(String[] args) {
FileWriter fw = null;
try {
fw = new FileWriter("C:\\Users\\LENOVO\\eclipse-workspace\\text\\src\\text\\unicode.txt");
//可自动创建
for(int c=0;c<50000;c++) {
fw.write(c);
}
fw.close();
}catch (IOException e1) {
e1.printStackTrace();
System.out.println("文件写入错误");
System.exit(-1);
}
}
}
3. 处理流类型
处理流把另一个流对象作为参数
3.1 缓冲流
- 缓冲流要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,减少读入次数,提高了读写的效率,保护硬盘,同时增加了一些新的方法。
- 对于输出的缓冲流,写出的数据会先在内存中缓存,使用 flush 方法将会使内存中的数据立刻写出。
3.1.1 Buffering
readLine()
import java.io.*;
public class TestBufferStream1{
public static void main(String[] args) {
try {
FileInputStream fis =
new FileInputStream("C:\\Users\\LENOVO\\eclipse-workspace\\text\\src\\text\\Hello.java");
BufferedInputStream bis =
new BufferedInputStream(fis);
int c = 0;
System.out.println(bis.read());
System.out.println(bis.read());
for(int i=0;i<=10 && (c=bis.read())!=-1;i++) {
System.out.print(c+" ");
}
System.out.println();
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.*;
public class TestBufferStream2{
public static void main(String[] args) {
try {
BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\Users\\LENOVO\\eclipse-workspace\\text\\src\\text\\dat2.txt"));
BufferedReader br = new BufferedReader(
new FileReader("C:\\Users\\LENOVO\\eclipse-workspace\\text\\src\\text\\dat2.txt"));
String s = null;
for(int i=1;i<=100;i++) {
s = String.valueOf(Math.random());
//产生一个随机数并转换成字符串
bw.write(s);
bw.newLine();//换行
}
bw.flush();
while((s=br.readLine())!=null) {//读取一行
System.out.println(s);
}
bw.close();
br.close();
} catch (IOException e){
e.printStackTrace();
}
}
}
3.2 转换流
字节流与字符流的转换
import java.io.*;
public class TestTransForm1{
public static void main(String[] args) {
try {
OutputStreamWriter osw = new OutputStreamWriter(
new FileOutputStream("C:\\Users\\LENOVO\\eclipse-workspace\\text\\src\\text\\char.txt"));
osw.write("bluemsun");
System.out.println(osw.getEncoding());
//返回字符编码
osw.close();
osw = new OutputStreamWriter(
new FileOutputStream("C:\\Users\\LENOVO\\eclipse-workspace\\text\\src\\text\\char.txt",true),
//true表示在原有基础上添加,不加则为替换
"ISO8859_1");
//按照规定的字符编码写入
osw.write("bluemsun");
System.out.println(osw.getEncoding());
osw.close();
} catch (IOException e){
e.printStackTrace();
}
}
}
import java.io.*;
public class TestTransForm2{
public static void main(String[] args) {
InputStreamReader isr =
new InputStreamReader(System.in);
//阻塞,等待键盘输入
BufferedReader br = new BufferedReader(isr);
String s = null;
try {
s = br.readLine();
while(s!=null) {
if(s.equalsIgnoreCase("exit"))break;
System.out.println(s.toUpperCase());
s = br.readLine();
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.3 数据流
- DataInputStream 和 DateOutputStream 分别继承自 InputStream 和 OutputStream,它属于处理流,需要分别“套接”在 INpurStream 和 OutputStream 类型的节点流上。
- DataInputStream 和 DateOutputStream 提供了可以存取与机器无关的 Java 原始类型数据的方法。
3.3.1 DataIO 和 ByteArrayIO
import java.io.*;
public class TestDataStream{
public static void main(String[] args) {
ByteArrayOutputStream baos =
new ByteArrayOutputStream();
//创建字节数组
DataOutputStream dos =
new DataOutputStream(baos);
//套接
try {
dos.writeDouble(Math.random());
//8个字节
dos.writeBoolean(true);
//1个字节
ByteArrayInputStream bais =
new ByteArrayInputStream(baos.toByteArray());
//转换成字节数组
System.out.println(bais.available());
//有多少个字节
DataInputStream dis = new DataInputStream(bais);
//套接
System.out.println(dis.readDouble());
//先写先读,先进先出
System.out.println(dis.readBoolean());
dos.close();
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.4 Print流
- PrintWriter 和 PrintStream 都属于输出流,分别针对字符和字节
- PrintWriter 和 PrintStream 提供了重载的 print
- Println 方法用于多种数据类型的输出
- PrintWriter 和 PrintStream 的输出操作不会抛出异常,用户通过检测错误状态获取错误信息
- PrintWriter 和 PrintStream 有自动 flush 功能
import java.io.*;
public class TestDataStream{
public static void main(String[] args) {
PrintStream ps = null;
try {
FileOutputStream fos =
new FileOutputStream("C:\\Users\\LENOVO\\eclipse-workspace\\text\\src\\text\\log.txt");
ps = new PrintStream(fos);
//套接
} catch (IOException e) {
e.printStackTrace();
}
if(ps != null) {
System.setOut(ps);
//输出到 ps
}
int ln = 0;
for(char c=0;c<=60000;c++) {
System.out.print(c+" ");
if(ln++>=100) {
System.out.println();
ln=0;
}
}
}
}
3.5 Object流
- 直接在 Object 写入或读入
- TestObjectIO.java
- transient 关键字
- serializable 接口(序列化,不用考虑如何序列)
- externalizable 接口(自己控制如何序列化)
import java.io.*;
public class TestObjectIO{
public static void main(String[] args) throws Exception {
T t = new T();
t.k = 8;
FileOutputStream fos = new FileOutputStream("C:\\Users\\LENOVO\\eclipse-workspace\\text\\src\\text\\testobjectio.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
//套接
oos.writeObject(t);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("C:\\Users\\LENOVO\\eclipse-workspace\\text\\src\\text\\testobjectio.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
//套接
T tReaded = (T)ois.readObject();
//读入并分解
System.out.println(tReaded.i+" "+tReaded.j+" "+tReaded.d+" "+tReaded.k);
}
}
class T implements Serializable{
int i = 10;
int j = 9;
double d =2.3;
int k = 15;
//transient int k = 15;序列化时不考虑,输出 k 为 0
}