前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java中的IO流(一)字节流的常用操作

Java中的IO流(一)字节流的常用操作

作者头像
HcodeBlogger
发布2020-07-14 10:00:56
1K0
发布2020-07-14 10:00:56
举报
文章被收录于专栏:Hcode网站

前言

今天带来的是JAVA的IO流中的字节流,InputStream和OutputStram子类流的用法。

基础概念

流是一种抽象概念,它代表了数据的无结构化传递。按照流的方式进行输入输出,数据被当成无结构的字节序或字符序列。从流中取得数据的操作称为提取操作,而向流中添加数据的操作称为插入操作。用来进行输入输出操作的流就称为IO流。换句话说,IO流就是以流的方式进行输入输出。

IO流分类导向图

何为输入流和输出流?

其实输入(InputStream,Reader)和输出(OutputStream,Writer)是相对于程序来讲,例如一个文件的数据要想在程序中被操作,那么就得输入到程序,这就是输入,操作完成之后又想保存到文件里面,从程序输出数据到文件的过程就是输出。

各种字节流的用法

①节点流

概念:可以从或向一个特定的地方(节点)读写数据。

  1. 文 件 FileInputStream,FileOutputStrean 文件进行处理的节点流。
  2. 数 组 ByteArrayInputStream,ByteArrayOutputStream 将数据与字节数组的互转的节点流。
②处理流

概念:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader.处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接

  1. 缓冲流(装饰流,装饰模式的其中之一):BufferedInputStream,BufferedOutputStream ---增加缓冲功能,避免频繁读写硬盘。
  2. 转换流:InputStreamReader, OutputStreamReader实现字节流和字符流之间的转换。
  3. 数据流:DataInputStream, DataOutputStream 提供将基础数据类型写入到文件中,或者读取出来.
  4. 对象流:ObjectInputStream, ObjectOutputStream对象流可以将一个对象写出,或者读取一个对象到程序中,也就是执行了序列化和反序列化的操作。

序列化的概念: 将一个对象存放到某种类型的永久存储器上称为保持。如果一个对象可以被存放到磁盘或磁带上,或者可以发送到另外一台机器并存放到存储器或磁盘上,那么这个对象就被称为可保持的。(在Java中,序列化、持久化、串行化是一个概念。)

③流的使用一般步骤
  1. 选择源,即是选择要操作的文件或者数据。
  2. 选择流,想要实现何种流的操作。
  3. 流的操作。
  4. 释放资源。
④流的关闭

遵循先开后闭的原则,有多种流的使用时,最先创建的流对象最后关闭。(字节数组流可以不用关闭)

代码语言:javascript
复制
流的对象的创建
try{
    流的操作
  }catch (IOException e) {
    异常处理
}finally{
    流的释放
}

Java7提供了try-with-resources机制,其类似Python中的with语句,将实现了 java.lang.AutoCloseable 接口的资源定义在 try 后面的小括号中,不管 try 块是正常结束还是异常结束,这个资源都会被自动关闭。 try 小括号里面的部分称为 try-with-resources 块。

代码语言:javascript
复制
try(流对象的创建){
    流的操作
  }catch (IOException e) {
    异常处理
}

具体用法

Ⅰ文件流

将文件abc1.txt的copy到abc2.txt

代码语言:javascript
复制
InputStream is = null;
OutputStream os = null;
try {
    InputStream is = new FileInputStream("abc1.txt");//参数可以为File对象
    OutputStream os = new FileOutputStream("abc2.txt");//参数可以为File对象
    byte[] flush = new byte[1024]; //缓冲容器
    int len = -1; //接收长度
    while((len=is.read(flush))!=-1) {
        os.write(flush,0,len); //分段写出
        }
    } catch (IOException e) {
        e.printStackTrace();
    }finally{ //先开后闭
        try {
            if (null != os) {
                os.close();
            } 
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if(null!=is) {
                is.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
}

Ⅱ字节数组流

简单demo将图片用字节数组流和文件流进行复制。

代码语言:javascript
复制
public class Test {

    public static void main(String[] args) {
        byte[] datas = fileToByteArray("p.png");
        System.out.println(datas.length);
        byteArrayToFile(datas,"p-byte.png");
    }
    /**
     * 1、图片读取到字节数组
     * 1)、图片到程序  FileInputStream
     * 2)、程序到字节数组   ByteArrayOutputStream
     */
    public static byte[] fileToByteArray(String filePath) {
        //1、创建源
        File src = new File(filePath);
        byte[] dest =null;
        //2、选择流
        InputStream  is =null;
        ByteArrayOutputStream baos =null;
        try {
            is =new FileInputStream(src);
            baos = new ByteArrayOutputStream();
            //3、操作 (分段读取)
            byte[] flush = new byte[1024*10]; //缓冲容器
            int len = -1; //接收长度
            while((len=is.read(flush))!=-1) {
                baos.write(flush,0,len);         //写出到字节数组中
            }
            baos.flush();
            return baos.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //4、释放资源
            try {
                if(null!=is) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
    /**
     * 2、字节数组写出到图片
     * 1)、字节数组到程序 ByteArrayInputStream
     * 2)、程序到文件 FileOutputStream
     */
    public static void byteArrayToFile(byte[] src,String filePath) {
        //1、创建源
        File dest = new File(filePath);
        //2、选择流
        InputStream  is =null;
        OutputStream os =null;
        try {
            is =new ByteArrayInputStream(src);
            os = new FileOutputStream(dest);
            //3、操作 (分段读取)
            byte[] flush = new byte[1024]; //缓冲容器
            int len = -1; //接收长度
            while((len=is.read(flush))!=-1) {
                os.write(flush,0,len);          //写出到文件 
            }
            os.flush(); //防止文件太小,都存在缓冲容器里面,刷新一下
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //4、释放资源
            try {
                if (null != os) {
                    os.close();
                } 
            } catch (Exception e) {
             e.printStackTrace();
            }
        }
    }
}

Ⅲ缓冲流

装饰流指不直接连接数据源,而是以其它流对象(实体流对象或装饰流对象)为基础建立的流类,该类流实现了将实体流中的数据进行转换,增强流对象的读写能力,比较常用的有BufferedInputStream/BufferedOutputStream和BufferedReader/BufferedWriter等,装饰流类不可以单独使用,必须配合实体流或装饰流进行使用。作用:可以一定限度加快读写速度。

输入流例子:

代码语言:javascript
复制
InputStream  is = new FileInputStream(File对象或者文件Path);//当然实例化可以是别的流的对象。
BufferedInputStream bis = new BufferedInputStream(is);

-----------------------------------------------------------------
//装饰模式的写法 ~~俄罗斯套娃?~~ 
BufferedInputStream bis = new BufferedInputStream(FileInputStream(File对象或者文件Path));

输出流例子:

代码语言:javascript
复制
OutputStream os = new FileOutputStream(File对象或者文件Path);//当然实例化可以是别的流的对象。
BufferedOutputStream bos = new BufferedOutputStream(os);
-----------------------------------------------------------------
//装饰模式的写法 ~~俄罗斯套娃?~~ 
BufferedOutputStream bos = new BufferedOutputStream(FileOutputStream(File对象或者文件Path));

Ⅳ转换流

作用:实现字节流和字符流之间的转换。

1、OutputStreamWriter,将字节输出流转换为字符输出流。

API方法: 1、flush():刷新该流的缓冲 2、close():关闭此流,关闭前需要刷新 3、getEncoding():获取此流使用的字符编码的名称。 4、write():write(char[] ,int offset,int length),写入字符数组的某一部分 write(String ,int offset ,int length),写入字符串的某一部分 write(String ),写入单个字符。

2、InputStreamReader,将字节输入流转换为字符输入流。

API方法: 1、close():关闭此流 2、getEncoding():获取此流使用的字符编码的名称 3、ready():判断此流是否已经准备好用于读取 4、read():read(),读取单个字符。 5、read(char[],int offset ,int length),将字符读入数组的某一部分。

代码语言:javascript
复制
public class ConvertTest {
    public static void main(String[] args) {
        //操作System.in 和System.out
        try(BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter writer =new BufferedWriter(new OutputStreamWriter(System.out));){
            //循环获取键盘的输入(exit退出),输出此内容
            String msg ="";
            while(!msg.equals("exit")) {
                msg = reader.readLine(); //循环读取
                writer.write(msg); //循环写出
                writer.newLine();
                writer.flush(); //强制刷新
            }
        }catch(IOException e) {
            System.out.println("操作异常");
        }       
    }
}

Ⅴ数据流

DataOutputStream的API方法: 1、void writeByte(int v) 将一个 byte 值以 1-byte 值形式写出到基础输出流中。 2、void writeShort(int v) 将一个 short 值以 2-byte 值形式写入基础输出流中,先写入高字节。 3、void writeInt(int v)将一个 int 值以 4-byte 值形式写入基础输出流中,先写入高字节。 4、void writeLong(long v) 将一个 long 值以 8-byte 值形式写入基础输出流中,先写入高字节。 5、void writeFloat(float v) 使用 Float 类中的 floatToIntBits 方法将 float 参数转换为一个 int 值, 然后将该 int 值以 4-byte 值形式写入基础输出流中,先写入高字节。 6、void writeDouble(double v) 使用 Double 类中的 doubleToLongBits 方法将 double 参数转换为一个 long 值, 然后将该 long 值以 8-byte 值形式写入基础输出流中,先写入高字节。 7、void writeChar(int v) 将一个 char 值以 2-byte 值形式写入基础输出流中,先写入高字节。 8、void writeBoolean(boolean v) 将一个 boolean 值以 1-byte 值形式写入基础输出流。 9、void flush() //清空此数据输出流。写入文件 10、int size() //返回计数器 written 的当前值,即到目前为止写入此数据输出流的字节数。 1、 byte readByte() ;//读取并返回一个输入字节。该字节被看作是 -128 到 127(包含)范围内的一个有符号值。 2、 int readInt() ;//读取四个输入字节并返回一个 int 值。 3、 short readShort() ;//参见 DataInput 的 readShort 方法的常规协定。 4、 long readLong() ;//读取八个输入字节并返回一个 long 值。 5、 float readFloat() ;//读取四个输入字节并返回一个 float 值。 6、 double readDouble() ;//读取八个输入字节并返回一个 double 值。实现这一点的方法是:先使用与 readlong 方法完全相同的方式构造一个 long 值, 然后使用与 Double.longBitsToDouble 方法完全相同的方式将此 long 值转换成一个 double 值。 7、 boolean readBoolean() ;//读取一个输入字节,如果该字节不是零,则返回 true,如果是零,则返回 false。 8、 char readChar() ;//读取两个输入字节并返回一个 char 值. 9、 String readUTF();//读入一个已使用 UTF-8 修改版格式编码的字符串.

代码语言:javascript
复制
public class DataTest {

    public static void main(String[] args) throws IOException {
        //写出
        ByteArrayOutputStream baos =new ByteArrayOutputStream();
        DataOutputStream dos =new DataOutputStream(new BufferedOutputStream(baos));
        //操作数据类型 +数据
        dos.writeUTF("Himit_ZH");
        dos.writeInt(520);
        dos.writeBoolean(false);
        dos.writeChar('q');
        dos.flush();
        byte[] datas =baos.toByteArray();
        System.out.println(datas.length);
        //读取
        DataInputStream dis =new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));
        //顺序与写出一致
        String msg = dis.readUTF(); 
        int age = dis.readInt();
        boolean flag = dis.readBoolean();
        char ch = dis.readChar();
        System.out.println(flag);
    }

}

Ⅵ对象流(序列化和反序列化)

对象流将一个序列化的对象保存到硬盘中,或者硬盘中读取一个对象。对象流的存储和读取包含以下几点内容: 1、所保存的对象必须实现Serializable接口。 2、 所保存的对象的属性也必须实现Serializable接口。 3、 最好要给该对象提供一个版本号,private static final long serialVersionId。

代码语言:javascript
复制
public class ObjectTest {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //写出 -->序列化
        ByteArrayOutputStream baos =new ByteArrayOutputStream();
        ObjectOutputStream oos =new ObjectOutputStream(new BufferedOutputStream(baos));
        //操作数据类型 +数据
        oos.writeUTF("Himit_ZH真帅");
        oos.writeInt(520);
        oos.writeBoolean(false);
        oos.writeChar('a');
        //对象
        oos.writeObject("Python是最好用的");
        oos.writeObject(new Date());
        Employee emp =new Employee("马云",400);
        oos.writeObject(emp);
        oos.flush();
        oos.close();
        byte[] datas =baos.toByteArray();
        System.out.println(datas.length);
        //读取 -->反序列化
        ObjectInputStream ois =new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));
        //顺序与写出一致
        String msg = ois.readUTF(); 
        int age = ois.readInt();
        boolean flag = ois.readBoolean();
        char ch = ois.readChar();
        System.out.println(flag);
        //对象的数据还原  
        Object str = ois.readObject();
        Object date = ois.readObject();
        Object employee = ois.readObject();
        //对应类型进行转型
        if(str instanceof String) {
            String strObj = (String) str;
            System.out.println(strObj);
        }
        if(date instanceof Date) {
            Date dateObj = (Date) date;
            System.out.println(dateObj);
        }
        if(employee instanceof Employee) {
            Employee empObj = (Employee) employee;
            System.out.println(empObj.getName()+"-->"+empObj.getSalary());
        }
        ois.close();

    }

}
//javabean 封装数据
class Employee implements java.io.Serializable{ //必须实现Serializable接口才能序列化
    private transient String name; //transient 该数据不需要序列化
    private double salary;
    public Employee() {
    }
    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getSalary() {
        return salary;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }

}

Ⅶ打印流(PrintStream)

类的继承:

代码语言:javascript
复制
          java.lang.Object
                   |-java.io.OutputStream
                            |-java.io.FilterOutputStream
                                   |-java.io.PrintStream

PrintStream 是用来装饰其它输出流。它能为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。 与其他输出流不同,PrintStream 永远不会抛出 IOException;它产生的IOException会被自身的函数所捕获并设置错误标记, 用户可以通过 checkError() 返回错误标记,从而查看PrintStream内部是否产生了IOException。 另外,PrintStream 提供了自动flush 和 字符集设置功能。所谓自动flush,就是往PrintStream写入的数据会立刻调用flush()函数。 API方法: PrintStream​(File file) 使用指定的文件创建没有自动行刷新的新打印流。 PrintStream​(File file, String csn) 使用指定的文件和字符集创建一个没有自动行刷新的新打印流。 PrintStream​(File file, Charset charset) 使用指定的文件和字符集创建一个没有自动行刷新的新打印流。 PrintStream​(OutputStream out) 创建新的打印流。 PrintStream​(OutputStream out, boolean autoFlush) 创建新的打印流。 PrintStream​(OutputStream out, boolean autoFlush, String encoding) 创建新的打印流。 PrintStream​(OutputStream out, boolean autoFlush, Charset charset) 创建一个新的打印流,具有指定的OutputStream,自动行刷新和字符集。 PrintStream​(String fileName) 使用指定的文件名创建没有自动行刷新的新打印流。 PrintStream​(String fileName, String csn) 使用指定的文件名和字符集创建一个没有自动行刷新的新打印流。 PrintStream​(String fileName, Charset charset) 使用指定的文件名和字符集创建一个没有自动行刷新的新打印流。 PrintStream append​(char c) 将指定的字符追加到此输出流。 void close() 关闭流. void flush() 刷新流.

代码语言:javascript
复制
public class Demo {
    public static void main(String[] args) throws Exception {// 此处直接抛出错误
        PrintStream ps = new PrintStream(new FileOutputStream(文件path或者File对象));
        //写出到对应文件,基本数据类型,String..
        ps.println("Himit_ZH");
        ps.print('c');
        ps.println(11.6 + 11.89); //多了换行
        ps.print(10 + 30);
        ps.write(byte[] buffer, int offset, int length);
        ps.flush();
        ps.close();
    }

}

Ⅷ附加:随机访问流

RandomAccessFile类:是Object的子类,此类的实例支持对随机访问文件的读取和写入。随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组。存在指向该隐含数组的光标或索引,称为文件指针;输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。如果随机访问文件以读取/写入模式创建,则输出操作也可用;输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。写入隐含数组的当前末尾之后的输出操作导致该数组扩展。该文件指针可以通过 getFilePointer 方法读取,并通过 seek 方法设置。

用途:例如迅雷的下载资源是随机下载资源的各个部分,而不是从头到尾。有些视频下载也是随机资源块下载。

代码语言:javascript
复制
public class RandTest {

    public static void main(String[] args) throws IOException {
        //分多少块
        File src = new File("p.png");
        //总长度
        long len = src.length();
        //每块大小
        int blockSize =1024;
        //块数: 多少块
        int size =(int) Math.ceil(len*1.0/blockSize);
        System.out.println(size);

        //起始位置和实际大小
        int beginPos = 0;
        int actualSize = (int)(blockSize>len?len:blockSize); 
        for(int i=0;i<size;i++) {
            beginPos = i*blockSize;
            if(i==size-1) { //最后一块
                actualSize = (int)len;
            }else {
                actualSize = blockSize;
                len -=actualSize; //剩余量
            }
            System.out.println(i+"-->"+beginPos +"-->"+actualSize);
            split(i,beginPos,actualSize);
        }

    }
    /**
     * 指定第i块的起始位置 和实际长度
     * @param i
     * @param beginPos
     * @param actualSize
     * @throws IOException
     */
    public static void split(int i,int beginPos,int actualSize ) throws IOException {
        RandomAccessFile raf =new RandomAccessFile(new File("p.png"),"r");
        RandomAccessFile raf2 =new RandomAccessFile(new File("dest/"+i+"p.png"),"rw");
        //随机读取 
        raf.seek(beginPos);
        //读取
        //3、操作 (分段读取)
        byte[] flush = new byte[1024]; //缓冲容器
        int len = -1; //接收长度
        while((len=raf.read(flush))!=-1) {
            if(actualSize>len) { //获取本次读取的所有内容
                raf2.write(flush, 0, len);
                actualSize -=len;
            }else { 
                raf2.write(flush, 0, actualSize);
                break;
            }
        }
        raf2.close();
        raf.close();
    }

}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020年4月9日 2,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 基础概念
  • IO流分类导向图
  • 何为输入流和输出流?
  • 各种字节流的用法
    • ①节点流
      • ②处理流
        • ③流的使用一般步骤
          • ④流的关闭
          • 具体用法
            • Ⅰ文件流
              • Ⅱ字节数组流
                • Ⅲ缓冲流
                  • Ⅳ转换流
                    • Ⅴ数据流
                      • Ⅵ对象流(序列化和反序列化)
                        • Ⅶ打印流(PrintStream)
                          • Ⅷ附加:随机访问流
                          相关产品与服务
                          文件存储
                          文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档