本章主要参考和摘自疯狂java讲义上面的(java编程思想的后面看过后有新的内容再补充进去吧)。
  输入输出是所有程序都必需的部分————使用输入机制允许程序读取外部数据(包括磁盘、光盘等存储设备上的数据和用户输入的数据)、使用输出机制允许程序记录运行状态,将程序数据输出到外部(磁盘、光盘等存储设备当中和控制台当中)。输入输出是从程序运行所在的内存的角度而言的。
  Java的IO起初仅通过java.io包下的类和接口来支持,但在java7中在java.nio及其子包下新增了关于IO的api,被称为NIO2。
  在java.io包下主要包括输入、输出两种IO流,每种输入输出流又可分为字节流字符流两大类。其中字节流以字节为单位来处理输入、输出操作,而字符流以字符来处理输入、输出操作。
  字节流和字符流的分法是基于类型区别上的,但另一方面为了方便理解,可以从功能上将IO流可分为底层结点流和上层处理流,其中结点流用于和数据源直接关联(但不同的数据源关联结点流的方式可能存在一定的差异,这里的数据源可以是文件、内存(字符串、数组等)、线程(管道通信)、网络(套接字通信中用到的)等)处理流则可以对现有的流进行包装,从而允许程序使用统一的输入、输出代码来读取不同的物理存储结点的资源。
  注意:处理流这个概念是同时包含了装饰者和适配器的。装饰者必须必须能取代被装饰者,所以装饰者与被装饰者必须是同一类型,也就是说二者有共同的父类或者实现了共同的接口,大部分的处理流是属于装饰者。但也有着例外存在,如转换流及其子类(InputStreamReader和OutputStreamWriter),他们是将字节流转换成了字符流,采用的是一种适配器模式,其本身并没有增加或者增强任和功能。

一、File类

  java.io.File类可以代表文件目录,如果希望在程序中操作文件和目录,都可以通过File类来完成。这里的操作比如可以是:新建、删除、重命名文件和目录。但File不能访问文件本身,要访问文件的内容还是需要使用输入输出流。

public class FileTest
{public static void main(String[] args)throws IOException{// 以当前路径来创建一个File对象File file = new File("."); // 直接获取文件名,输出一点System.out.println(file.getName());// 获取相对路径的父路径可能出错,下面代码输出nullSystem.out.println(file.getParent());// 获取绝对路径System.out.println(file.getAbsoluteFile());// 获取上一级路径System.out.println(file.getAbsoluteFile().getParent());// 在当前路径下创建一个临时文件File tmpFile = File.createTempFile("aaa", ".txt", file);// 指定当JVM退出时删除该文件tmpFile.deleteOnExit();// 以系统当前时间作为新文件名来创建新文件File newFile = new File(System.currentTimeMillis() + "");System.out.println("newFile对象是否存在:" + newFile.exists());// 以指定newFile对象来创建一个文件newFile.createNewFile();// 以newFile对象来创建一个目录,因为newFile已经存在,// 所以下面方法返回false,即无法创建该目录newFile.mkdir();// 使用list()方法来列出当前路径下的所有文件和路径String[] fileList = file.list();System.out.println("====当前路径下所有文件和路径如下====");for (String fileName : fileList){System.out.println(fileName);}// listRoots()静态方法列出所有的磁盘根路径。File[] roots = File.listRoots();System.out.println("====系统所有根路径如下====");for (File root : roots){System.out.println(root);}}
}

1.访问文件和目录

访问文件名相关的方法

文件检测相关的方法


获取常规文件信息

文件操作相关方法

目录操作相关方法


注意:Windows的路径分割符使用反斜线(),而Java程序当中的反斜线表示转义字符,要表示反斜线的话则需要使用两条反斜线(如"F:\abc\test.txt")或者直接使用斜线(/)也可以,java支持将斜线当成与平台无关的路径分隔符。

2.文件过滤器

  在File类的list(FilenameFilter filter)方法中可以接受一个FilenameFilter参数(函数式接口),通过该参数可以只列出符合条件的文件。FilenameFilter这一函数式接口中的方法是一个accept(File dir,String name)方法,该方法将对指定的File即dir(在list方法中传入的是this即下面代码中的file)的所有子目录进行的迭代。
  list(FilenameFilter filter)会循环调用accept,每次传入this和this所在目录中不同的文件或者子目录名,如果这个accept方法返回true.那么list方法会列出该该子目录或者该文件。

public class FilenameFilterTest
{public static void main(String[] args) {File file = new File(".");//.代表当前project的目录//如果文件名以.java结尾,或者文件对应一个路径,则返回trueString[] nameList = file.list((dir,name)->name.endWith(".java")||new File(name).isDirectory);for(String name : nameList){System.out.println(name);}}
}

二、理解Java的IO流

流的分类

1.输入流和输出流

按照流的流向来分,可以分为输入流和输出流。输入输出是从程序运行所在的内存的角度而言的。

  • 输入流:只能从中读取数据,而不能向其中写入数据。
  • 输出流:只能向其写入数据,而不能从中读取数据。


Java的输入流主要由InputStream和Reader作为基类,而输出流主要由OutputStream和Writer作为基类。他们都是一些抽象基类,无法直接创建实例。

2.字节流和字符流

  字节流和字符流的用法几乎完全一样,区别于字节流和字符流所操作的数据单元不同————字节流操作的数据单元是8位的字节,而字符流操作的数据单元是16位的字符。
  字节流主要由InputStream和OutputStream作为基类,而字符流则主要由Reader和Writer作为基类。

3.节点流和处理流

按照流的角色来分

  • 可以从/向一个特定的IO设备(如磁盘、网络)读/写数据的流,称为节点流,节点流也被称为低级流。
  • 处理流则用于对一个已存在的流进行连接或者封装,通过封装后的流实现数据读/写功能。处理流也被称为高级流。其实采用的是一种装饰者的设计模式。

流的概念模型




  从图中可以看到,对于输入/输出流中字符流和字节流处理方式是相似的,区别在于处理的输入/输出单位(即水滴)不同而已
  输入流使用隐式的记录指针来表示当前正准备从哪个“水滴”开始读取,每当程序从InoutStream或Writer中取出一个或者多个水滴后,记录指针向后(右)移动;除此外,InputStream和Reader里都提供一些方法来控制记录指针的移动。
  对于图中的输出流而言,当执行输出时,程序相当于依次把“水滴”放入到输出流的水管中,输出流同样采取隐式的记录指针来标识当前水滴即将放入的位置,每当程序向OutputStream或Writer输出一个或者多个水滴后,记录指针自动向后(右)移动。
  处理流的功能主要体现在以下两个方面:

  • 性能的提高:主要以增加缓冲的方式来提高输入/输出的效率。
  • 操作的便捷:处理流可能提供了一系列便捷的方法来一次输入/输出大批量的内容,而不是输入/输出一个或者多个水滴。

处理流可以嫁接在任何已存在的流(包括处理流)的基础之上,这就允许Java应用程序采用相同的代码、透明的方式来访问不同的输入/输出设备的数据流。

三、字节流和字符流

  正如上面所说,字符流和字节流的操作方式几乎完全一样,区别只在于操作的数据单元不同而已————字节流操作的数据单元是字节,字符流操作的数据单元是字符。

InputStream和Reader

  InputStream和Reader是所有输入流的抽象基类,本身并不能创建实例来执行输入,但它们是所有输入流的模板,定义的方法是所有输入流都可以使用的方法。



  对比可以发现:两个基类的功能基本是一样的

  下面以InputStream和Reader的子类FileInputStream和FileReader来举例,它们都是节点流————会直接和指定文件相关联。
  使用数组其实就是和我们平时用水杯而不是用小酒盅去饮水机接水一样,用酒盅每次只能先打开水龙头接一滴,然后关上水龙头,然后喝完了再去打开水龙头去接下一滴。而用水杯就可以一次打开水龙头之后,接很多很多滴,再关上水龙头,喝完之后再去接水。可以看出使用水杯减少了喝水的次数的开关水龙头的次数,其实就是减少了读取的次数。

public class FileInputStreamTest
{public static void main(String[] args) throws IOException{// 创建字节输入流FileInputStream fis = new FileInputStream("src\\IO系统\\FileInputStreamTest.java");byte[] bbuf = new byte[1024];//创建一个长度为1024的“竹筒”int hasRead = 0;//记录每次实际读取的字节数// 使用循环来重复“取水”过程while ((hasRead = fis.read(bbuf)) > 0 ){// 取出“竹筒”中水滴(字节),将字节数组转换成字符串输入!System.out.print(new String(bbuf , 0 , hasRead ));}fis.close();// 关闭文件输入流,放在finally块里更安全}
}

注意:在创建较小长度的字节数组时,需要多次读取,而文件保存时用的是GBK或者UTF-8,每个中文占两个或三个字节,如果某次读取时读到了不完整的中文字符(1/2,1/3,2/3),就会出现乱码。但利用下面的字符流就不会出现这种情况,因为在读取中文的时候会自动转换为unicode字符集的两个字节,刚好是一个char能够放得下的。详情参考自己的印象笔记:关于utf-8所占用的字节

public class FileReaderTest
{public static void main(String[] args){try(FileReader fr = new FileReader("src\\IO系统\\FileReaderTest.java"))// 创建字符输入流{char[] cbuf = new char[32];//创建一个长度为32的“竹筒”int hasRead = 0;//记录每次实际读取的字符数while ((hasRead=fr.read(cbuf)) > 0 )// 使用循环来重复“取水”过程{// 取出“竹筒”中水滴(字符),将字符数组转换成字符串输入!System.out.print(new String(cbuf , 0 , hasRead));}}catch (IOException ex){ex.printStackTrace();}}
}

  上面的FileInputStreamTest程序最后使用了fis.close()来关闭文件输入流。因为与JDBC编程一样,程序里打开的IO资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以显式关闭文件IO资源。而在FileReaderTest不需要的原因是,Java7改写了所有的IO资源类,它们都实现了AutoCloseable接口,因此如果将打开IO资源的声明与初始化写在try的括号里面,那么在try语句块结束后(注意不是在整个try-catch-finnaly结束后),打开的IO资源会被虚拟机自动关闭。

OutputStream和Writer

OutputStream和Writer也有着相似的方法:

  使用FileInputStream和FileOutputStream复制文件的例子:

public class FileOutputStreamTest
{//就是先用输入流从硬盘读到内存中,再用输出流从内存中输出到硬盘上public static void main(String[] args){try(FileInputStream fis = new FileInputStream("src\\IO系统\\FileOutputStreamTest.java");// 创建字节输入流FileOutputStream fos = new FileOutputStream("newFile.txt"))// 创建字节输出流{byte[] bbuf = new byte[32];int hasRead = 0;// 循环从输入流中取出数据while ((hasRead = fis.read(bbuf)) > 0 ){fos.write(bbuf , 0 , hasRead);// 每读取一次,即写入文件输出流,读了多少,就写多少}}catch (IOException ioe){ioe.printStackTrace();}}
}

下面是使用FileWriter进行写入的例子:字符串最后有\r\n,这是windows平台的换行符,通过这种方式就可以让输出内容换行,如果是unix/linux等平台,则使用\n就是换行符。

public class FileWriterTest
{public static void main(String[] args){try(FileWriter fw = new FileWriter("poem.txt")){fw.write("锦瑟 - 李商隐\r\n"); fw.write("锦瑟无端五十弦,一弦一柱思华年。\r\n");fw.write("庄生晓梦迷蝴蝶,望帝春心托杜鹃。\r\n");fw.write("沧海月明珠有泪,蓝田日暖玉生烟。\r\n");fw.write("此情可待成追忆,只是当时已惘然。\r\n");}catch (IOException ioe){ioe.printStackTrace();}    }
}

四、输入输出流体系

  上节介绍了输入输出流的4个抽象基类,并介绍了4个访问文件结点流的用法。借助于处理流,我们可以进一步简化编程

处理流的用法


  下面是处理流PrintStream的例子,它包装OutputStream,使用处理流之后可以更方便,在这里的PrintStream体现为:相比OutputStream,可以直接打印字符串、并且设定打印格式,还能打印其他各种基本数据类型的变量。此外还有可以设置自动flush的功能,在调用了println方法后或者write写入了一个数组后或者write写入了r或者n之后自动flush。详细可参考api或者进行测试

public class PrintStreamTest
{    /**用notepad++实时查看文件是否写入是不准确的的。因为notepad++的机制可能是在你不操作文件的时候,不定时地去解除对文件的占用。当未解除占用的时候,eclipse里面执行完写入也是写不进去的。所以最好是在单步调试中的每次写入操作完毕后,手动打开文件查看一下**/public static void main(String[] args){try(FileOutputStream fos = new FileOutputStream("test.txt");PrintStream ps = new PrintStream(fos,true)){// 使用PrintStream执行输出ps.println("直挂云帆济沧海");// 直接使用PrintStream输出对象ps.println(new PrintStreamTest());}catch (IOException ioe){ioe.printStackTrace();}}
}

提示:由于PrintStream的功能非常强大,通常如果需要输出文本内容,都应该将输出流包装秤PrintStream后进行输出在使用处理流包装了后,关闭输入输出流资源时,只需要关闭最外层的处理流即可,系统会自动关闭该处理流包装的的节点流。其实还是装饰者设计模式的思想

输入输出流体系



上面那张图做了功能上的分类,

  • 文件结点流
  • 数组结点流
  • 字符串结点流
  • 管道节点流

  • 打印控制处理流
  • 推回输入处理流
  • 缓冲处理流
  • 对象序列化处理流
  • 转换处理流(适配器)
  • 数据处理流

需要提一下的是转换处理流内部其实也使用了一个装饰者的机制,先将字节流用StreamDecoder/StreamEncoder进行了装饰,然后再进行的适配,这个装饰的效果之一就是提供了默认大小8192字节的缓冲区,所以可以说适配器也是有着缓冲区功能的。
  另外还有就是FileWriter和FileReader,按照上面节点流的定义,查看源码后,认为不应该算作是节点流,而是转换处理流。因为它正是在转换流内部先根据文件名等信息先创建了真正的文件节点流(FileInputStream、FileOutputStream)对象,再进行转换而已。虽然这点上在概念有分歧但并不影响我们的正常使用。

关于继承关系则可以看下面这几张图,从原图的基础上自己加了文字注释:




  通常来说,字节流的功能比字符流的功能更强大,因为计算机里面的所有数据都是二进制的,而字节流可以处理所有的二进制文件——但问题是,如果使用字节流来处理文本文件,则需要使用合适的方式把这些字节转换成字符。所以,通常的规则是:如果进行输入/输出的内容是文本内容,则应该考虑使用字符流;如果输入/输出的内容是二进制内容,则应该考虑使用字节流。

  此外还有些关于重定向、读写其他进程的数据、任意文件访问类、NIO以及NIO2的相关知识,可以参看疯狂java一书或者其他文章。本篇笔记就写到这了,不能再加了,太卡了...

第十五章 输入输出系统相关推荐

  1. 第二十五章 使用系统监视器 - 应用程序监视器

    文章目录 第二十五章 使用系统监视器 - 配置健康监视器类 设置运行状况监视器选项 应用程序监视器 应用程序监视器概述 第二十五章 使用系统监视器 - 配置健康监视器类 此子菜单中的选项可让自定义 H ...

  2. 《操作系统真象还原》第十五章 ---- 实现系统交互 操作系统最终章 四十五天的不易与坚持终完结撒花 (下)(遗憾告终)

    文章目录 专栏博客链接 相关查阅博客链接 本书中错误勘误 部分缩写熟知 实现exec的思路与启发 遗憾告终 专栏博客链接 <操作系统真象还原>从零开始自制操作系统 全章节博客链接 相关查阅 ...

  3. 《操作系统真象还原》第十五章 ---- 实现系统交互 操作系统最终章 四十五天的不易与坚持终完结撒花(上)

    文章目录 专栏博客链接 相关查阅博客链接 本书中错误勘误 闲聊时刻 实现fork 实现fork的介绍 实现fork的原理 编写完的thread.c(fork_pid) 编写完的thread.h(str ...

  4. 计算机组成原理(第三版)唐朔飞-第五章输入输出系统-课后习题

    目录 第五章 5.1I/O设备有哪些编址方式,各有何特点? 5.2 简要说明CPU与I/O设备之间传递信息可采用哪几种联络方式,它们分别用于什么场合. 5.3I/O设备与主机交换信息时,共有哪几种控制 ...

  5. C/C++教程 第二十五章 —— Linux系统入门

    文章目录 前言 一.理解操作系统 二.安装Linux系统 1.两种安装方式 2.子系统安装Linux系统 3.安装错误的解决办法 三.使用方法 1.基本信息介绍 2.文件操作命令 3.文本编辑 4.命 ...

  6. 计算机组成原理起始位,计算机组成原理第5章(输入输出系统).ppt

    文档介绍: 第五章输入输出系统5.6DMA方式5.5程序中断方式5.4程序查询方式5.3I/O接口5.2外部设备5.1概述辐滤咎尝迅香膜巍辆苞婉绒偶手赘柿基琉硕跑例退佰豆绽蛹姨亮柬鸳行爸计算机组成原理 ...

  7. 【正点原子Linux连载】第四十五章 pinctrl和gpio子系统实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  8. 【正点原子MP157连载】第二十五章 pinctrl和gpio子系统实验-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  9. 【XJTUSE 计算机组成与结构笔记】第十五章 控制器操作Control Unit Operation

    文章目录 第十五章 控制器操作Control Unit Operation Key points 15.1 微操作 取指周期 间接周期 中断周期 执行周期 指令周期 15.2 处理器控制 控制信号 控 ...

最新文章

  1. 肠道微生物的研究不复杂,不信看这篇Science
  2. Nginx 使用中文URL,中文目录路径
  3. 用java制作扑克牌_阿里三面被挂,幸获内推,历经5轮终于拿到口碑offer(java研发)...
  4. boost::geometry::model::multi_point用法的测试程序
  5. zoj 3809 枚举水题 (2014牡丹江网赛 A题)
  6. linux运维之道基础命令,Linux运维之道(7)——Linux管理类命令
  7. 调整 Windows系统参数网址
  8. BCGControlBar MFC对话框换肤 续
  9. linux漏洞知乎_Linux本地内核提权漏洞(CVE-2019-13272)
  10. 基于Jekyll的博客模板
  11. Node.js 学习笔记--- (2)创建一个简单的博客工程
  12. 几个移动端直播SDK开源地址
  13. NTLite精简Windows 7 Ultimate SP1
  14. PSNR峰值信噪比(python代码实现+SSIM+MSIM)
  15. Python告诉你咪蒙10w+文章标题的秘密
  16. es文件浏览器访问ftp服务器,es文件浏览器如何ftp服务器
  17. scheme语言编译成c语言,Scheme语言--简单介绍
  18. 做个优秀的时间管理者
  19. TCP协议:RST标志位
  20. 实习收获之 获取json对象键值的几种方法

热门文章

  1. HM二次开发 - 外部程序采用socket与HM实现通信
  2. BIM与三维GIS结合应用
  3. SpringBoot+vue实现前后端分离的简历系统
  4. QlikView的应用
  5. 华裔科学家张首晟:个人数据完全由个人拥有
  6. 更新mysql表结构,同步数据库表结构
  7. 有一个8位机,采用单总线结构......(计算机组成原理课后习题)
  8. 稳定kvm服务器vps,kvm的服务器vps
  9. VB如何生成EXE文件
  10. 普通用户sftp 无法使用解决方法