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