流(stream)
本章内容
Java流式输入/输出原理
Java流类的分类
流入/流出流类
常见的节点流和处理流
文件流
缓冲流
数据流
转换流
Print流
Object流
流的原理:读写数据
真正的文件在硬盘里数据,读数据都是0和1,文件想像成一个桶,文件里取数据,管道就是流,和水流的原理一样。
流就是2根管道,一个向里一个向外,无论向里还是向外都是对我们写的程序来说的
包在外面是处理流,一根不够包2根处理流管道,直接套在节点流或者处理流上面
在java程序中,对于数据的输入/输出操作以“流”方式进行,J2SDK提供了各种各样的“流”类,用以获取不通种类的数据,程序中通过标准的方法输入和输出数据
通常我们指的流就是:io包里的类,java.io包中定义了多个流类型(类或抽象类)来事先输入/输出功能:可以从不桶的调度对其进行分类:
按数据流的方向不同可以分为输入流 和 输出流
按处理数据单位不同可以分为字节流 和 字符流
按照功能不同可以分为节点流 和 处理流
(注释):
输入流:程序的角度读数据
输出流:往外写
字节流:0和1
字符流:一个字符一个字符(2个字节)
节点流:管道直接接到数据源上
处理流:套在其他管道上的流,处理流是“连接”在已存在的流(节点流或处理流)之上,
J2SDK所提供的所有刘类型位于包java.io内都分别继承自以下四种抽象流类型
| 字节流 | 字符流 |
输入流 | InuputStream | Reader往里读 |
输出流 | OutputStream | Writer字符形式往外写 |
4根管道:一端接着程序一端接着数据源
InputStream:
继承自InputStream的流都是用于向程序中输入数据,且单位为字节(8bit)
深色:节点流 浅色:处理流
InputStream的基本方法:
abstract | read() throws IOException read一下读一个字节 读取一个字节并以正数的形式返回(0-255) 如果返回-1已到输入流的末尾 |
int | read(byte[] b) throws IOException 返回实际读取的字节数,如果读取前已到输入流的末尾返回-1 (Eg.p2p毁硬盘是硬盘访问的频繁的原因,可以在内存中分配个空间,读一次装满,写:在内存里写好了,一次性写到硬盘里去) |
int | read(byte[] b, int off, int len) throws IOException |
long | skip(long n) throws IOException |
void | close()throws IOException |
OutputStream:
继承自OutputStream的流是用于程序中输出数据,且数据的单位为字节(8bit),此抽象类是表示输出字节流的所有类的超类。
下图中深色为节点流,浅色为处理流
OutputStream的基本方法:
abstract | write(int b) throws IOException 将指定的字节写入此输出流。该字节数据为参数b的低8位 |
void | close() throws IOException 关闭此输出流并释放与此流有关的所有系统资源。 |
void | flush()throws IOException 刷新此输出流并强制写出所有缓冲的输出字节。 |
void | write(byte[] b) throws IOException 将 b.length 个字节从指定的 byte 数组写入此输出流。 |
void | write(byte[] b, int off, int len) throws IOException 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。 |
close()之前调用flush()把缓冲区中的剩余数据写入硬盘
Reader
继承自Reader的流都是用于向程序中输入数据,且数据的单位为字符(16bit)
(2个字节2个字节地往外读)
下图中深色为节点流,浅色为处理流
Writer
继承自Writer的流都是用于程序中输出数据,且数据的单位为字符(16bit);下图中深色为节点流,浅色为处理流
abstract | close() 关闭此流,但要先刷新它。 |
abstract | flush() 刷新该流的缓冲。 |
void | write(char[] cbuf) 写入字符数组。 |
abstract | write(char[] cbuf, int off, int len) |
void | write(int c)写入单个字符。 |
void | |
void |
节点流类型:
类型 | 字符流 | 字节流 |
File(文件) | FileReader FileWriter | FileInputStream FileOutputStream |
Memory Array | CharArrayReader CharArrayWriter | ByteArrayInputStream ByteArrayOutputStream |
Memory String | StringReader StringWriter | |
Pipe(管道) | PipedReader PipedWriter | PipedInputStream PipedOutputStream |
(Stream结尾的都是字节,Writer和Read结尾的都是字符)
FileInputStream(File file)通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。 |
FileInputStream(String name)通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。 |
FileOutputStream(File file) |
FileOutputStream(File file, boolean append) |
FileOutputStream(String name) |
FileInputStream和FileOutputStream类支持父类InputStream和OutputStream提供的数据读写方法
处理流类型
处理类型 | 字符流 | 字节流 |
Buffering | BufferedReader BufferedWriter | BufferedInputStream BufferedOutputStream |
Filtering | FilterReader FilterWriter | FilterInputStream FilterOutputStream |
Converting between bytes and character | InputStreamReader OutputStreamWriter | |
Object Serialization | | ObjectInputStream ObjectOutputStream |
Data conversion | | DataInputStream DataOutputStream |
Counting | LineNumberReader | LineNumberInputStream |
Peeking ahead | PushackReader | PushhackInputStream |
Printing | PrintWriter | PrintStream |
(1)缓冲流:
缓冲流要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,提高了读写的效率,增加了一些新的方法
J2SDK提供了4种缓冲流:
BufferedReader
BufferedWriter
BufferedInputStream
BufferedOutputStream
构造方法:
BufferedReader(Reader in) |
BufferedReader(Reader in, int sz) //sz为自定义缓冲区大小 |
BufferedWriter(Writer out) |
BufferedWriter(Writer out, int sz) |
BufferedInputStream(InputStream in) |
BufferedInputStream(InputStream in, int size) |
BufferedOutputStream(OutputStream out) |
BufferedOutputStream(OutputStream out, int size) |
缓冲输入量支持其父类(java.io.FilterInputStream)的mark和reset方法
BufferedReader提供了readLine方法用于读取一行字符串(以\r或\n分割)
BufferedWriter提供了newLine用于写入一个行分隔符
对于输出缓冲流,写出的数据会先在内存中缓存,使用flush方法会使内存中的数据立即写出。
举例(1)
// BufferedInputStream
import java.io.*;
public class TestBufferStream1 {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("D:/java/TestR.java");
BufferedInputStream bis = new BufferedInputStream(fis);//套接在节点流上
int c = 0;
System.out.println(bis.read());//112
System.out.println(bis.read());//117
bis.mark(100);//标记标到100,从第100个字符开始往外读
for(int i=0;i<=10 && (c=bis.read())!=-1;i++){
System.out.print((char)c+" ");
}
System.out.println();
bis.reset();//又回到刚才标记的第100个字符的点
for(int i=0;i<=10 && (c=bis.read())!=-1;i++){
System.out.print((char)c+" ");
//char强制转化成字符,注意这有个里ASCII码 回车:13换行:10,可以去掉char看到
}
bis.close();
} catch (IOException e) {e.printStackTrace();}
}
}
举例(2)
//BufferedWriter BufferedReader
import java.io.*;
public class TestBufferStream2 {
public static void main(String[] args) {
try {
BufferedWriter bw = new BufferedWriter(new FileWriter("d:/java/bw.txt"));
//管道是writer类型,在这个文件夹内写进100行数据,每一行是一个随机数
BufferedReader br = new BufferedReader(new FileReader("d:/java/br.txt"));//管道是reader类型txt:事先写了一些中文字符
String s = null;
for(int i=1;i<=100;i++){
s = String.valueOf(Math.random());
bw.write(s);
bw.newLine(); //以"行"输入
}
bw.flush(); //close()之前调用flush()把缓冲区中的剩余数据写入硬盘
while((s=br.readLine())!=null){ //readLine方法值得使得在其他管道上套上Buffered**
System.out.println(s);
}
bw.close();
br.close();
} catch (IOException e) {e.printStackTrace();}
}
}
(2)转换流
InputStreamReader和OutputSteamWriter用于字节数据到字符数据之间的转换
InputStreamReader需要和InputStream”套接”
OutputStreamWriter需要和OutputStream”套接”
转换流在构造时可以指定其编码集合
InputStream isr = new InputStreamReader(System.in,”ISO8859_1”)
InputStream转换成Reader
编码:
ISO8859_1是个比较特殊的字符集,它是一个8位的编码,这就让他成为一个非常好的中介机构,会忠实的将每个字节的数据记录下来而不改变,估计这是web传输和jdbc传输使用它为字符集的原因吧
它包含所有西欧语言,还有个名字叫latin-1(latin-1~latin-9基本把西方所有国家文字统一)
东方人: GBK、gb2312(国标码)
全球统一:Unicode
(jsp的时候再说这个问题)
实例(1)
import java.io.*;
public class TestTransForm1 {
public static void main(String[] args) {
try {
OutputStreamWriter osw = new OutputStreamWriter(
new FileOutputStream("d:\\bak\\char.txt"));
osw.write("mircosoftibmsunapplehp");//而FileOutputStream是没有这个接口直接写字符串的
System.out.println(osw.getEncoding());//当前程序默认的编码是GBK
osw.close();
osw = new OutputStreamWriter(
new FileOutputStream("d:\\bak\\char.txt", true),"ISO8859_1");
//原来的文件内容基础上添加,去掉true就覆盖了//按照指定的字符编码写
osw.write("mircosoftibmsunapplehp");
System.out.println(osw.getEncoding());
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
实例(2)
import java.io.*;
public class TestTransForm2 {
public static void main(String args[]) {
InputStreamReader isr =
new InputStreamReader(System.in);
//System.out是dos界面,System.in键盘输入
//注意:System.in本身就是个inputStream,就是根管道
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)数据流
Eg.把一个long类型的数写到文件里,long类型的数很长,不断地转换成字符串,再转换成字节数组,内存占用大
但一个long类型的数组占8个字节,转换出来要80个字节
我们可以直接把8个字节写到一个文件里去
DataInputStream和DataOutputStream分别继承自InputStream和OutputStream,它属于处理流,需要分别”套接”在InputStream和OutputStream类型的节点流上
DataInputStream和DataOutputStream提供了可以存取与机器无关的java原始类型数据(如:int,double等)的方法
DataInputStream和DataOutStream的构造方法:
DataInputStream(InputStream in) |
DataOutputStream(OutputStream out) |
方法查看api文档
实例
import java.io.*;
public class TestDataStream {
public static void main(String[] args) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//(1)分配了一个字节数组(2)管道堆在了内存里的这块字节数组上
DataOutputStream dos = new DataOutputStream(baos);//套接
try {
dos.writeDouble(Math.random());//一下把8个字节(double型)写进去
dos.writeBoolean(true);//布尔(1个字节)
ByteArrayInputStream bais =
new ByteArrayInputStream(baos.toByteArray());
//toByteArray(转换成字节数组byte[]
System.out.println(bais.available());//有多少个字节
DataInputStream dis = new DataInputStream(bais);//dis套到bais上(字节数组)上去了
System.out.println(dis.readDouble());//注意!先进先出(队列)
System.out.println(dis.readBoolean());
dos.close();
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Print流:
PrintWriter和PrintStream都属于输出流,分别针对与字符和字节(属于成对)
PrintWriter和PrintStream提供了重载的print
Println方法用于多种数据类型的输出
PrintWriter和PrintStream的输出操作不会抛出异常,用户通过检测错误状态获取错误信息
PrintWriter和PrintStream有自动flush功能
问: 怎么没有调用close方法
(在jsp的时候,try catch太多了很累,那么用Print比较省力)
PrintWriter(Writer out) |
PrintWriter(Writer out, boolean autoFlush) |
PrintWriter(OutputStream out) |
PrintWriter(OutputStream out, boolean autoFlush) |
PrintStream(OutputStream out) |
PrintStream(OutputStream out, boolean autoFlush) |
实例(1)
import java.io.*;
public class TestPrintStream1 {
public static void main(String[] args) {
PrintStream ps = null;
try {
FileOutputStream fos =
new FileOutputStream("d:\\bak\\log.dat");
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;
}
}
}
}
实例(2)
import java.io.*;
public class TestPrintStream2 {
public static void main(String[] args) {
String filename = args[0];//命令行参数
if(filename!=null){
list(filename,System.out);//把filename当成参数传到list方法内
}
}
public static void list(String f,PrintStream fs){//fs->System.out
//把本来传到dos界面的文字传到fs
try {
BufferedReader br =
new BufferedReader(new FileReader(f));
String s = null;
while((s=br.readLine())!=null){
fs.println(s);
}
br.close();
} catch (IOException e) {
fs.println("无法读取文件");
}
}
}
//运行:java TestPrintStream2 Hello.java(绝对路径或者当前目录下有的文件)
实例(3)
import java.util.*;
import java.io.*;
public class TestPrintStream3 {
public static void main(String[] args) {
String s = null;
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
try {
FileWriter fw = new FileWriter
("d:\\bak\\logfile.log", true); //java日志开发包Log4J
PrintWriter log = new PrintWriter(fw);
while ((s = br.readLine())!=null) {//阻塞,必须键盘输入
if(s.equalsIgnoreCase("exit")) break;
System.out.println(s.toUpperCase());
log.println("-----");//log文档里输入
log.println(s.toUpperCase()); //log文档里输入大写
log.flush();//保险写写无所谓
}
log.println("==="+new Date()+"==="); //当前的时间
log.flush();
log.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Object流:
直接将Object写入或读出,
transient关键字
serlalizable接口
externalizable接口:标记是否可以序列化(但不可以控制), 不过它自己可以控制自己的序列化过程,serlalizable接口的子接口, 尽量不要控制,
把整个Object写到硬盘上
序列化和反序列化
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("d:/share/java/io/testobjectio.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(t);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("d:/share/java/io/testobjectio.dat");
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;
transient int k = 15;
//transient透明:可以用来修饰成员变量,在序列化的时候不予考虑,不写入硬盘,默认值0
}
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。
ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
具体看api
总结
InputStream/OutputStream
Reader/Writer
FileInputStream/FileOutputStream
FileReader/FileWriter
BufferedInputStream/Output
BufferedReader/BufferedWriter
ByteArrayInputStream/Output
InputStreamReader/Output
DataInputReader/OutputStreamWriter
PrintStream/PrintWriter
ObjectInputStream/Out