一、流的分类:
- 操作数据单位:字节流、字符流
- 数据的流向:输入流、输出流
- 流的角色:节点流、处理流
二、流的体系结构(重点!~):
这些要配合File类来对文件进行操作:File操作
抽象基类 | 节点流(或文件流) | 缓冲流(处理流的一种) | 转换流(处理流的一种) | 对象流(处理流的一种) |
---|---|---|---|---|
InputStream | FileInputStream | BufferedInputStream | ObjectInputStream | |
OutputStream | FileOutputStream | BufferedOutputStream | ObjectOutputStream | |
Reader | FileReader | BufferedReader | InputStreamReader | |
Writer | FileWriter | BufferedWriter | OutputStreamWriter |
总体的过程如下:
1. File类实例化
2. 对应的节点流、处理流实例化
3. 读入/写入的操作 //.read() 读入 .write()写入
4. 资源的关闭
字节流和字符流的操作其实是相同的,在字节流中,把char[] 数组换成了byte[]数组而已。
三、实例操作:
- 节点流(或文件流)
File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。(读取必须要有文件,不然会报错)
File对应的硬盘中的文件如果存在:
如果流使用的构造器是:FileWriter(file,false) / FileWriter(file) :对原有文件覆盖
如果流使用的构造器是:FileWriter(file,true):不会对原有文件覆盖,而是在原有文件基础上追加内容
结论:
- 对于文本文件(.txt,.java,.c,.cpp),使用字符流处理
- 对于非文本文件(.jpg,.mp3,.mp4,.avi,.doc,.ppt等),使用字节流处理
1.字符流读写
读取文件,我们自定义了一个数组,读取文件时,循环存入这个数组中,当read方法返回-1时,意味着文件读到头了。
//创建一个File实例化对象
File f=new File("d:\\io流数据的验证\\test.txt");
//创建文件字符输入流对象,即打开文件
FileReader fr=new FileReader(f);
//定义一个字符数组
char date[]=new char[1024];
int len;
while((len= fr.read(date))!=-1){
//方法1:
// for (int i=0;i<len;i++){
// System.out.print(date[i]);
// }
//方法2:
String str=new String(date,0,len);
System.out.print(str);
}
fr.close(); //关闭文件
写入文件,新建或打开文件,当为true时,意味着接着写文件,为false意味着覆盖原有文件,\n代表换行
//创建一个File实例化对象
File f=new File("d:\\io流数据的验证\\test.txt");
//新建或打开文件,当为true时,意味着接着写文件,为false意味着覆盖原有文件
FileWriter fw=new FileWriter(f,true);
//写入文件 \n代表换行的意思
fw.write("\n我喜欢xml");
fw.close();;
2.字节流读写
和上面一样,只是char数组换成了byte数组
//创建两个File实例化对象
File f1=new File("d:\\io流数据的验证\\1.jpg");
File f2=new File("d:\\io流数据的验证\\2.jpg");
//创建文件字节输入流对象 字节输出流对象
FileInputStream fr1= new FileInputStream(f1);
FileOutputStream fr2=new FileOutputStream(f2);
//定义一个byte数组
byte date[]=new byte[1024];
int len;
while((len= fr1.read(date))!=-1){
//将读取到的写入文件中
fr2.write(date,0,len);
}
fr1.close(); //关闭文件
fr2.close(); //关闭文件
-
处理流:
1. 缓冲流:处理流之一
子类: - BufferedInputStream - BufferedOutputStream - BufferedReader - BufferedWriter 作用:提供流的读取、写入的速度 提高读写速度的原因:内部提供了一个缓冲区 处理流,就是套接在已有流的基础上。
字节流的读写:
//1.造文件
File f1=new File("d:\\io流数据的验证\\1.jpg");
File f2=new File("d:\\io流数据的验证\\3.jpg");
//2.造流
//2.1造节点流
FileInputStream fr1= new FileInputStream(f1);
FileOutputStream fr2=new FileOutputStream(f2);
//2.2造缓冲流
BufferedInputStream b1=new BufferedInputStream(fr1);
BufferedOutputStream b2=new BufferedOutputStream(fr2);
//可以这样简写
BufferedInputStream b3=new BufferedInputStream(new FileInputStream("d:\\io流数据的验证\\4.jpg"));
//3.复制的细节:读取写入
byte date[]=new byte[1024];
int len;
while((len= b1.read(date))!=-1){
b2.write(date,0,len);
// b2.flush(); write方法内部已经刷新缓存啦
}
//4.资源关闭
//要求先关闭外层的流,再关闭内层的流
//但是关闭外层流的同时,内层流也会自动的进行关闭
b1.close(); //关闭文件
b2.close(); //关闭文件
字符流的读写:
注意.read()到了结尾是-1;.readLine()到了结尾是null
//1.造文件
//2.造流
//2.1造节点流
//2.2造缓冲流
BufferedReader b1=new BufferedReader(new FileReader("d:\\io流数据的验证\\test.txt"));
BufferedWriter b2=new BufferedWriter(new FileWriter("d:\\io流数据的验证\\test1.txt"));
//3.复制的细节:读取写入
//方式一:使用char型数组
/*
char date[]=new char[1024];
int len;
while((len= b1.read(date))!=-1){
b2.write(date,0,len);
}
*/
//方式二:使用String
String date;
while ((date=b1.readLine()) !=null){
//方法一:
//b2.write(date+"\n"); //date中不含换行符
//方法二:
b2.write(date);
b2.newLine(); //提供新的换行符
}
//4.资源关闭
b1.close(); //关闭文件
b2.close(); //关闭文件
2. 转换流:处理流之二
转换流:属于字符流。(按照结尾的单词判断的)
子类:
InputStreamReader:将一个字节的输入流转换成字符的输入流
OutputStreamWriter:将一个字符的输出流转换为字节的输入流
作用:提供字节流和字符流之间的转换
解码:字节、字节数组---->字符串、字符数组
编码:字符串、字符数组---->字节、字节数组
示例图如下:
//综合使用InputStreamReader和OutputStreamWriter
File f1=new File("d:\\io流数据的验证\\test.txt");
File f2=new File("d:\\io流数据的验证\\test_gbk.txt");
FileInputStream fr1=new FileInputStream(f1);
FileOutputStream fr2=new FileOutputStream(f2);
InputStreamReader isr=new InputStreamReader(fr1,"UTF-8");
OutputStreamWriter osw=new OutputStreamWriter(fr2,"gbk");
char date[]=new char[1024];
int len;
while((len= isr.read(date))!=-1){
osw.write(date,0,len);
}
isr.close();
osw.close();
3.字节数组流:
在输入的过程中,常常可能遇到数组长度不够的问题,我们可以采用字节数组流来读取
public class io1 {
public static void main(String[] args) throws IOException {
FileInputStream fis=new FileInputStream("D:\\io流数据的验证\\aa.txt");
BufferedInputStream bis=new BufferedInputStream(fis);
ByteArrayOutputStream baos=new ByteArrayOutputStream();
byte[] buff=new byte[5];
int len;
while ((len=bis.read(buff))!=-1){
baos.write(buff,0, len);
}
System.out.println(baos.toString());
}
}
4.对象流:处理流之三
对象流的使用:
1.ObjectInputStream 和 ObjectOutputStream
2.作用:用于存储和读取基本类型数据或对象处理流。它的强大之处就是可以把Java中的对象序列化存储和反序列化读取。
3.要想一个java对象是可序列化,需要满足相应的要求。(见Person.java)
4.序列化机制:
对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。
当其他程序获取这种二进制流,就可以恢复成原来的Java对象。
5.Transient只能放在变量的前面,表示此变量不被序列化。
序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去
使用ObjectOutputStream实现
注意:记得使用刷新操作!
//1.
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("test.dat"));
//2.
oos.writeObject(new String("我爱北京天安门"));
oos.flush(); //刷新操作
//3
oos.close();
反序列化:将磁盘文件中的对象还原为内存中的一个java对象 使用ObjectInputStream实现
//1.
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("test.dat"));
//2.
Object obj=ois.readObject();
String str=(String) obj;
//3
ois.close();
可是我们在写文件的时候,将具体类写入的时候,会报错,为什么呢?是因为该类没用序列化。
//具体类对象的写入 会报错
oos.writeObject(new Person("name",12));
Person需要满足如下的要求,方可序列化:
-
需要实现接口:Serializable(这是一个空接口)
-
当前类提供一个全局变量:serialVersionUID
-
除了当前的Person类需要实现Serializable接口之外,还必须保证其内部所有属性也必须是可序列化的。(默认情况下,基本数据类型可序列化)
ObjectOutputStream和ObjectInputStream不能序列化 static 和 transient 修饰的成员变量。
这个全局变量一定要有,如果没有定义,系统会自动生成。若类的实例变量做了修改,serialVersionUID可能发生变化。这时候会出错。
class Person implements Serializable{
public static final long serialVersionUID=123124424432423L;
....字段名
....getset、构造器
....toString等
}
操作ArrayList和HashMap对Person类操作的对象流的例子:(重点)
ArrayList:
//写文件
// //1.
// ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("test.dat"));
// //2.
// List<Person> arrayList=new ArrayList<>();
// arrayList.add(new Person("name",12));
// arrayList.add(new Person("aaa",11));
// oos.writeObject(arrayList);
// oos.flush(); //刷新操作
// //3
// oos.close();
// System.out.println(arrayList);
//读文件
//1.
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("test.dat"));
//2.
ArrayList<Person> list=(ArrayList<Person>)ois.readObject();
for (Person ob:list){
System.out.println(ob.name+" "+ob.age);
}
//3
ois.close();
HashMap:
//写文件
// //1.
// ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("test.dat"));
// //2.
// HashMap<Person,Integer> map=new HashMap<>();
// map.put(new Person("yyyzl",12),66);
// map.put(new Person("xxxml",11),77);
// oos.writeObject(map);
// oos.flush(); //刷新操作
// //3
// oos.close();
// System.out.println(map);
//读文件
//1.
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("test.dat"));
//2.
Map<Person,Integer> map1=(Map<Person, Integer>) ois.readObject();
Set<Map.Entry<Person, Integer>> entries = map1.entrySet();
Iterator<Map.Entry<Person, Integer>> iterator = entries.iterator();
while (iterator.hasNext()){
Map.Entry<Person, Integer> m=iterator.next();
System.out.println(m.getKey().name+" "+m.getKey().age+" "+m.getValue());
}
//3
ois.close();
补充:后面写io流操作的时候,可以直接调用common-io包内提供的方法,完成文件的复制
包的地址:common-io包
File f=new File("d:\\io流数据的验证\\1.jpg");
File f1=new File("d:\\io流数据的验证\\10.jpg");
FileUtils.copyFile(f,f1);