文章目录
一.概述
1.概念
流就是一组有顺序的,有起点和终点的字节集合,它的本质就是设备之间(例如磁盘与内存之间)进行数据传输.
2.分类
- 按照数据的处理类型不同,分为字节流和字符流
- 按照流的方向不同分为输入流和输出流(入和出都是相对于内存而言的)
- 按照功能不同分为字节流(直接操作数据源)和处理流(对其他流进行处理)
3.抽象类定义
提示:带Stream的都是字节流(万物皆可字节流)带Reader或者Writer的都是字符流(这种流仅限于处理文本数据)
二.字节流
1.概述
记住一句话:计算机世界中,一切皆字节
所以什么信息都可以使用字节流进行处理
字节流一般的类名中都带有Stream关键字
2.FileInputStream与FileOutPutStream
文件字节输入流和文件字节输出流,将文件以字节的方式进行操作
例子:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
public class TestFileInputStream {
public static void main(String[] args) throws Exception {
//创建一个文件输入流(磁盘-->内存),到内存中,所以是输入流
FileInputStream fis = new FileInputStream("test.txt");
//创建一个文件输出流(内存-->磁盘),从内存中出去,所以是输出流
FileOutputStream fos = new FileOutputStream("test_bak.txt");
//开始读取文件到内存中并将其写入到磁盘中的test_bak.txt
byte[] buf = new byte[1024];//创建一个缓存区,一次读1024个字节
int len;//实际读取到的字节数,防止最后一次读取的时候不够1024个字节
while((len = fis.read(buf))>0){//读取到的长度如果大于0那么循环读取
fos.write(buf,0, len);//通过输出流写入到磁盘中
}
System.out.println("success");
fos.close();
fis.close();
}
}
3.BufferedInputStream与BufferedOutputStream
这个是字节缓存流,是一种处理流,这个流比直接使用字节流更加的高效,因为它引入了缓存区
下面看具体的使用方法:
例子:
package exercise;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class TestBufferedInputStream {
public static void main(String[] args) throws Exception {
//创建文件输入流
FileInputStream fis = new FileInputStream("test.txt");
//将文件输入流传入输入缓存流中
BufferedInputStream bis = new BufferedInputStream(fis);
//创建文件输出流
FileOutputStream fos = new FileOutputStream("test_bak");
//将文件输出流传入输出缓存流中
BufferedOutputStream bos = new BufferedOutputStream(fos);
//将文件从磁盘读取并再写入磁盘(复制)
byte[] buf = new byte[1024];
int len;
while((len = bis.read(buf))!=-1){
fos.write(buf, 0, len);
}
System.out.println("success");
bos.close();
fos.close();
bis.close();
fis.close();
}
}
4.字节输入流和字节缓存流的效率比较
以上两个例子没法直接看出效果,那么找一个比较大的文件测试一下两种实现的效率
测试文件1大小:40M
测试文件2大小:249M
普通字节流
package exercise;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
public class TestFileInputStream {
public static void main(String[] args) throws Exception {
//创建一个文件输入流(磁盘-->内存),到内存中,所以是输入流
FileInputStream fis = new FileInputStream("Xshell.exe");
//创建一个文件输出流(内存-->磁盘),从内存中出去,所以是输出流
FileOutputStream fos = new FileOutputStream("Xshell_bak.exe");
//开始时间
long timeBegin = System.currentTimeMillis();
//开始读取文件到内存中并将其写入到磁盘中的test_bak.txt
byte[] buf = new byte[1024];//创建一个缓存区,一次读1024个字节
int len;//实际读取到的字节数,防止最后一次读取的时候不够1024个字节
while((len = fis.read(buf))>0){//读取到的长度如果大于0那么循环读取
fos.write(buf,0, len);//通过输出流写入到磁盘中
}
System.out.println("success");
//结束时间
long timeEnd = System.currentTimeMillis();
//总时间
System.out.println(timeEnd-timeBegin);
fos.close();
fis.close();
}
}
文件1:执行时间:235
文件2执行时间:1374
字节缓存流
package exercise;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class TestBufferedInputStream {
public static void main(String[] args) throws Exception {
//创建文件输入流
FileInputStream fis = new FileInputStream("Xshell.exe");
//将文件输入流传入输入缓存流中
BufferedInputStream bis = new BufferedInputStream(fis);
//创建文件输出流
FileOutputStream fos = new FileOutputStream("Xshell_bak.exe");
//将文件输出流传入输出缓存流中
BufferedOutputStream bos = new BufferedOutputStream(fos);
//开始时间
long timeBegin = System.currentTimeMillis();
//将文件从磁盘读取并再写入磁盘(复制)
byte[] buf = new byte[1024];
int len;
while((len = bis.read(buf))!=-1){
fos.write(buf, 0, len);
}
System.out.println("success");
//结束时间
long timeEnd = System.currentTimeMillis();
//总时间
System.out.println(timeEnd-timeBegin);
bos.close();
fos.close();
bis.close();
fis.close();
}
}
文件1:执行时间:157
文件2执行时间:922
结论:
使用字节缓存流要比直接使用普通的字节流快,大概是普通字节流的1.3~1.5倍,我只测了两个文件,小文件的速度1.3倍,大文件是1.5倍,应该可以猜到,越大的文件使用缓存流会越高效
三.字符流
1.概述
字符流一般是用来处理文本数据的,它比字节流好的地方是可以指定文件的编码格式,使得在传输文本信息的时候不容易乱码
字符流类一般都带有Writer和Reader关键字
2.FileReader和FileWriter
文件字符输入流和文件字符输出流,专门用于处理文本类型数据,这个还不能指定
package exercise;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
public class TestFileReader {
public static void main(String[] args) throws Exception {
//创建字符输入流
FileReader fr = new FileReader("test.txt");
//创建字符输出流
FileWriter fw = new FileWriter("test_bak.txt");
//读取文件并写入
int len;
char[] buffer = new char[1024];
while((len = fr.read(buffer))!=-1){
fw.write(buffer, 0, len);
}
System.out.println("success");
//关闭流(不关闭会出现问题,刚刚忘了关闭了,然后数据就一点也没写进去)
fw.close();
fr.close();
}
}
3.BufferedReader和BufferedWriter
字符缓存流,这个是一种处理流,使用了缓存区,所以要比直接使用文件字符流效率高
package exercise;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
public class TestBufferedReader {
public static void main(String[] args) throws Exception {
//创建文件字符输入流
FileReader fr = new FileReader("test.txt");
//创建字符缓存输入流
BufferedReader br = new BufferedReader(fr);
//创建文件字符输出流
FileWriter fw = new FileWriter("test_bak.txt");
//创建字符缓存输出流
BufferedWriter bw = new BufferedWriter(fw);
//读取文件并写入(这里可以一行一行的读)
//下面的是固定写法,可以防止最后一行多加一个换行符
String lineStr = null;
int counter = 0;
while((lineStr = br.readLine())!=null){
if(counter>0){
bw.newLine();
}
bw.write(lineStr);
counter++;
}
System.out.println("success");
//关闭流,关闭流,关闭流
bw.close();
fw.close();
br.close();
fr.close();
}
}
四.转换流
1.概述
转换流主要是将字节流向字符流的转换,主要有InputStreamReader和OutputStreamWriter
InputStreamReader :主要是将字节输入流转换成字符输入流
OutputStreamWriter :主要是将字节输出流转化成字符输出流
使用转换流可以指定字符编码,不容易出现乱码
2.代码实现
package iotest;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class TestInputStreamReaderAndOutputStreamWriter{
public static void main(String[] args) throws Exception {
String filePath = "src/resources/";
File file = new File(filePath+"hello.txt");
//创建文件字节输入流
FileInputStream fis = new FileInputStream(file);
//创建输入转换流
InputStreamReader isr = new InputStreamReader(fis,"utf-8");
//创建文件字节输出流
FileOutputStream fos = new FileOutputStream(filePath+"hello_bak.txt");
//创建输出转换流
OutputStreamWriter osw =new OutputStreamWriter(fos,"utf-8");
//开始复制文件
char[] buf = new char[1024];
int len;
while((len = isr.read(buf))>0){
osw.write(buf,0,len);
}
//关闭流
osw.close();
fos.close();
isr.close();
fis.close();
System.out.println("success");
}
}
五.打印流
1.概述
打印流只有输出流,它将普通的输出流包装了一下,更方便的实现数据打印.一般用于日志输出
2.代码实现
package iotest;
import java.io.PrintStream;
public class TestPrintStream {
public static void main(String[] args) throws Exception{
String logFilePath = "src/resources/";
//创建打印流
PrintStream ps = new PrintStream(logFilePath+"log.txt");
//向文件中打印日志
ps.print("hello");
//关闭打印流
ps.close();
System.out.println("success");
}
}
六.序列化与反序列化
1.概述
序列化就是将存储在内存中的对象转变为字节流,使其可以方便的进行持久化和数据传输
反序列化就是将字节流转换为内存中的具体对象
实际开发中序列化用到的地方非常多,比如qq的聊天记录就是将聊天信息序列化到了手机的存储中
注意:所有要使用序列化和反序列化的对象的类都要实现Serializable 接口,该接口仅仅是一个标志接口,没有任何方法
ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
2.代码实现
实TestObj
package iotest;
import java.io.Serializable;
public class TestObj implements Serializable{
public String name;
public int age;
public TestObj(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "TestObj [name=" + name + ", age=" + age + "]";
}
}
TestObjectStream类
package iotest;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class TestObjectStream {
public static void main(String[] args) throws Exception{
/***********序列化************/
TestObj testObj1 = new TestObj("张三",22);
TestObj testObj2 = new TestObj("李四",22);
//创建对象输入流
FileOutputStream fos = new FileOutputStream("TestObj.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
//将上面的对象写入到文件TestObj.obj中
oos.writeObject(testObj1);
oos.writeObject(testObj2);
oos.close();
fos.close();
/***********反序列化************/
FileInputStream fis = new FileInputStream("TestObj.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
TestObj obj1 = (TestObj)ois.readObject();
TestObj obj2 = (TestObj)ois.readObject();
System.out.println(obj1);
System.out.println(obj2);
}
}
一般在要序列化的类中要加一个字段来表示当前类的版本
private static final long serialVersionUID;
这样 可以区分在写入前后类是否变化
七.数据流
1.概述
为了方便地操作Java语言的基本数据类型和String的数据,可以使用数据流
数据流有两个类:(用于读取和写出基本数据类型、String类的数据)
⦁ DataInputStream 和 DataOutputStream
⦁ 分别“套接”在 InputStream 和 OutputStream 子类的流上
这个流一般用在网络编程上
2.代码实现
package iotest;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class TestDataStream {
public static void main(String[] args) throws Exception {
//创建数据输出流
DataOutputStream dos = new DataOutputStream(new FileOutputStream("test.data"));
String str = "中国";
int a = 66;
double b = 100.0;
boolean flag = true;
//将不同类型是数据写入问价test.data
dos.writeUTF(str);
dos.writeInt(a);
dos.writeDouble(b);
dos.writeBoolean(flag);
//关闭输出流
dos.close();
//创建数据输入流读取数据
DataInputStream dis = new DataInputStream(new FileInputStream("test.data"));
//读取数据
System.out.println(dis.readUTF());
System.out.println(dis.readInt());
System.out.println(dis.readDouble());
System.out.println(dis.readBoolean());
//关闭输入流
dis.close();
}
}