IO流
1.概念
流:代表任何有能力产出数据的数据源对象或者是有能力接受数据的接收端对象
流的本质:数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
作用:为数据源和目的地建立一个输送通道
IO流:数据传输是需要通道的,而IO流就是数据传输的通道.
IO流可以形象的比喻为运送货物的传输带.
2.分类
根据操作的数据类型的不同可以分为:字节流与字符流.
根据数据的流向分为:输入流与输出流,程序(内存)作为参照物,程序从外部读取称为输入(Input),程序向外部写数据成为输出(Output).
根据封装类型不同流又分为:节点流与处理流.
如果流封装的是某种特定的数据源,如文件、字符串、字符串数组等,则称为节点流。
如果流封装的是其它流对象,称为处理流。
处理流提供了缓冲功能,提高读写效率,同时增加了一些新的方法。
节点流中常用类
字节输入流 FileInputStream
字节输出流 FileOutputStream
字符输入流 FileReader
字符输出流 FileWriter
处理流中常用类
缓冲字节输出流 BufferedOutputStream
缓冲字节输入流 BufferedInputStream
缓冲字符输入流 BufferedReader
缓冲字符输出流 BufferedWriter
3.字节流
字节流:每次读取(写出)一个字节,当传输的资源文件有中文时,就会出现乱码.
字节输入流:
父类:InputStream
常用的字节输入流:FileInputStream
1.FileInputStream
①构造方法:
FileInputStream(File)
FileInputStream(String filename)
2.常用方法
①read :读取一个字节,返回该字节的值,如果到达文件的末尾,则返回-1。需要注意:read()方法和迭代器一样,会自动下移的
②read(byte[ ])从输入流中读取至多一个数组长度的内容,到达文件末尾,则返回-1。
- 数组称为缓冲区数组,大小一般取值为1024的整数倍。
- 转换为字符时,使用String(byte [ ] bytes,int offset,int length)
- available()没有读取的剩余字节数,如果该文件还从未被读取,就返回该文件的长度。
- close() 关闭流并释放资源
字节输出流:
字节输出流:OutputStream 是所有输出流的超类
常用子类:FileOutputStream 文件字节输出流
构造方法:
FileOutputStream(File file) /FileOutputStream(String name)
注意:如果父目录不存在,会报FileNotFoundException异常,如果父目录存在,会创建一个新的文件,如果此时已经有文件存在,会覆盖原文件
FileOutputStream(File file,boolean flag)/FileOutputStream(String name,boolean flag)
注意:如果当前文件需要从文件末尾进行插入(接着文件里的内容继续写),必须将第二个参数设置为true,默认不写为false,会覆盖原文件
常用方法:
write(int)向文件中写入一个字节的值
write(byte[]) 向文件中写入一个数组的数据。
***③ write(byte[] offset len) 将 偏移量为 offset 的索引位置的长度为 len 的数据,写入到输出流中。
public static void main(String[] args) throws IOException {
//输入流向程序中读入数据
FileInputStream in = new FileInputStream("E:\\demo.txt" );
//输出流 从程序向外输出数据
FileOutputStream out = new FileOutputStream("F:\\demo.txt");
//FileInputStream read(int b)//一次读一个字节,当内容读完之后返回-1
//FileInputStream read(byte[] b)//每次从输入流中读入ygbyte数组个字节,返回的是数组中实际装入的字节数量
//FileOutStream write(int b) 一次向外写一个字节
// int b = 0;
// while((b = in.read())!= -1){
// out.write(b);
// }
byte[] b = new byte[1024];
int length= 0;
while((length = in.read(b))!=-1) {
System.out.println(length);
out.write(b,0,length);
}
in.close();
out.close();
}
带缓冲区的字节流
- 通过比较 read() 与 read(byte[]) 的方法复制文件的时间的长短,可以看出,带缓冲区的读写文件的速度快,
java 上提供了专门带缓冲区的字节流,用以提高读写速度。 - BufferedInputStream /BufferedOutputStream 带缓冲区的字节输入 / 输出流
- 结论: 带缓冲区的字节流 读取速度高于字节流
public static void main(String[] args) throws IOException {
/*
节点流:直接包含的是文件,字符串等数据
处理流:包含的是其他的流对象
BufferedInputStream,BufferedOutputStream内部提供了一个缓冲数组,提高读写效率
*/
//输入流 向程序中读入数据 节点流 包含的是文件,字符串等数据
FileInputStream in = new FileInputStream("E:\\demo.txt");
BufferedInputStream bin = new BufferedInputStream(in);
//输出流 从程序向外输出数据
FileOutputStream out = new FileOutputStream("F:\\demo.txt");
BufferedOutputStream bou = new BufferedOutputStream(out);
//bin.read();
byte[] b = new byte[1024];
int length = 0;
while((length = in.read(b))!=-1){
bou.write(b,0,length);
}
bin.close();
bou.flush();//刷新缓冲输出流
bou.close();
}
4.字符流
字符流:每次读取(写出)两个字节,有中文时,使用该流就可以正确传输显示中文。
字符输入流:
- Reader 是所有的输入字符流的父类,它是一个抽象类。
- **CharReader、StringReader 是两种基本的介质流,它们分别将Char 数组、String中读取数据。**PipedReader 是从与其它线程共用的管道中读取数据。
- BufferedReader 很明显就是一个装饰器,它和其子类负责装饰其它Reader 对象。
- FilterReader 是所有自定义具体装饰流的父类,其子类PushbackReader 对Reader 对象进行装饰,会增加一个行号。
- InputStreamReader 是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。FileReader 可以说是一个达到此功能、常用的工具类,在其源代码中明显使用了将FileInputStream 转变为Reader 的方法。我们可以从这个类中得到一定的技巧。Reader 中各个类的用途和使用方法基本和InputStream 中的类使用一致。后面会有Reader 与InputStream 的对应关系。
字符输出流:
- Writer 是所有的输出字符流的父类,它是一个抽象类。
- **CharArrayWriter、StringWriter 是两种基本的介质流,它们分别向Char 数组、String 中写入数据。**PipedWriter 是向与其它线程共用的管道中写入数据,
- BufferedWriter 是一个装饰器为Writer 提供缓冲功能。
- PrintWriter 和PrintStream 极其类似,功能和使用也非常相似。
- OutputStreamWriter 是OutputStream 到Writer 转换的桥梁,它的子类FileWriter 其实就是一个实现此功能的具体类(具体可以研究一SourceCode)。功能和使用和OutputStream 极其类似,后面会有它们的对应图。
字符流和字节流的使用范围:字节流一般用来处理图像,视频,以及PPT,Word类型的文件。字符流一般用于处理纯文本类型的文件,如TXT文件等,字节流可以用来处理纯文本文件,但是字符流不能用于处理图像视频等非文本类型的文件。
public static void main(String[] args) throws IOException {
//字符流,每次直接读到一个字符,底层有一个转换流,InputStreamReader
//字符流只能读纯文本文件
FileReader reader = new FileReader("E:demo.txt");
FileWriter writer = new FileWriter("F:demo.txt");
/*System.out.println(reader.read());
System.out.println(reader.read());
System.out.println(reader.read());
System.out.println(reader.read());
System.out.println((char)reader.read());//强转*/
/*int c = 0; //一次读写一个字符
while((c = reader.read())!= -1){
writer.write(c);
}
reader.close();
writer.close();*/
char[] c = new char[1024]; //一次读写1024个字符
int length = 0;
while((length = reader.read(c))!=-1){
writer.write(c,0,length);
}
reader.close();
writer.close();
}
public static void main(String[] args) throws IOException {
FileReader reader = new FileReader("E:\\demo.txt");
BufferedReader bufferedReader = new BufferedReader(reader);
FileWriter fileWriter = new FileWriter("F:\\demo.txt");
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
String line = null;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
bufferedWriter.write(line);
bufferedWriter.newLine();//换行
}
bufferedReader.close();
bufferedWriter.flush();
bufferedWriter.close();
}
5.字节字符之间转换
转换流的作用,文本文件在硬盘中以字节流的形式存储时,通过InputStreamReader读取后转化为字符流给程序处理,程序处理的字符流通过OutputStreamWriter转换为字节流保存。
转换流的特点:
- 其是字符流和字节流之间的桥梁
- 可对读取到的字节数据经过指定编码转换成字符
- 可对读取到的字符数据经过指定编码转换成字节
何时使用转换流?
- 当字节和字符之间有转换动作时;
- 流操作的数据需要编码或解码时。
具体的对象体现:
- InputStreamReader:字节到字符的桥梁
- OutputStreamWriter:字符到字节的桥梁
这两个流对象是字符体系中的成员,它们有转换作用,本身又是字符流,所以在构造的时候需要传入字节流对象进来。
OutputStreamWriter(OutStreamout):将字节流以字符流输出。
InputStreamReader(InputStream in):将字节流以字符流输入。
6.字节字符区别
节流没有缓冲区,是直接输出的,而字符流是输出到缓冲区的。因此在输出时,字节流不调用colse()方法时,信息已经输出了,而字符流只有在调用close()方法关闭缓冲区时,信息才输出。要想字符流在未关闭时输出信息,则需要手动调用flush()方法。
· 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
· 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
结论:只要是处理纯文本数据,就优先考虑使用字符流。除此之外都使用字节流。
7.Print流
Print 打印流:只做输出没有输入
打印流分为字节打印流和字符打印流
PrintWriter:字符打印流
print方法可以打印各种类型数据
public static void main(String[] args) throws FileNotFoundException {
//单向输出(从服务器端向客户端浏览器去打印输出网页内容)
PrintWriter out = new PrintWriter("E:\\demo1.html");
out.println("<b>adadaw</b>");
out.close();
}
8.对象输入输出流
对象的输入输出流 : 主要的作用是用于写入对象信息与读取对象信息。 对象信息一旦写到文件上那么对象的信息就可以做到持久化了 对象的输出流: ObjectOutputStream 对象的输入流: ObjectInputStream
要将序列化之后的对象保存下来,需要通过对象输出流(ObjectOutputStream)将对象状态保存,之后再通过对象输入流(ObjectInputStream)将对象状态恢复。
在ObjectInputStream 中用readObject()方法可以直接读取一个对象,
ObjectOutputStream中用writeObject()方法可以直接将对象保存到输出流中。
//序列化
public static void main(String[] args) throws IOException {
//输出对象信息到指定文件,长久保存,这个过程称为对象的序列化
FileOutputStream fileOutputStream= new FileOutputStream("E:\\temp1.obj");
ObjectOutputStream o = new ObjectOutputStream(fileOutputStream);
Date date = new Date();
String s = "abc";
o.writeObject(date);
o.writeObject(s);
}
//反序列化
public static void main(String[] args) throws IOException, ClassNotFoundException {
//使用对象输入流,将文件中序列化的对象信息读取生成对象的过程称为对象反序列化(也称为创建对象的一种方式)
FileInputStream fileInputStream = new FileInputStream("E:\\temp1.obj");
ObjectInputStream ob = new ObjectInputStream(fileInputStream);
Date date = (Date)ob.readObject();
String s = (String)ob.readObject();
System.out.println(date);
System.out.println(s);
}
9.对象序列化
对象的寿命通常随着生成该对象的程序的终止而终止。
有时候,可能需要将对象的状态保存下来,在需要时再将对象恢复。
对象的输出流将指定的对象写入到文件的过程,就是将对象序列化的过程,对象的输入流将指定序列化好的文件读出来的过程,就是对象反序列化的过程。既然对象的输出流将对象写入到文件中称之为对象的序列化,所以必须要实现Serializable接口。
Serializable接口中没有任何方法。当一个类声明实现Serializable接口后,表明该类可被序列化。
在类中可以生成一个编号
private static final long serialVersionUID = -5974713180104013488L; 随机生成 唯一的
可以显示的声明创建序列号id,如果实现接口不显式的生成序列化版本号,类的信息一旦改变,序列化id也会随之改变。
transient 所修饰的属性不被序列化到文件中。
serialVersionUID 用来表明实现序列化类的不同版本间的兼容性。某个类在与之对应的对象已经序列化出去后做了修改,该对象依然可以被正确反序列化