IO流
一、阻塞队列
BlockingQueue
是Queue的子接口,有两个实现类:ArrayBlockingQueue何LinkedBlockingQueue
ArrayBlockingQueue是有界队列;队列中存储的元素可以限制(推荐)
LinkedBlockingQueue是无界队列;队列中存储的元素无限制,可以一直存储
阻塞队列中提供了存储阻塞的方法:take和put
//阻塞队列接口:BlockingQueue; 两个实现类:有界和无界队列
BlockingQueue<String> queue = new ArrayBlockingQueue<String>(3);
queue.put("zs"); //存储-超过个数则阻塞
queue.put("zs");
queue.put("ww");
//queue.put("zl");
System.out.println(queue);
System.out.println(queue.take()); //获取,
System.out.println(queue.take()); //获取,
System.out.println(queue.take()); //获取,
System.out.println(queue.take()); //获取,超过了,则阻塞
模拟生产者消费者
//通过阻塞队列模拟生产者和消费者模型
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5);
new Thread(new Runnable() {
@Override
public void run() {
//一直负责生产的线程
for(int i=1;i<=100;i++) {
try {
queue.put(i);
} catch (InterruptedException e) {}
System.out.println("正在生产第"+i+"件货");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
//一直负责消费的线程
for(int i=1;i<=100;i++) {
try {
System.out.println("正在消费第"+queue.take()+"件货");
} catch (InterruptedException e) {}
}
}
}).start();
二、IO概述与划分
概述
IO流:文件与程序之间数据的传输
IO流的输入与输出是通过程序作为参考
IO划分
按方向划分:输入流与输出流
输入流:从文件中读取数据到程序
输出流:从程序中写出数据到文件
按单位划分:字节流与字符流
字节流:一个字节一个字节的读写;读写效率低,但可以读写所有的文件
字符流:一个字符一个字符的读写;读写效率高,但只能读写文本文件;不能读写二进制文件(音频,图片,视频)
按功能划分:节点流与处理流
节点流:没有经过包装的最原生的流
处理流:经过包装后,功能更为强大的流
三、字节流
流都是分开的,有输入流和输出流两个类
字节流最大的类为OutputStream/InputStream(抽象类)
应用:实例化子类对象来进行操作,且有很多场景下直接使用面向对象来操作流,因为需要使用子类独有方法
字节节点流
文件输入输出流FileOutputStream/FileInputStream:是一个字节节点流
FileOutputStream节点流写数据
@Test //单元测试的注解,可以针对指定方法进行测试 注意当前包不要有Test的类
public void outTest(){
//字节流分为输入流和输出流:
//输入流提供了读和关闭方法; 输出流提供了写和关闭方法
//注意:在项目中操作IO流往往都是捕获;我们后续为了方便讲解,则使用抛出即可
OutputStream os = null;
try {
//实例化输出流,指定存文件名; 没有加盘符,说明是当前工程下的文件
os = new FileOutputStream("a.txt");
os.write("helloworld".getBytes()); //String转byte[]
} catch (Exception e) {
e.printStackTrace();
}finally {
//统一资源关闭
Utils.closeAll(os); //注意:读写数据后,一定要记得关闭资源;否则容易出问题
}
}
FileInputStream节点流读数据
@Test
public void readTest() {
InputStream is = null;
try {
is = new FileInputStream("a.txt");
byte[] b = new byte[1024];
int len = is.read(b); //读取数据,并放到b数组,返回长度;如果结束,则返回-1
while(len!=-1) {//完整写法;如果文件的数据超出1024字节,即可循环去读
//参数1:转数据的字节数组 参数2:转的下标-0 参数3:转的长度
System.out.println(new String(b,0,len)); //byte[]转字符串
len = is.read(b); //-1
}
} catch (Exception e) {
e.printStackTrace();
}finally {
Utils.closeAll(is); //统一资源关闭
}
}
Utils关闭资源工具类
public class Utils {
//所有IO流类的接口,里面提供了统一close方法
public static void closeAll(Closeable... cs) { //可变长参数
for(Closeable c:cs) { //数组的增强for遍历
if(c!=null) {
try {
c.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
文件的拷贝:将当前工程的a.txt拷贝到d:\b.txt
分析:先将a.txt的资源循环读取,并写到b.txt
//文件的拷贝:将当前工程的a.txt拷贝到d:\b.txt
//分析:先将a.txt的资源循环读取,并写到b.txt
InputStream is = new FileInputStream("a.txt");
OutputStream os = new FileOutputStream("d:\\b.txt");
byte[] b = new byte[1024];
int len;
while((len=is.read(b))!=-1) { //循环先读后写
os.write(b,0,len); //读多少则写多少
}
Utils.closeAll(is,os); //统一资源关闭
字节缓冲流
字节缓冲流:BufferedOutputStream/BufferedInputStream,父类是FilterOutputStream,爷爷类是OutputStream
字节缓冲流是处理流,包装了节点流后生成的缓冲流
@Test //写
public void outTest() throws IOException {
//字节缓冲流:里面带了缓冲区,存取数据都需要先经过缓冲区;
//缓冲区满或刷新缓冲区,内容才会到磁盘文件
//好处:减少了与磁盘交互次数,提升了性能
//内部提供的缓冲区大小为8192个(恒定值)字节,如果未超出范围,都在缓冲区中
//设计模式:装饰者模式,针对组织结构的设计
//BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a.txt"),10); //不常用
bos.write("helloworld..缓冲流".getBytes()); //写到缓冲区
//bos.flush(); //刷新缓冲区
bos.close(); //刷新缓冲区+关闭资源
}
@Test //读
public void inTest() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
byte[] b = new byte[1024];
int len = bis.read(b);
System.out.println(new String(b, 0,len));
bis.close();
}
对象流
ObjectOutputStream对象流:用于读写对象的流; 写的过程则是序列化(加密);读的过程则是反序列化(解密)
//Serializable 标记型接口,没有抽象方法
class Person implements Serializable{ //实现序列化接口
/*transient*/ String name; //transient,static:都是不参与序列化的关键字
int age;
public Person(String name,int age) {
this.name=name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
public class Test2 {
@Test //对象流的写
public void outTest() throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
//oos.writeObject("zs");
//存自定义对象
//也可以通过集合写和读多个对象,并存储到对象流
oos.writeObject(new Person("zs", 30));
oos.writeObject(new Person("ls", 20)); //存多个对象
oos.writeObject(new Person("ww", 25));
oos.close();
}
@Test //对象流的读
public void inTest() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
//String o = (String) ois.readObject();
Object o = null;
try {
while(true) {
System.out.println(ois.readObject()); //取多个对象
}
} catch (Exception e) {
}finally {
ois.close();
}
}
}
四、字符流
字符流的抽象类为Writer/Reader,其他字符流都是它的子类
字符节点流
FileWriter/FileReader: 在字符流里面,字符节点流是最简单的流;
应用上及自身设计都很简单,如果只是简单的读写,则可用字符节点流
为什么设计简单?因为该类全部靠继承而来; 字符流都可以直接存储字符串
@Test
public void outTest() throws IOException {
//查看内部实现可知,字符流都是通过字节流转换而来
FileWriter fw = new FileWriter("a.txt");
fw.write("hello,filewriter..");
fw.close();
}
@Test
public void inTest() throws IOException {
FileReader fr = new FileReader("a.txt");
char[] cbuf = new char[1024];
int len = fr.read(cbuf);
//char[]转String
System.out.println(new String(cbuf,0,len));
fr.close();
}
字符缓冲流
类似字节缓冲流,减少了与磁盘交互的次数,提高了性能
字符缓冲流中提供了两个常用的方法:换行读和写
@Test
public void outTest() throws IOException {
//字符缓冲流:包装字符节点流得到的缓冲流
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
for(int i=1;i<=5;i++) {
bw.write("好好学习,天天向上..."+i);
bw.newLine(); //换行
}
bw.close();
}
@Test
public void inTest() throws IOException {
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
String msg = null; //换行读取,如果读到文件末尾则返回null
while((msg = br.readLine())!=null) {
System.out.println(msg);
}
br.close();
}
字符输出流
字符输出流PrintWriter:打印输出到文件
特点:支持打印换行及原样打印输出
//PrintWriter:打印输出的字符流 (系统打印中PrintStream是打印输出字节流)
//写的方法有:write,以及print/println(打印换行)方法,类似系统打印的方法
PrintWriter pw = new PrintWriter("a.txt");
pw.write("hello,world"); //写字符串
//pw.write(97); //写的是没有换行的码值
pw.println(97); //支持原样打印
pw.println(3.5); //写换行
pw.close();
字符转换流
字符转换流OutputStreamWriter: 可以将字节流转换为字符流; 同时可设置字符的编码方式
@Test
public void outTest() throws IOException {
//字符节点流FileWriter的父类OutputStreamWriter
//中文乱码问题:当写的编码和读的编码不一致,则会出现乱码
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"),"utf-8");
osw.write("hello,转换流"); //将数据以utf-8编码方式写到文件
osw.close();
}
@Test
public void inTest() throws IOException {
InputStreamReader isw = new InputStreamReader(new FileInputStream("a.txt"),"utf-8");
char[] cbuf = new char[1024];
int len = isw.read(cbuf);
System.out.println(new String(cbuf,0,len));
isw.close();
}
//应用场景:直接从字节流转换到字符缓冲流,如何转? ---转换流
@Test
public void bufferTest() throws IOException {
OutputStream os = new FileOutputStream("a.txt");
//通过封装转换流,可以得到字符缓冲流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
bw.write("helloworld..."); //字符缓冲流提升性能,且可换行读写功能
bw.close();
}
五、总结与作业
总结
作业
1.使用FileInputStream读取文件,修改文字,在使用FileOutputStream输出修改文件
2.使用FileInputStream复制图片
3.使用对象流,写出集合,集合封装多个对象的存储,并使用对象流读取出来
4.使用字符节点流进行文件的读取和写出
5.使用字符转换流设置编码格式进行文件读取和写出
晨考
1.什么是流_______
答:文件和程序之间的数据传递。
2.创建FileOutputStream 对象时,如果对应的文件在硬盘上不存在,则会__创建内容___,如果对应的文件在硬盘上已经存在,则____清除内容___________;
3.字节缓冲流的缓冲区大小是多少?为什么默认是这个值?
8192,恒定值。当前范围内最稳定值
4.设置编码需要用到哪个流?什么情况下会出现乱码?
转换流;写和读的编码不一致时
5.请依次默写:字符缓冲流,字节节点流,字节缓冲流,转换流
答:
字符缓冲流:Bufferedwriter/BufferedReader
字节节点流:Fileoutputstream/FileInputstream
字节缓冲流:Bufferedoutputstream/BufferedInputstream
转换流:outputstreamwriter/InputstreamReader