Web全栈~26.IO

上一期

Java处理文件的常见方法

IO流

在Java中,文件是视为输入输出(Input/Output , IO)设备的一种。Java使用基本统一的概念来处理所有的IO,包括键盘、显示终端、网络等。这个统一的概念称之为流。而流则有输入流和输出流之分。

输入流就是可以从中获取数据,输入流的实际提供者可以是键盘、文件、网络等。输出流就是可以向其中写入数据,输出流的实际目的地也可以是显示终端、文件、网络等。所以在我们后来学习Socket编程的时候就会经常的使用IO流。

Java的IO的基本类大多位于包java.io中。类InputStream表示输入流,OutputStream表示输出流,而FileInputStream表示文件输入流,FileOutputStream表示文件输出流。那么,有了流的概念,就也有了很多面向流的代码,比如说对流做加密、压缩、计算信息摘要、以及计算检验和等,这些代码呢接受的参数和返回的结果都是抽象的流,它们构成了一个协作体系,这类似于之前介绍的接口概念、面向接口的编程,以及容器类协作体系。一些实际上不是IO的数据源和目的地也转换为了流,以方便参与这种协作,比如字节数组,也包装为了流ByteArrayInputStream和ByteArrayOutputStream。

装饰器设计模式

基本的流按字节读写,没有缓冲区,这不方便使用。Java解决这个问题的方法是使用装饰器设计模式。在Java中也有很多的装饰类,有两个基类:过滤器输入流FileInputStream和过滤器输出流FileOutputStream。过滤器其实并没有改变流的本质,只是在流的基础上增加了些功能。

BufferedInputStream和BufferedOutputStream对流起缓冲装饰。

DataInputStream和DataOutput-Stream。可以按8种基本类型和字符串对流进行读写

GZIPInputStream、ZipInputStream、GZIPOutput-Stream和ZipOutputStream。可以对流进行压缩和解压缩

PrintStream可以将基本类型、对象输出为其字符串表示。

Reader/Writer

以InputStream/OutputStream为基类的流基本都是以二进制形式处理数据的,不能够方便地处理文本文件,没有编码的概念,能够方便地按字符处理文本数据的基类是Reader和Writer

FileReader和FileWriter读写文件。

BufferedReader和BufferedWriter起缓冲装饰

CharArrayReader和CharArrayWriter将字符数组包装为Reader/Writer

StringReader和StringWriter将字符串包装为Reader/Writer

InputStreamReader和OutputStreamWriter将InputStream/OutputStream转换为Reader/Writer。

PrintWriter将基本类型、对象输出为其字符串表示

序列化和反序列化

简单来说,序列化就是将内存中的Java对象持久保存到一个流中,反序列化就是从流中恢复Java对象到内存。序列化和反序列化主要有两个用处:一是对象状态持久化,二是网络远程调用,用于传递和返回对象。

Java主要通过接口Serializable和类ObjectInputStream/ObjectOutputStream提供对序列化的支持,基本的使用是比较简单的,但也有一些复杂的地方。不过,Java的默认序列化有一些缺点,比如,序列化后的形式比较大、浪费空间,序列化/反序列化的性能也比较低,更重要的问题是,它是Java特有的技术,不能与其他语言交互。

二进制文件和字节流

InputStream/OutputStream

IO流的基类,抽象类

FileInputStream/FileOutputStream

输入源和输出目标是文件的流

ByteArrayInputStream/ByteArrayOutputStream

输入源和输出目标是字节数组的流

DataInputStream/DataOutputStream

装饰类,按基本类型和字符串而非只是字节读写流

BufferedInputStream/BufferedOutputStream

装饰类,对输入输出流提供缓冲功能。

InputStream/OutputStream

InputStream

public int read(byte b[]) throws IOException

读入的字节放入参数数组b中,第一个字节存入b[0],第二个存入b[1],以此类推,一次最多读入的字节个数为数组b的长度,但实际读入的个数可能小于数组长度,返回值为实际读入的字节个数。如果刚开始读取时已到流结尾,则返回-1;否则,只要数组长度大于0,该方法都会尽力至少读取一个字节,如果流中一个字节都没有,它会阻塞,异常出现时也是抛出IOException。该方法不是抽象方法,InputStream有一个默认实现,主要就是循环调用读一个字节的read方法,但子类如FileInputStream往往会提供更为高效的实现。

批量读取还有一个更为通用的重载方法
public int read(byte b[],int off,int len) throws IOException

读入的第一个字节放入b[off],最多读取len个字节,read(byte b[])就是调用了该方法。流读取结束后,应该关闭,以释放相关资源。不管read方法是否抛出了异常,都应该调用close方法,所以close方法通常应该放在finally语句内。close方法自己可能也会抛出IOException,但通常可以捕获并忽略。

OutputStream

public abstract void write(int b) throws IOException

向流中写入一个字节,参数类型虽然是int,但其实只会用到最低的8位。这个方法是抽象方法,具体子类必须实现,FileInputStream会调用本地方法。

public void write(byte b[]) throws IOException
public void write(byte b[],int off,int len) throws IOException

在第二个方法中,第一个写入的字节是b[off],写入个数为len,最后一个是b[off+len-1],第一个方法等同于调用write(b,0,b.length);。OutputStream的默认实现是循环调用单字节的write()方法,子类往往有更为高效的实现,FileOutpuStream会调用对应的批量写本地方法。

public void flush() throws IOException
public void close() throws IOException

flush方法将缓冲而未实际写的数据进行实际写入,比如,在BufferedOutputStream中,调用flush方法会将其缓冲区的内容写到其装饰的流中,并调用该流的flush方法。基类OutputStream没有缓冲,flush方法代码为空。

可能会认为,调用flush方法会强制确保数据保存到硬盘上,但实际上不是这样,FileOutputStream没有缓冲,没有重写flush方法,调用flush方法没有任何效果,数据只是传递给了操作系统,但操作系统什么时候保存到硬盘上,这是不一定的。要确保数据保存到了硬盘上,可以调用FileOutputStream中的特有方法

close方法一般会首先调用flush方法,然后再释放流占用的系统资源。同InputStream一样,close方法一般应该放在finally语句内。

FileInputStream/FileOutputStream

FileOutputStream

public FileOutputStream(File file,boolean append)throws FileNotFoundException
public FileOutputStream(String name) throws FileNotFoundException

File类型的参数file和字符串的类型的参数name都表示文件路径,路径可以是绝对路径,也可以是相对路径,如果文件已存在,append参数指定是追加还是覆盖,true表示追加,false表示覆盖,第二个构造方法没有append参数,表示覆盖。new一个FileOutputStream对象会实际打开文件,操作系统会分配相关资源。如果当前用户没有写权限,会抛出异常SecurityException,它是一种RuntimeException。如果指定的文件是一个已存在的目录,或者由于其他原因不能打开文件,会抛出异常FileNotFoundException,它是IOException的一个子类。

代码示例
public class Test {public static void main(String[] args) throws IOException {OutputStream outputStream = new FileOutputStream("G:/HTML/Java/fileTest/alvin.txt");try{String data = "hello world java";byte[] bytes = data.getBytes(Charset.forName("UTF-8"));outputStream.write(bytes);}finally {outputStream.close();}}
}

OutputStream只能以byte或byte数组写文件,为了写字符串,我们调用String的get-Bytes方法得到它的UTF-8编码的字节数组,再调用write()方法,写的过程放在try语句内,在finally语句中调用close方法。

FileInputStream

public FileInputStream(String name) throws FileNotFoundException
public FileInputStream(File file) throws FileNotFoundException

参数与FileOutputStream类似,可以是文件路径或File对象,但必须是一个已存在的文件,不能是目录。new一个FileInputStream对象也会实际打开文件,操作系统会分配相关资源,如果文件不存在,会抛出异常FileNotFoundException,如果当前用户没有读的权限,会抛出异常SecurityException。

public static void main(String[] args) throws IOException {InputStream inputStream = new FileInputStream("G:/HTML/Java/fileTest/alvin.txt");try{byte[]buf = new byte[1024];int n = inputStream.read(buf);String data = new String(buf,0,n,"UTF-8");System.out.println(data);}finally {inputStream.close();}
}

读入到的是byte数组,我们使用String的带编码参数的构造方法将其转换为了String。这段代码假定一次read调用就读到了所有内容,且假定字节长度不超过1024。为了确保读到所有内容,可以逐个字节读取直到文件结束

public static void main(String[] args) throws IOException {InputStream inputStream = new FileInputStream("G:/HTML/Java/fileTest/alvin.txt");try{byte[]buf = new byte[1024];int n = -1;int index = 0;while((n = inputStream.read()) != -1){buf[index++] = (byte)n;}}finally {inputStream.close();}
}

在没有缓冲的情况下逐个字节读取性能很低,可以使用批量读入且确保读到结尾

public static void main(String[] args) throws IOException {InputStream inputStream = new FileInputStream("G:/HTML/Java/fileTest/alvin.txt");try{byte[]buf = new byte[1024];int off = 0;int n = 0;while((n = inputStream.read(buf,off,1024-off))!= -1){off += n;}String data = new String(buf,0,off,"UTF-8");System.out.println(data);}finally {inputStream.close();}}

ByteArrayInputStream/ByteArrayOutputStream

这里输入源和输出目标都是字节数组

ByteArrayOutputStream

ByteArrayOutputStream的输出目标是一个byte数组,这个数组的长度是根据数据内容动态扩展的。

public ByteArrayOutputStream()
public ByteArrayOutputStream(int size)

第二个构造方法中的size指定的就是初始的数组大小,如果没有指定,则长度为32。在调用write方法的过程中,如果数组大小不够,会进行扩展,扩展策略同样是指数扩展,每次至少增加一倍。

ByteArrayOutputStream有如下方法,可以方便地将数据转换为字节数组或字符串

public synchronized byte[]toByteArray()
/**
toString()方法使用系统默认编码。
ByteArrayOutputStream中的数据也可以方便地写到另一个OutputStream:
**/
public synchronized String toString()
public synchronized String toString(String charsetName)
public synchronized void writeTo(OutputStream out) throws IOException

size方法返回当前写入的字节个数。reset方法重置字节个数为0,reset后,可以重用已分配的数组。

public synchronized int size()
public synchronized void reset()

使用ByteArrayOutputStream,我们可以改进前面的读文件代码,确保将所有文件内容读入

    public static void main(String[] args) throws IOException {InputStream inputStream = new FileInputStream("G:/HTML/Java/fileTest/alvin.txt");try{ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();byte[] buf = new byte[1024];int n = 0;while((n = inputStream.read(buf)) != -1){byteArrayOutputStream.write(buf,0,n);}String data = byteArrayOutputStream.toString();System.out.println(data);}finally {inputStream.close();}}

读入的数据先写入ByteArrayOutputStream中,读完后,再调用其toString方法获取完整数据。

ByteArrayInputStream

ByteArrayInputStream将byte数组包装为一个输入流,是一种适配器模式

public ByteArrayInputStream(byte buf[])
public ByteArrayInputStream(byte buf[],int offset,int length)

构造方法以buf中offset开始的length个字节为背后的数据。ByteArrayInput-Stream的所有数据都在内存,支持mark/reset重复读取。

DataInputStream/DataOutputStream

DataOutputStream

DataOutputStream是装饰类基类FilterOutputStream的子类,FilterOutputStream是Output-Stream的子类。它接受一个已有的OutputStream,基本上将所有操作都代理给了它。DataOutputStream实现了DataOutput接口,可以以各种基本类型和字符串写入数据

void writeBoolean(boolean v) throws IOException;
void writeInt(int v) throws IOException;
void writeUTF(String s) throws IOException;

writeBoolean:写入一个字节,如果值为true,则写入1,否则0。

writeInt:写入4个字节,最高位字节先写入,最低位最后写入。

writeUTF:将字符串的UTF-8编码字节写入,这个编码格式与标准的UTF-8编码略有不同,不过,我们不用关心这个细节。

与FilterOutputStream一样,DataOutputStream的构造方法也是接受一个已有的Output-Stream

public DataOutputStream(OutputStream out)
代码案例(写)
class Student{private int son;private String name;private Double score;public Student(int son, String name, Double score) {this.son = son;this.name = name;this.score = score;}public int getSon() {return son;}public String getName() {return name;}public Double getScore() {return score;}
}
public class Test {public static void main(String[] args) throws IOException {List<Student> list = new ArrayList<>();Student A = new Student(1,"alvin",77.5);Student B = new Student(2,"bob",88.5);Student C = new Student(3,"clear",90.0);list.add(A); list.add(B); list.add(C);DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("G:/HTML/Java/fileTest/alvin.txt"));try{dataOutputStream.writeInt(list.size());for(Student s : list){dataOutputStream.writeInt(s.getSon());dataOutputStream.writeUTF(s.getName());dataOutputStream.writeDouble(s.getSon());}}finally {dataOutputStream.close();}}
}

DataInputStream

DataInputStream是装饰类基类FilterInputStream的子类,FilterInputStream是Input-Stream的子类。DataInputStream实现了DataInput接口,可以以各种基本类型和字符串读取数据

boolean readBoolean() throws IOException;
int readInt() throws IOException;
String readUTF() throws IOException;

在读取时,DataInputStream会先按字节读进来,然后转换为对应的类型。

代码案例(读)
public static void main(String[] args) throws IOException {DataInputStream dataInputStream = new DataInputStream(new FileInputStream("G:/HTML/Java/fileTest/alvin.txt"));try{int size = dataInputStream.readInt();List<Student> list = new ArrayList<>();for(int i = 0; i < size; i++){Student student = new Student(dataInputStream.readInt(),dataInputStream.readUTF(),dataInputStream.readDouble());list.add(student);}System.out.println(list.toString());}finally {dataInputStream.close();}}

BufferedInputStream/BufferedOutputStream

FileInputStream/FileOutputStream是没有缓冲的,按单个字节读写时性能比较低,虽然可以按字节数组读取以提高性能,但有时必须要按字节读写,怎么解决这个问题呢?方法是将文件流包装到缓冲流中。BufferedInputStream内部有个字节数组作为缓冲区,读取时,先从这个缓冲区读,缓冲区读完了再调用包装的流读

public BufferedInputStream(InputStream in)
public BufferedInputStream(InputStream in,int size)

size表示缓冲区大小,如果没有,默认值为8192。除了提高性能,BufferedInputStream也支持mark/reset,可以重复读取。与BufferedInputStream类似,BufferedOutputStream的构造方法也有两个,默认的缓冲区大小也是8192,它的flush方法会将缓冲区的内容写到包装的流中。在使用FileInputStream/FileOutputStream时,应该几乎总是在它的外面包上对应的缓冲类

Web全栈~26.IO相关推荐

  1. Web全栈 第九周-曾老师-专题视频课程

    Web全栈 第九周-303人已学习 课程介绍         Web 前端工程师课程 第9周 https://edu.csdn.net/topic/web115 课程收益     Web 前端工程师课 ...

  2. php web教程视频教程下载,Web全栈 PHP+React系列视频教程下载

    Web全栈 PHP+React系列视频教程下载 课程介绍: 此套Web全栈 PHP+React系列视频教程覆盖PHP.前端和区块链应用开发三大热门职位,教程对网络基础.前端基础(HTML CSSJav ...

  3. Web全栈工程师修养

    全栈工程师现在是个很热的话题,如何定义全栈工程师?在著名的问答网站Quora上有人提出了这个问题,其中一个获得了高票的回答是: 全栈工程师是指,一个能处理数据库.服务器.系统工程和客户端的所有工作的工 ...

  4. web全栈-第一讲:web开发基本知识点

    移动互联网时代越来越凸显前端技术重要性! 最近在学习万门教育的WEB全栈工程师零基础特训班课程. 第一讲 基本知识点 以京东网站讲解网站案例. 看网站页面先看布局,了解共性.从大处着眼.小处着手. 整 ...

  5. Web全栈~25.文件

    Web全栈~25.文件 上一期 前言 Web全栈系列博客到了文件这里已经逐步的走向重点.前面的内容之所以很多都不全面,其实根本原因还是自己太懒...当然对于一些细节日后会在其他系列的博客去说 事实上我 ...

  6. Web全栈~28.网络编程

    Web全栈~28.网络编程 上一期 前言 网络编程往往都是困难.复杂,而且极易出错的.程序员必须掌握与网络有关的大量细节,有时甚至要对硬件有深刻的认识.一般地,我们需要理解网络协议中不同的" ...

  7. 开课吧mysql课件百度云_开课吧web全栈十二期|百度云|天翼云下载

    目录:/Web全栈架构师第12期 [93.1G] ┣━━阶段10:webpack [6.2G] ┃ ┣━━10-1 01课 webpack实战 (2019.11.21).mp4 [1.7G] ┃ ┣━ ...

  8. 《Web全栈工程师的自我修养》读书笔记

    <Web全栈工程师的自我修养>读书笔记 [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://w ...

  9. 小程序高级电商前端第1周走进Web全栈工程师<三>----首页Banner、分类六宫格

    继续接着上一次小程序高级电商前端第1周走进Web全栈工程师<二>的首页逻辑往下演示,上一次对于首页的主题已经处理完了: 接下来则来处理Banner: 接下来则来实现一下它. 获取Banne ...

最新文章

  1. Httpclient学习日记(一)
  2. Smack Component 多线程环境下的问题解决
  3. html编写个人博客_Django 开发简易博客网站
  4. 关于搭建php电商环境时缺少fileinfo、数据库安装出错问题解决办法
  5. FIR特性及仿真实现_01
  6. PhpStorm 配置debug断点调试
  7. 鸿蒙开发版智慧生活,华为发布全新分布式鸿蒙OS,打造全场景智慧生活新体验...
  8. 【英语学习】【WOTD】katzenjammer 释义/词源/示例
  9. 【转账】API自动化测试
  10. mysql innodb数据库引擎_mysql的innodb数据库引擎详解
  11. 线性表(单链表)—图书管理系统 c语言版
  12. html5图片无限循环播放,原生js实现无限循环轮播图效果
  13. iPhone12与mate40,你pick哪一款?
  14. Tensorflow让神经网络自动创造音乐
  15. CFS线程调度机制分析
  16. 【JavaScript】
  17. maya多边形建模怎样做曲面_maya将曲面模型转换成多边形模型
  18. 使用 chkdsk 命令修复 SD 卡
  19. unity制作交互视频/互动视频
  20. 澳洲找IT工作最好用的12个网站, 找实习,找兼职,宝藏网站值得收藏 澳洲求职 Australia Jobs Websites

热门文章

  1. SLIC——代码、改进
  2. vant 固定定位不生效
  3. 洛谷P4942 小凯的数字
  4. PNETlab最新版介绍与下载
  5. 单例模式之饿汉式单例模式
  6. ASP.NET Core开发-后台任务利器Hangfire使用
  7. 去除字符串中所有的空格
  8. python无法调用urlopen_python urlopen 不了一个url 而用curl 是可以的
  9. 【BAT 面试题宝库附详尽答案解析】分布式事务实现原理
  10. 微商城模式适合什么规模企业?新商云,满足多种行业需求!