1 流式输入/输出原理
数据以字节或者字符的形式,通过流(节点流/处理流)进行输入输出。
java.io包(阻塞)
java.nio包(非阻塞,jdk5)
2 输入流和输出流
根据数据从程序角度来看,从文件里读数据叫输入流,写文件叫输出流。
一个字节为单位的读写,叫字节流。
一个字符为单位的读写,叫字节流。
注意:
一个字节为8bit,一个字符是2个字节(一个汉字是一个字符,读汉字的时候建议字符流)。
InputStream:字节流,输入流
OutputStream:字节流,输出流
Reader:字符流,输入流
Writer:字符流,输出流
3 节点流和处理流
节点流连接数据源和程序。处理流包裹节点流,提供更强大的流能力。
4 InputStream(输入流)
说明:
read()方法是一个字节一个字节地往外读,每读取一个字节,就处理一个字节。read(byte[] buffer)方法读取数据时,先把读取到的数据填满这个byte[]类型的数组buffer(buffer是内存里面的一块缓冲区),然后再处理数组里面的数据。
5 OutputStream(输出流)
6 Reader流
7 Writer流
8 节点流的使用
以File类型的节点流说明。
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
* File类型的节点流
* 使用FileInputStream流来读取FileInputStream.java文件的内容
*/
public class FileStreamMain {
public static void main(String[] args) {
int b = 0; // read() 方法读取字节,并以整数的形式返回,-1表示返回末尾
// 字节流读中文会乱码
FileInputStream in = null;
// 字符流读中英文都可以正确显示,每次读一个字符
// FileReader in = null;
try {
in = new FileInputStream("D:\\1.txt");
// in = new FileReader("D:/1.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("系统找不到指定文件!");
System.exit(-1); // 系统非正常退出
}
long num = 0; // 记录读到的字节/字符数
try {
while ((b = in.read()) != -1) {
System.out.println((char)b);
num++;
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件读取错误!");
} finally {
try {
in.close(); // 关闭流
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
* File类型的节点流
* 使用FileOutputStream流往一个文件里面写入数据
*/
public class FileOutputStreamMain {
public static void main(String[] args) {
int b = 0;
FileInputStream in = null;
FileOutputStream out = null;
// FileReader reader = null;
// FileWriter writer = null;
try {
in = new FileInputStream("D:\\1.txt");
out = new FileOutputStream("D:/2.txt");
while ((b = in.read()) != -1) {
out.write(b);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
in.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package io;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
* 字符节点流
*
*/
public class FileReadMain {
public static void main(String[] args) {
FileWriter writer = null;
FileReader reader = null;
try {
writer = new FileWriter("D:/3.txt");
// 把0~60000里面的所有整数都输出
// 全世界各个国家的文字都0~60000内的整数的形式来表示
for (int i = 0; i <=6000; i++) {
writer.write(i); // 以字符的形式写入
}
reader = new FileReader("D:\\3.txt");
int b = 0, num = 0;
while((b = reader.read())!= -1){
System.out.print((char)b + "\t");
num++;
}
System.out.println("总共读取了"+num+"个字符");
} catch (IOException e) {
e.printStackTrace();
}
}
}
9 处理流的使用
带有缓冲区的,缓冲区(Buffer)就是内存里面的一小块区域,读写数据时都是先把数据放到这块缓冲区域里面,减少io对硬盘的访问次数。
package io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
* 处理流
* Buffer类型的处理流
*
*/
public class BufferStreamMain {
public static void main(String[] args) {
FileInputStream inputStream = null;
int c = 0;
try {
inputStream = new FileInputStream("D:/1.txt");
// 在FileInputStream节点流的外面套接一层处理流BufferedInputStream
BufferedInputStream inputStream1 = new BufferedInputStream(inputStream);
System.out.println((char) inputStream1.read());
System.out.println((char) inputStream1.read());
inputStream1.mark(100); // 在第100个字符处做一个标记
for (int i = 0; i <= 10 && (c = inputStream1.read()) != -1; i++) {
System.out.println((char) c);
}
inputStream1.reset(); // 重新回到原来标记的地方
for (int i = 0; i <= 10 && (c = inputStream1.read()) != -1; i++) {
System.out.println((char) c);
}
inputStream1.close();
BufferedWriter writer = new BufferedWriter(new FileWriter("D:\\1.txt"));
String s = null;
for (int i = 0; i < 100; i++) {
s = String.valueOf(Math.random()); //“Math.random()”将会生成一系列介于0~1之间的随机数
writer.write(s);
writer.newLine(); // 调用newLine()方法使得每写入一个随机数就换行显示
}
writer.flush(); // 调用flush()方法清空缓冲区
BufferedReader reader = new BufferedReader(new FileReader("D:/1.txt"));
while ((s = reader.readLine()) != null) {
// readLine()方法读取文件中的数据时是一行一行读取的
// 使用readLine()方法读取数据返回的字符串为空值后则表示已经读取到文件的末尾了
System.out.println(s);
}
writer.close();
reader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
转换流非常的有用,它可以把一个字节流转换成一个字符流,转换流有两种,一种叫InputStreamReader,另一种叫OutputStreamWriter。
InputStream是字节流,Reader是字符流,InputStreamReader就是把InputStream转换成Reader。
OutputStream是字节流,Writer是字符流,OutputStreamWriter就是把OutputStream转换成Writer。
把OutputStream转换成Writer之后就可以一个字符一个字符地通过管道写入数据了,而且还可以写入字符串。我们如果用一个FileOutputStream流往文件里面写东西,得要一个字节一个字节地写进去,但是如果我们在FileOutputStream流上面套上一个字符转换流,那我们就可以一个字符串一个字符串地写进去。
package io;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
/**
* 转换流
* 处理流
* 字节流--》 字符流
*
*/
public class InputStreamMain {
public static void main(String[] args) {
try {
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("D:/1.txt"));
writer.write("abafsd");
System.out.println(writer.getEncoding()); // 使用getEncoding()方法取得当前系统的默认字符编码
writer.close();
// 如果在调用FileOutputStream的构造方法时没有加入true,那么新加入的字符串就会替换掉原来写入的字符串,
// 在调用构造方法时指定了字符的编码
writer = new OutputStreamWriter(new FileOutputStream("D:/1.txt", true),
"ISO8859_1");
// 再次向指定的文件写入字符串,新写入的字符串加入到原来字符串的后面
writer.write("1111");
System.out.println(writer.getEncoding());
writer.close();
// System.in这里的in是一个标准的输入流,用来接收从键盘输入的数据
InputStreamReader reader = new InputStreamReader(System.in);
BufferedReader reader1 = new BufferedReader(reader);
String s = null;
s = reader1.readLine();
while (s != null) {
System.out.println(s.toUpperCase());
s = reader1.readLine(); // 在循环体内继续接收从键盘的输入
if (s.equalsIgnoreCase("exit")) {
break;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package io;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* 数据流
* 处理流
*
*/
public class DataStreamMain {
public static void main(String[] args) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// 在输出流的外面套上一层数据流,用来处理int,double类型的数
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
try {
dataOutputStream.writeDouble(Math.random());
dataOutputStream.writeBoolean(true);
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
System.out.println(inputStream.available());
DataInputStream dataInputStream = new DataInputStream(inputStream);
// 先写进去的就先读出来,调用readDouble()方法读取出写入的随机数
System.out.println(dataInputStream.readDouble());
// 后写进去的就后读出来,这里面的读取顺序不能更改位置,否则会打印出不正确的结果
System.out.println(dataInputStream.readBoolean());
dataInputStream.close();
inputStream.close();
dataInputStream.close();
dataOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class PrintStreamMain {
public static void main(String[] args) {
PrintStream printStream = null;
try {
FileOutputStream outputStream = new FileOutputStream("D:/1.txt");
printStream = new PrintStream(outputStream); // 在输出流的外面套接一层打印流,用来控制打印输出
if (printStream != null) {
// 这里调用setOut()方法改变了输出窗口,以前写System.out.print()默认的输出窗口就是命令行窗口.
System.setOut(printStream);
// 但现在使用System.setOut(ps)将打印输出窗口改成了由ps指定的文件里面,
// 通过这样设置以后,打印输出时都会在指定的文件内打印
}
for (char c = 0; c <= 6000; c++) {
System.out.println(c + "\t");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* 对象流
* 处理流
*
*/
public class ObjectStreamMain {
public static void main(String[] args) {
T t = new T();
t.k = 101;
try {
FileOutputStream outputStream = new FileOutputStream("D:/1.txt");
// ObjectOutputStream流专门用来处理Object的,
// 在fos流的外面套接ObjectOutputStream流就可以直接把一个Object写进去
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(t); // 直接把一个t对象写入到指定的文件里面
objectOutputStream.flush();
objectOutputStream.close();
FileInputStream inputStream = new FileInputStream("D:/1.txt");
// ObjectInputStream专门用来读一个Object的
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
T t1 = (T) objectInputStream.readObject();
System.out.println(t1.k);
objectInputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
// 凡是要将一个类的对象序列化成一个字节流就必须实现Serializable接口
// Serializable接口中没有定义方法,Serializable接口是一个标记性接口,用来给类作标记,只是起到一个标记作用。
// 这个标记是给编译器看的,编译器看到这个标记之后就可以知道这个类可以被序列化 如果想把某个类的对象序列化,就必须得实现Serializable接口
class T implements Serializable {
int i = 10;
int j = 9;
transient int k = 100;
// 在声明变量时如果加上transient关键字,那么这个变量就会被当作是透明的,即不存在。
}
直接实现Serializable接口的类是JDK自动把这个类的对象序列化,而如果实现public interface Externalizable extends Serializable的类则可以自己控制对象的序列化,建议能让JDK自己控制序列化的就不要让自己去控制。