目录

前言

1.io流的概述

2.字节流

3.字符流

4.字节字符转换流

5.缓冲流

6.打印流

8.对象流

9.数据流

10.字节数组流

11.字符串流

12.管道流

13.zip流

14.实现文件的分割----缓冲流的运用

总结



前言

io流在Java的开发中,都是非常核心的一部分内容,今天来比较全面的来梳理,关于Java io流相关的知识。file的操作在上一节的内容已经提及,这里不再赘述。下面我们来开始学习io流吧!

1.io流的概述

io流:1.输入流——input    2.输出流——output

流:是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或者抽象。数据在两台设备间的传输称为流。流的本质是数据传输,根据数据传输特性将流抽象为各种类,可以方便直观的对数据进行操作。

io流的分类:   

根据处理数据类型的不同分为:  字符流和字节流

根据流的方向:输入流和输出流

2.字节流

字节输出流:OutputStream

public abstract class OutputStream extends Object implements Closeable,Flushable

这个抽象类是所有输出字节流的超类。输出流接受输出字节并且将这些字节发送到inputStream类某个接收器要向文件输出,使用FileOutputStream类

字节输入流:InputStream

public abstract class IntputStream extends Object implements Closeable

这个抽象类是所有输入字节流的超类,FileInputStream类

补充:read()方法的使用

1、 read()方法,这个方法从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。

2、read(byte[] b,int off,int len)方法,将输入流中最多 len 个数据字节读入 byte 数组。尝试读取len 个字节,但读取的字节也可能小于该值。以整数形式返回实际读取 的字节数。

3、read(byte[] b)方法,从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。以整数形式返回实际读取的字节数。

字节流代码示例     实现数据的写入和读取

public static  void out() throws IOException {
        //确定目标文件
    File file = new File("D:\\io\\lzy.txt");
    //构建一个文件输出流的对象
    OutputStream out = new FileOutputStream(file);
    //输出内容
    String info = "我好开心啊啊啊啊";
    //把输出内容转换成字节写入到文件中
    out.write(info.getBytes(StandardCharsets.UTF_8));
    out.close();//关闭流
    System.out.println("成功输入");
}

public static void In() throws IOException {
        //确定目标文件
    File file = new File("D:\\io\\lzy.txt");
    //创建输入流对象
    InputStream in = new FileInputStream(file);
    //创建一个有1024个字节大小的字节数组
    byte[] bytes = new byte[1024];
    StringBuffer buf = new StringBuffer();
    int len = -1;
    //in.read(bytes)  向目标文件读取1024byte大小
    //in.read(bytes)读取一定数量的字节   并且保存在byte[]数组中,返回实际读取字节数,如果没有返回-1;
    while ((len = in.read(bytes))!=-1){ 
        //new String(bytes)   字节变成字符  然后添加到buf
        buf.append(new String(bytes));
    }
    System.out.println(buf);   //输出
    in.close();
}

3.字符流

字符流:

字符输出流:  Writer,对文件的操作使用子类,FileWriter

字符输入流: Reader,对文件的操作使用子类,FileReader

每一次操作的单位都是字符

字符流和字节流的区别:

首先,在所有的操作中,字节永远是最基础的,任何基于字节的操作都是正确的。如果确认流里只有可打印的字符,英文,中文等等,可以考虑字符流。看上去方便一点。如果不确定,使用字节流总是不会出错的。

字符流代码示例:   tip:可以和上面的字节对比一下

/**
 * 字符io  StringBuffer
 * */

public  static void out() throws IOException {
    File file = new File("D:\\io\\lzy.txt");// 目标文件
    Writer out = new FileWriter(file,true);//字符输出流对象
    out.write(",来了来了来了");//输出内容
    out.close();
}

public static  void in() throws IOException {
    File file = new File("D:\\io\\lzy.txt");
    Reader in = new FileReader(file);
    char[] cs = new char[1];
    int len = -1;
    StringBuffer buf = new StringBuffer();
    while ((len=in.read(cs))!=-1){   //每次读取一个字符  然后添加到buf
        buf.append(new String(cs,0,len));
    }
    in.close();
    System.out.println(buf);

}

4.字节字符转换流

转换流:可以将字节流转换为字符流,也可以将一个字符流转换为字节流

OutputStreamWriter:  字符----->字节

InputStreamWriter:字节----->字符

示意图: 

字符串转字节串 java_java

转换流代码示例:

public static void write(OutputStream out) throws IOException {
        Writer writer = new OutputStreamWriter(out);  //创建一个字符输出流对象  并且字符转换为字节  传参out
        writer.write("我最帅\r\n");  //写入数据
        writer.close();
    }

    private  static  void  read(InputStream in) throws IOException {
        Reader reader = new InputStreamReader(in, Charset.defaultCharset());  //创建一个字符输入流对象  并且字节转换为字符
        char[] cs = new char[1024];
        int len = -1;
        while ((len=reader.read(cs))!=-1){
            System.out.println(new String(cs));   //直接以字符串形式输出cs数组
        }
    }

5.缓冲流

首先,对文件或者是对目标频繁的进行读写操作,效率低,性能差。

所以引入缓冲流,来使更加高效的读写信息,原理就是将数据先缓存起来,然后一起写入或者读出。

BufferedInputStream:为另一个输入流添加一些功能,在创建BufferedInputStream时,会创建一个内部缓冲区数组,用于缓冲数据。

BufferedOutputStream:设置这种输出流,应用程序可以将字节写入底层输出流中,而不必针对每一次字节写入调用底层系统。

BuffereReader:字符输入流中读取文本,缓冲各个字符,从而实现字符的高效读取。

BuffereWriter:将文本写入字符输出流,缓冲各个字符,从而实现字符的高效写入。

字节缓冲流代码:

/**
 * 字节缓存流
 * */

private static void byteWriter() throws IOException {
    File file = new File("D:\\io\\lzy.txt");
    OutputStream out = new FileOutputStream(file);
    BufferedOutputStream bos = new BufferedOutputStream(out);//输出流缓存  一次缓存8kb
    String info = "小桥流水人家";
    bos.write(info.getBytes());   //将字符串转化为字节
    bos.close();  //先关闭out再刷新
}

private static void byteRead() throws IOException {
    File file = new File("D:\\io\\lzy.txt");
    InputStream in = new FileInputStream(file);
    BufferedInputStream bis = new BufferedInputStream(in);
    byte[] bytes = new byte[1024];
    int len = -1;
    while ((len=bis.read(bytes))!=-1){
        System.out.println(new String(bytes));
    }
}

字符缓冲流代码

/**
 * 字符流缓存
 * */
private  static void charReader() throws IOException {
    File file = new File("D:\\io\\lzy.txt");
    Reader reader = new FileReader(file);
    BufferedReader br = new BufferedReader(reader);
    char[] chars = new char[1024];
    br.read(chars);
    System.out.println(chars);
    br.close();
}

private static void charWriter() throws IOException {
    File file = new File("D:\\io\\lzy.txt");
    Writer writer = new FileWriter(file);
    BufferedWriter bw = new BufferedWriter(writer);
    bw.write("我爱乐昌");
    bw.close();
}

6.打印流

主要功能是用于输出,在整个IO包中打印流分为两种类型

字节打印流:PrintStream        字符打印流:PrintWriter

打印流可以很方便的进行输出

private static void bytePrint() throws FileNotFoundException {
        File file = new File("D:\\io\\lzy.txt");
        OutputStream out = new FileOutputStream(file);
        BufferedOutputStream bos = new BufferedOutputStream(out);
        //bos.write();
        PrintStream  ps = new PrintStream(bos);  //打印流实例
        ps.println("花姑娘,大大滴好");
        ps.close();
}

private static void charPrin() throws IOException {
        File file = new File("D:\\io\\lzy.txt");
        Writer out = new FileWriter(file);
        BufferedWriter bos = new BufferedWriter(out);
        PrintWriter pw = new PrintWriter(bos);
        pw.println("小桥六十水水水水");
        pw.close();
}

8.对象流

先要明白一些概念:

对象的序列化和反序列化

对象的序列化:把对象写入到输出流中,用来存储或者传输 保存的是对象的状态 ,即它的成员变量

反序列化: 从输入流中读取对象

对象流的两个类: 

ObjectOotputStream         :将Java对象基本数据类型和图形写入OutputStream

ObjectInputStream             :对以前使用ObjectOotputStream   写入的基本数据和对象进行反序列化

transient关键字:使用这个关键字说明的变量,不再进行序列化存储

要序列化的对象一定要实现Serializable接口,这个接口没有任何定义,只是告诉JVM该类对象可以被序列化。

什么时候需要对象的序列化呢?

1.把对象存储到文件     2.把对象需要在网络上传输

代码示例:

首先:准备要序列化的对象

字符串转字节串 java_数据_02

/**对象序列化
 *
 * */
public static void writeObect() throws IOException {
        Dog dog = new Dog("旺旺",12);
        File file = new File("D:\\io\\lzy1.txt");
        OutputStream out = new FileOutputStream(file);  //字节流对象实例
    ObjectOutputStream oos = new ObjectOutputStream(out);//对象流实例
    oos.writeObject(dog);
    oos.close();
}

/**对象反序列化*/
public static void readObject() throws IOException, ClassNotFoundException {
    File file = new File("D:\\io\\lzy1.txt");
    InputStream in = new FileInputStream(file);
    ObjectInputStream ois = new ObjectInputStream(in);
    Dog dog = (Dog) ois.readObject();
    ois.close();
    System.out.println(dog.toString());
}

9.数据流

DataInputStream : 数据输入流 , 允许程序与机器无关的方式从底层输入流中读取Java的基本数据类型,程序可以使用数据输入流写入,然后用数据输出流对数据进行读取。 线程不安全

DataOutputStream 数据输入流 允许程序将Java基本数据类型写入输出流、

专门用来读写各种数据 int char long 等等 二者的读写顺序要一样

/**数据流*/
private static void write() throws IOException {
        File file = new File("D:\\io\\lzy1.txt");
        OutputStream out = new FileOutputStream(file);
        BufferedOutputStream bos = new BufferedOutputStream(out);
        DataOutputStream dos = new DataOutputStream(bos);
        dos.writeInt(10);  
        dos.writeByte(1);//写入1个字节
        dos.writeUTF("我爱中国");
        dos.close();
}

private static void read() throws IOException {
    File file = new File("D:\\io\\lzy1.txt");
    InputStream in = new FileInputStream(file);
    BufferedInputStream bis = new BufferedInputStream(in);
    DataInputStream dis = new DataInputStream(bis);
    int num = dis.readInt();  //一定要按写入的数据顺序进行读取
    byte b = dis.readByte();
    String s = dis.readUTF();
    System.out.println(num+","+b+","+s);
}

10.字节数组流

ByteArrayInputStream:   包含一个内部缓冲区,改缓冲区包含从流中读取的字节,内部计数器跟踪read方法要提供下一个字节,关闭ByteArrayInputStream无效,在关闭此流后任然可以被调用,不会产生io异常

  ByteArrayOutputStream:实现一个输出流,其中数据写入一个byte数组,缓冲区会随着数据的增加而自动增长,可以使用toByteArray()和toString()获取数据。也是关闭无效

代码示例:

 在一串字符中,输出字母

/**
 * 再一串字符中,输出字母
 * */
private static void byteArray(){
        String s = "asda1231asda213123asd";
    ByteArrayInputStream bais = new ByteArrayInputStream(s.getBytes());
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    int curr = -1; //每次读取的字节
    while ((curr=bais.read())!=-1) {    //curr=bais.read()返回 0 到 255 范围内的 int 字节值
        if ((curr>=65&&curr<=90)||(curr>=97&&curr<=122)){    //一个字节一个字节读取
            baos.write(curr);
        }
    }
    System.out.println(baos.toString());
    }

运行结果:

字符串转字节串 java_输出流_03

11.字符串流

用途:操作xml 或者json用到

StringReader  StringWriter 可以用在字符串缓冲区的输出来构建字符串,

代码示例:

把一个字符串作为数据源 来构建一个字符流

/**
     * @author zhiyu2.lai
     * @deprecated
     * 字符串流
     * */
    private  static  void stringReader() throws IOException {
        String info = "good good bbbbbbb  lzy";
        StringReader sr = new StringReader(info);
        //流标记器
        StreamTokenizer st = new StreamTokenizer(sr);
        int count = 0;
        while (st.ttype !=StreamTokenizer.TT_EOF){  //EOF是文件结束符
            if (st.nextToken()==StreamTokenizer.TT_WORD){  //nextToken() - 分析下一个
                count++;
            }
        }
        sr.close();
        System.out.println("count="+count);
    }

12.管道流

管道输入流应该链接管道输出流,数据由某个线程从PipedInputStream对象读取,并且由其它线程写入PipedOutputStream.

管道A数据写入 管道B数据读取

字符串转字节串 java_字符串转字节串 java_04

代码示例:

不同线程写入读取数据

//写入数据线程
class WriteThread implements Runnable{
    private PipedOutputStream pout;
    WriteThread(PipedOutputStream pout){
        this.pout = pout;
    }

    @Override
    public void run() {
        try {
            pout.write("一个美女".getBytes());  //管道输出流
            pout.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}
//读取数据线程
class ReadThread implements Runnable{
    private PipedInputStream pin;  //输入管道
    ReadThread(PipedInputStream pin){
        this.pin = pin;
    }

    @Override
    public void run() {
        byte[] buf = new byte[1024];
        try {
            int len = pin.read(buf);
            String s = new String(buf,0,len);
            System.out.println("读到"+s);
            pin.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
。。。。。
    public static void main(String[] args) throws IOException {
        PipedInputStream pin = new PipedInputStream();
        PipedOutputStream pout = new PipedOutputStream();
        pin.connect(pout);// 管道相连

        ReadThread readThread = new ReadThread(pin);
        WriteThread writeThread = new WriteThread(pout);
        new Thread(readThread).start();
        new Thread(writeThread).start();
    }

13.zip流

在java中如何实现文件的压缩和解压?

1)ZipOutputStream     :创建新的zip输出流       ,实现文件的压缩

 void putNesxtEntry(zipEntry e)       开始写入新的zip文件条目,并且将流定位到条目数据开始处

zipEntry(String name)        使用指定名称创建新的zip条目

2) ZipInputStream :创建新的zip输入流,实现文件的解压       

ZipEntry  getNextEntry()    

读取下一个zip文件条目,并且将流定位到改文件条目数据的开始处

代码示例:

实现文件的压缩和解压

public class ioTest {
    public static void main(String[] args) throws IOException {

       compression("D:\\test.zip",new File("D:\\io"));
       decompression("D:\\test.zip","D:\\aaa\\");


    }


private static void  zip(ZipOutputStream zOut, File targetFile, String name/*压缩文件名*/, BufferedOutputStream bos) throws IOException {
        //如果是目录
    if (targetFile.isDirectory()){
        File[] files = targetFile.listFiles();
        if (files.length==0){
            zOut.putNextEntry(new ZipEntry(name+"/"));//处理空目录 putNextEntry  写入zip条目
        }
        for (File f:files){
            zip(zOut,f,name+"/"+f.getName(),bos);
        }
    }else {
        zOut.putNextEntry(new ZipEntry(name));//putNextEntry开始写入新的zip文件   ZipEntry为新的zip文件设置名字
        InputStream in = new FileInputStream(targetFile);
        BufferedInputStream bis = new BufferedInputStream(in);
        byte[] bytes = new byte[1024];
        int len = -1;
        while ((len=bis.read(bytes))!=-1){
            bos.write(bytes,0,len);
        }
    }
}
/**
 * @param zipFileName   压缩文件名
 * @param targetFile    要压缩的文件
 * */
private static void compression(String zipFileName,File targetFile) throws IOException {
        System.out.println("正在压缩");
        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFileName));
        BufferedOutputStream bos = new BufferedOutputStream(out);
        zip(out,targetFile,targetFile.getName(),bos); //out压缩文件的字节流
        bos.close();
        out.close();
}

/**
 * 解压
 * */
private static void decompression(String targetFileName,String parent) throws IOException {
    //构建解压输入流
    System.out.println("解压成功");
    ZipInputStream zIn = new ZipInputStream(new FileInputStream(targetFileName));
    ZipEntry entry = null;
    File file =null;
    while ((entry = zIn.getNextEntry())!=null&& !entry.isDirectory()){  //zIn.getNextEntry()读取下一个zip文件条目
        file = new File(parent,entry.getName()); //创建解压文件
        if (!file.exists()){
            new File(file.getParent()).mkdirs(); //创建此文件上的上一级目录
        }
        OutputStream out = new FileOutputStream(file);  //输出流
        BufferedOutputStream bos = new BufferedOutputStream(out);
        byte[] bytes = new byte[1024];
        int len = -1;
        while ((len=zIn.read(bytes))!=-1){
            bos.write(bytes,0,len);  //将解压流写入
        }
        bos.close();
        System.out.println(file.getAbsoluteFile()+"解压成功");
    }
}


}

14.实现文件的分割----缓冲流的运用

注意分割算法   有详细注释

public class IO {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        File file = new File("D:\\io\\lzy1.txt");
        division(file,70);
}

private static void division(File targetFile,long cutSize) throws IOException {
        if (targetFile==null){return;}
        //计算分割文件数
        int num = targetFile.length()%cutSize==0? (int) ((int) (targetFile.length()) / cutSize) :(int)(targetFile.length()/cutSize+1);   //如果不够除,那就把它强行转换为int 再加一
        //构造文件输入流
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(targetFile));
        BufferedOutputStream out = null;   //缓存输出流
        byte[] bytes = null;//每次读取的字节数
        int len = -1;//读取的内容
        int count = 0;//每一个文件要读取的次数
        for (int i=0;i<num;i++){
            out = new BufferedOutputStream(new FileOutputStream(new File("D:\\io"+i+targetFile.getName())));
            if (cutSize<=1024){   //每一个文件大小  小于1024   1kb
                bytes = new byte[(int) cutSize];  //写入原来的
                count=1;//写入一次
            }else {
                bytes = new byte[1024];   //写入1024
                count=(int) cutSize/1024;// 写入次数
            }

            while (count>0&&(len = in.read(bytes))!=-1){   //读入
                out.write(bytes,0,len);// 写出
                out.flush();//刷新
                count--;
            }
            if (cutSize%1024!=0){   //单个文件大小,不能整除
                //bytes = new byte[(int) cutSize%1024];  //读取大小 ==余数
                bytes = new byte[1024];  //读取大小 ==余数
                len = in.read(bytes);
                out.write(bytes,0,len);
                out.flush();
                out.close();
            }
        }
        in.close();
}


}

总结

对各种的流进行了比较深入的讲解,并且都有代码示例来理解各种流的作用,上面的代码都经过本人亲自的编写和运行,所以因为问题不会很大。有错误的地方欢迎指正。