1. 概述

Java IO一般包含两个部分:

  1.java.io包中堵塞型IO;

  2.java.nio包中的非堵塞型IO,通常称为New IO。

java.io包下,分为四大块近80个类:

1、基于字节操作的I/O接口:InputStream和OutputStream

2、基于字符操作的I/O接口:Writer和Reader

3、基于磁盘操作的I/O接口:File

4、基于网络操作的I/O接口:Socket(不在java.io包下)

  影响IO性能的无非就是两大因素:数据的格式及存储的方式,前两类主要是数据格式方面的,后两个类是存储方式方面的:本地和网络。

  我们很少单独使用哪个类来实现IO操作,平时都是几个类合起来使用,这其实体现了一种装饰器模式

2. 字节&字符

字节流可以处理任意类型的数据,而字符只能处理字符类型的数据

在Java中把不同的输入/输出源抽象表述为"流"。流是一组有顺序的字节集合,是对数据传输的总称或抽象。

流有输入和输出,输入时是流从数据源流向程序。输出时是流从程序传向数据源,而数据源可以是内存,文件,网络或程序等。

  • 关于字节,每个字节(byte)有8bit组成。
  • 关于字符,我们可能知道代表一个汉字或者英文字母。

3. 编码

Java采用unicode编码,通讲,2个字节来表示一个字符。
在0~127整数之间的字符映射,unicode向下兼容ASCII,也就是1个字节表示一个字符。
一个中文或英文字符的unicode编码都占2个字节。

编码方式 英文字符 中文字符
GB 2312、GBK 1 2
UTF-8 1 3-4
UTF-16 2 3-4
UTF-32 4 4

4. 基于字节的I/O接口

我们先来看看类图:

  

FileInputStream:从文件中读取数据。

ByteArrayInputStream:将内存中的Byte数组适配为一个InputStream。

StringBufferInputStream:将内存中的字符串适配为一个InputStream,内部实现用的是StringBuffer。

             该类被Deprecated。主要原因是StringBuffer不应该属于字节流,所以推荐使用StringReader。

SequenceInputStream:将多个流对象转化成一个InputStream。

PipedInputStream:用于从管道中读取数据,在流中实现了管道的概念。

ObjectInputStream: 该流允许读取或写入用户自定义的类(对象)。

FilterInputStream:装饰器类,为其它InputStream类提供功能。

    DataInputStream:一般和DataOutputStream配对使用,完成基本数据类型的读写。

    BufferedInputStream:使用该对象阻止每次读取一个字节都会频繁操作IO。将字节读取一个缓存区,从缓存区读取。

    LineNumberInputStream:跟踪输入流中的行号。可调用getLineNumber和 setLineNumber(int)方法得到和设置行号。

    PushbackInputStream: 可以在读取最后一个byte 后将其放回到缓存中。主要用在编译器的语法、词法分析部分。

ByteArrayOutputStream:在内存中创建一个buffer。所有写入此流中的数据都被放入到此buffer中。

FileOutputStream:将信息写入文件中。

PipedOutputStream:任何写入此对象的信息都被放入对应PipedInputStream 对象的缓存中,从而完成线程的通信,实现了“管道”的概念。

FilterOutputStream:实现装饰器功能的抽象类。为其它OutputStream对象增加额外的功能。

    DataOutputStream:使用它可以写入基本数据类型。

    BufferedOutputStream:使用它可以避免频繁地向IO写入数据,数据一般都写入一个缓存区,在调用flush方法后会清空缓存、一次完成数据的写入。

    PrintStream:产生具有格式的输出信息。

        System.in, System.out, System.error(注:Java标准输入、输出、错误输出)是PrintStream的实例。

4.1 ObjectInputStream & ObjectOutputStream

  我们在写入文件的时候,常常因为要保存的是一个对象,也就是一个obj,但是里面的变量又很多,我们不可能挨个申明,一个个写入,这时候,我们就可以使用对象的序列化与反序列化。

序列化就是对象到保存文件的过程。
反序列化就是从保存的文件,转换为对象的过程。

一般在以下几种情况下,我们可能会用到序列化:
a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候; 
b)当你想用套接字在网络上传送对象的时候; 
c)当你想通过RMI传输对象的时候。

4.2 PipedInputStream & PipedOutputStream

  管道主要用来实现同一个虚拟机进程中的两个线程进行交流。因此,一个管道既可以作为数据源媒介也可作为目标媒介。

  需要注意的是java中的管道和Unix/Linux中的管道含义并不一样,在Unix/Linux中管道可以作为两个位于不同空间进程通信的媒介,而在java中,管道只能为同一个JVM进程中的不同线程进行通信。

4.3 BufferedInputStream & BufferedOutputStream

  BufferedInputStream顾名思义,就是在对流进行写入时提供一个buffer来提高IO效率。

  在进行磁盘或网络IO时,原始的InputStream对数据读取的过程都是一个字节一个字节操作的,而BufferedInputStream在其内部提供了一个buffer,在读数据时,会一次读取一大块数据到buffer中,这样比单字节的操作效率要高的多,特别是进程磁盘IO和对大量数据进行读写的时候。

  使用BufferedInputStream十分简单,只要把普通的输入流和BufferedInputStream组合到一起即可。

4.4 DataInputStream & DataOutputStream

 (1)编码方式:而DataOutputStream则采用的是UTF-8。

 (2)异常机制:DataOutputStream在通过write()向“输出流”中写入数据时,若产生IOException,会抛出。

 (3)构造函数:DataOutputStream的构造函数只有一个:DataOutputStream(OutputStream out)。即它只支持以输出流out作为“DataOutputStream的输出流”。

 (4)目的:DataOutputStream的作用是装饰其它的输出流,它和DataInputStream配合使用:允许应用程序以与机器无关的方式从底层输入流中读写java数据类型。

4.5 PrintStream

  (1)编码方式:PrintStream是输出时采用的是用户指定的编码(创建PrintStream时指定的),若没有指定,则采用系统默认的字符编码。

  (2)异常机制:与其他输出流不同,PrintStream 永远不会抛出 IOException;它产生的IOException会被自身的函数所捕获并设置错误标记,

           用户可以通过 checkError() 返回错误标记,从而查看PrintStream内部是否产生了IOException。

  (3)构造函数:在PrintStream的构造函数中,能“指定字符集”和“是否支持自动flush()操作”。所谓自动flush,就是往PrintStream写入的数据会立刻调用flush()函数

  (4)目的:PrintStream为其它输出流提供打印各种数据值表示形式,使其它输出流能方便的通过print(), println()或printf()等输出各种格式的数据。

4.6 System.out.println

  (01) out是System.java的静态变量
  (02) 而且out是PrintStream对象,PrintStream.java中有许多重载的println()方法。

FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));

将这两句话细分,可以划分为以下几步:
第1步 FileDescriptor fd = FileDescriptor.out;
第2步 FileOutputStream fdOut = new FileOutputStream(fd);
第3步 BufferedOutputStream bufOut = new BufferedOutputStream(fdOut, 128);
第4步 PrintStream ps = new PrintStream(bufout, true);
第5步 setOut0(ps);

  (01) 获取FileDescriptor.java中的静态成员out,out是一个FileDescriptor对象,它实际上是“标准输出(屏幕)”的标识符。

  (02) 创建“标准输出(屏幕)”对应的“文件输出流”。

  (03) 创建“文件输出流”对应的“缓冲输出流”。目的是为“文件输出流”添加“缓冲”功能。

  (04) 创建“缓冲输出流”对应的“打印输出流”。目的是为“缓冲输出流”提供方便的打印接口,如print(), println(), printf();使其能方便快捷的进行打印输出。

  (05) System.java中setOut0() 执行setOut0(ps),setOut0()是一个native本地方法,就是将ps设置为System.java的out静态变量。

5. 基于字符的I/O接口

  Reader是所有的输入字符流的父类,它是一个抽象类。

    CharReader、StringReader是两种基本的介质流,它们分别将Char数组、String中读取数据。

    PipedReader是从与其它线程共用的管道中读取数据。

    BufferedReader很明显就是一个装饰器,它和其子类负责装饰其它Reader对象。

    FilterReader是所有自定义具体装饰流的父类。   

Writer和Reader操作的目的就是操作字符,不是字节,设计Writer和Reader的目的是国际化,使IO操作支持16位的Unicode。

5.1 InputStreamReader & OutputStreamReader

对于IO操作,不管是磁盘还是网络,最终都是对字节的操作,而我们平时写的程序都是字符形式的,所以在传输的过程中需要进行转换。

在字符到字节的转换过程中,我们需要用到一个类:InputStreamReader。

InputStreamReader是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。

注意:inputStream和reader,outputStream与write的函数都很相似,并且每次进行了IO操作,要记得close,

因为IO资源并不属于内存资源,并不会被GC回收,所以需要显示的手动的回收资源。

对于输出操作,close还会自动flush。

6、基于磁盘的I/O操作(File)

6.1 File

  文件和文件夹的操作都可以用File来完成。

6.2 RandomAccessFile

优点: RandomAccessFile 可以实现对文件的随机读写,但是他并不是继承于以上4中基本虚拟类。

缺点:RandomAccessFile的方法有一个最大的局限,就是只能读写文件,不能读写其他IO节点。

使用场景:RandomAccessFile的一个重要使用场景就是网络请求中的多线程下载及断点续传。 

mode中,有4中启动的方式:

  "r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。

  "rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。

  "rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备

  "rwd" 打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备

注意:RandomAccessFile虽然可以设置了偏移的方法,但他不能实现中间插入的效果,如果你需要实现文本中间插入的话,要先将后面的文件内容拷贝,然后写入,最后在写入的写一行,将拷贝的东西复制回来。

6.3 FileDescriptor

  FileDescriptor 是“文件描述符”。
  FileDescriptor 可以被用来表示开放文件、开放套接字等。
  以FileDescriptor表示文件来说:当FileDescriptor表示某文件时,我们可以通俗的将FileDescriptor看成是该文件。但是,我们不能直接通过FileDescriptor对该文件进行操作;若需要通过FileDescriptor对该文件进行操作,则需要新创建FileDescriptor对应的FileOutputStream,再对文件进行操作。

  
  FileDescriptor的对象in, out, err介绍

  (01) in -- 标准输入(键盘)的描述符

  (02) out -- 标准输出(屏幕)的描述符

  (03) err -- 标准错误输出(屏幕)的描述符

7、基于网络的I/O操作(Socket)

8. 总结

9. 抄录地址

java io系列01之 "目录"(很好值得推荐)

java io系列17之 System.out.println("hello world")原理

Java之美[从菜鸟到高手演变]之Java中的IO

Java IO详解

Java IO用法详解

Java IO完全总结(转载) --- 重点在源码分析

深入分析 Java I/O 的工作机制

http://ifeve.com/java-io/

http://tutorials.jenkov.com/java-io/file.html

Java IO系列之一:IO的更多相关文章

  1. 【网络IO系列】IO的五种模型,BIO、NIO、AIO、IO多路复用、 信号驱动IO

    前言 在上一篇文章中,我们了解了操作系统中内核程序和用户程序之间的区别和联系,还提到了内核空间和用户空间,当我们需要读取一条数据的时候,首先需要发请求告诉内核,我需要什么数据,等内核准备好数据之后 , ...

  2. Java基础系列8——IO流超详细总结

    该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形成自己的知识框架. 在初学Java时,I ...

  3. java面试系列<4>——IO

    面试系列--javaIO 一.概述 java的IO主要分为以下几类: 磁盘操作:File 字节操作:InputStream 和 OutputStream 字符操作:Reader 和 Writer 对象 ...

  4. java io系列16之 PrintStream(打印输出流)详解

    本章介绍PrintStream以及 它与DataOutputStream的区别.我们先对PrintStream有个大致认识,然后再深入学习它的源码,最后通过示例加深对它的了解. 转载请注明出处:htt ...

  5. Java基础复习笔记系列 七 IO操作

    Java基础复习笔记系列之 IO操作 我们说的出入,都是站在程序的角度来说的.FileInputStream是读入数据.?????? 1.流是什么东西? 这章的理解的关键是:形象思维.一个管道插入了一 ...

  6. java io系列01之 "目录"

    java io 系列目录如下: 01. java io系列01之  "目录" 02. java io系列02之 ByteArrayInputStream的简介,源码分析和示例(包括 ...

  7. java io系列06之 序列化总结(Serializable 和 Externalizable)

    本章,我们对序列化进行深入的学习和探讨.学习内容,包括序列化的作用.用途.用法,以及对实现序列化的2种方式Serializable和Externalizable的深入研究. 转载请注明出处:http: ...

  8. java io系列15之 DataOutputStream(数据输出流)的认知、源码和示例

    本章介绍DataOutputStream.我们先对DataOutputStream有个大致认识,然后再深入学习它的源码,最后通过示例加深对它的了解. 转载请注明出处:http://www.cnblog ...

  9. Java IO系列之四:NIO通信模型

    分布式rpc框架有很多,比如dubbo,netty,还有很多其他的产品.但他们大部分都是基于nio的, nio是非阻塞的io,那么它的内部机制是怎么实现的呢. 1.由一个专门的线程处理所有IO事件,并 ...

随机推荐

  1. Android Studio教程06-布局,监听器以及基本控件

    目录 2. 监听器 3. 布局 3.1. 布局分类 (1). Linear Layout (2). Relative Layout (3). ListView (4). Grid View 4. 其他 ...

  2. Java 环境下载设置

    本篇文章出处: http://www.cnblogs.com/xiaofeixiang/p/4085159.html 和 https://www.cnblogs.com/renqiqiang/p/68 ...

  3. LVM 磁盘分区扩容

    前提:将磁盘中未分区磁盘进行分区操作 https://www.cnblogs.com/guoxiangyue/p/10033367.html 然后进行vg扩容 pvcreate /dev/sdc lv ...

  4. windows10 uwp获取设备当前地理位置(经纬度)

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  5. RobotFramework和Eclipse集成-安装和使用说明

    1.安装python3. 安装说明: https://www.cnblogs.com/Simple-Small/p/9179061.html 2.RF安装命令:Pip install RobotFra ...

  6. QinQ 简介

    QinQ 是一种二层隧道协议,通过将用户的私网报文封装上外层 VLAN Tag,使其携带两层 VLAN Tag 穿越公网,从而为用户提供了一种比较简单的二层VPN隧道技术.QinQ 的实现方式可分为两 ...

  7. html简单的知识

    分布式版本控制git       pwd查询当前目录 ls ls -la   git config --global user.name xxx   git config --global user. ...

  8. React 精要面试题讲解(五) 高阶组件真解

    说明与目录 在学习本章内容之前,最好是具备react中'插槽(children)'及'组合与继承' 这两点的知识积累. 详情请参照React 精要面试题讲解(四) 组合与继承不得不说的秘密. 哦不好意 ...

  9. Python-异常处理-66

    异常和错误 # 1/0 # name # 2+'3' # [][3] #{}['k'] try: ') # 1/0 ') # name # 2+'3' # [][3] # {}['k'] ret = ...

  10. 微信h5支付

    分为 微信内H5调起支付 和 非微信浏览器H5支付. 1.H5支付(微信内) 参考链接:https://www.jianshu.com/p/6b9acdd10de6 2.JSAPI支付(非微信) 参考 ...