1、Java IO 流
io是java中实现输入输出的基础,它可以很方便的完成数据的输入输出操作,Java把不同的输入输出抽象为流,通过流的方式允许Java程序使用相同的方式来访问不同的输入、输出。
2、流的分类
输入流、输出流
A、输入流:只能从中读取数据,而不能向里面写数据
B、 输出流:只能向里面写数据,而不能读数据
可以这样理解,数据从内存到硬盘,通常认为是输出流,即 写操作 ;相反,从硬盘到内存,通常认为是输入流,即读操作;这里的输入、输出是 从内存的角度划分的 。
Java的输入流主要有InputStream和Reader作为基类,而输出流则主要由OutputStream和Writer作为基类;
字节流和字符流
字节流和字符流区别非常简单,它们的用法几乎一样。区别在于字节流和字符流所操作的数据单元不同:字节流操作的最小单元数据是8位字节,而字符流作为最小数据单元是16为字节。
字节流主要由InputStream、OutputStream作为基类,而字符流则主要由Reader和Writer作为基类完成。
节点流和处理流
按照流的角色分,可以分为节点流和处理流。
可以从、向一个特定的IO设备,读写数据流,称为节点流,节点流常常也被常务低级流(Low Level Stream)
处理流则用于对一个已经存在的流进行连接封装,通过封装后来实现数据的读写功能。处理流称为高级流;
当用处理流的进行输入、输出时,程序并不会直接连接到实际数据源,没有和实际的输入、输出节点连接。使用处理流一个明显的好处是:只要使用相同的处理流,程序就可以采用相同的输入、输出代码来访问不同数据源,随着处理流锁包装节点流的改变,程序实际所访问的数据源也相应发生改变。
处理流的功能主要体现在两个方面:
性能提高:主要以增加缓冲方式来提高输入、输出的效率
操作的便捷: 处理流可能提供了系列便捷的方法来一次性输入、输出大批量的内容,而不是输入、输出一个或多个单位数据
处理流可以“嫁接”在任何已经存在的流的基础上,这就允许Java应用程序采用相同的代码、透明的方式来访问不同的输入、输出设备输入流。
字节输入流 InputStream 和字符输入流 Reader
InputStream和Reader是所有输入流的基类,他们都是2个抽象类,本身并不能创建实例来执行输入,但他们有输入流的模版,所以它们的方法是所有的输入流和输出流可以用的方法。
在InputStream里常用的方法:
int read(): 从输入流中读取单个自己
int read(byte[] b): 从输入流中读取最多b.length个字节,将读取的字节存在数组b中,返回实际读取的字节数
int read(byte[] b, int off, int len): 从输入流中读取最多len个字节数据,并将其存储在数字b中,放入b数组中时,并不是从数组起点开始,而是从off位置开始,返回实际读取字节数。
在Reader里经常使用的方法:
int read(): 从输入流中读取单个字符
int read(char[] c): 从输入流读取最多c.length个字符数据,并将其存储在字符数组c中,返回实际读取的字符
int read(char[] c, int off, int len): 从输入流中读取最多len个字符的数据,将读取的数据放到字符数组c中保存,从数组的off开始读取;
InputStream、Reader还支持如下几个方法移动指针:
void mark(int readAheadLimit): 在记录指针当前位置第一个标记(mark)
boolean markSupported(): 判断此输入流是否支持mark()操作,即是否支持记录标记
void reset():将此流的记录的指针重新定位到上一次记录的标记的位置
long skip(long n):记录指针向前移动n个字节、字符
OutputStream 字节输出流和 Writer 字符输出流
具有以下方法:
void write(int c): 指定的字节、字符输出到输出流中
void write(byte[]/char[] buf): 将字节数组/字符数组中的数据传输到指定输出流中
void write(byte[]/char[] buf, int off,int len):将指定数组中的数据输出到指定输出流中
字符输出流还有以下方法:
void write(String s):将指定字符串输出到指定输出流中
void write(String s, int off, int len):将字节数组、字符数组中从off位置开始,长度为len的字节、字符输出到输出流中
3、Java的输入、输出流:
分类 |
字节输入流 |
字节输出流 |
字符输入流 |
字符输出流 |
抽象基类 |
InputStream |
OutputStream |
Reader |
Writer |
访问文件 |
FileInputStream |
FileOutputStream |
FileReader |
FileWriter |
访问数组 |
ByteArrayInputStream |
ByteArrayOutputStream |
CharArrayReader |
CharArrayWriter |
访问管道 |
PipedInputStream |
PipedOutputStream |
PipedReader |
PipedWriter |
访问字符串 |
StringReader |
StringWriter |
||
缓冲流 |
BufferedInputStream |
BufferedOutputStream |
BufferedReader |
BufferedWriter |
转换流 |
InputStreamReader |
OutputStreamWriter |
||
对象流 |
ObjectInputStream |
ObjectOutputStream |
||
抽象基类 |
FilterInputStream |
FilterOutputStream |
FilterReader |
FilterWriter |
打印流 |
PrintStream |
PrintWriter |
||
推回输入流 |
PushbackInputStream |
PushbackReader |
||
特殊流 |
DataInputStream |
DataOutputStream |
输入、输出流的体系:
推回输入流:
有2个特殊流与众不同,就是PushbackInputStream、PushbackReader,它们有以下常用方法:
void unread(byte[]/char[] buf):将一个字节/字符数组推到缓冲区里,运行重复读取推回的内容
void unread(byte[]/char[] buf, int off, int len):将一个字节/字符数组从off位置开始读取,长度是len的字符/字节数组的内容推回到缓冲区中,允许重复刚才读取的内容
void unread(int b):将一个字节、字符推回到缓冲区
这2个推回输入流都带一个缓冲区,当程序调用unread的时候,系统就会把指定数组的内容推回到缓冲区,而推回输入流每次调用read的方法,总会先去读取推回缓冲区中的内容,只有完全读取的缓冲区里面的内容后,而且还没有装满read所需的数组,才会到原输入流中读取内容;
重定向标准输入输出
在System中有3大标准输入、输出方法:
static void setErr(PrintStream e):重定向“标准”错误输出流
static void setIn(InputStream in):重定向“标准”输入流
static void setOut(OutputStream out):重定向“标准”输出流
JVM 写其他进程数据
Runtime对象有exec方法,他可以运行jvm命令行,该方法产生一个Process对象,Process对象代表由该Java程序启动的子进程,Process类有以下方法,可以和子进程通信;
InputStream getErrorStream():获取子进程的错误流
InputStream getInputStream():获取子进程的输入流
OutputStream getOutputStream():获取子进程的输入流
4、RandomAccessFile
RandomAccessFile是Java输入、输出体系中功能最丰富的文件内容访问类,它提供了众多方法来访问文件内容,它既可以读文件内容也可以像文件输出数据。它和普通的输入、输出流不同的是,它可以随机访问的方式,操作文件数据。
RandomAccessFile可以自由定位文件指针,所以RandomAccessFile可以不从开始的地方输出,RandomAccessFile可以向已经存在的文件后追加内容。
RandomAccessFile对象包含一个指针用来记录当前读写的位置,当程序新创建一个RandomAccessFile对象时,该指针位于文件头部,既0的位置;当读取或写入了n个字节后,文件指针就指向最后的位置,除此之外,文件指针也是可以随意移动的,RandomAccessFile有以下方法操作指针:
long getFilePointer():返回文件记录指针的当前位置
void seek(long pos):将文件记录指针定位到pos位置
RandomAccessFile既可以读也可以写,所以它包含了完全类似于InputStream的read方法,和OutputStream的write方法,用法和原来的都一样;而且RandomAccessFile还包含了readXxx、writeXxx的输入输出方法;
RandomAccessFile的文件访问模式:
r:以只读方式打开指定文件
rw:以读取、写入方式打开指定文件,如果文件不存在就创建
rws:以读取、写入的方式打开指定文件,相对应rw模式,还要求文件内容和元数据的每个更新都同步写入到底层存储设备
rwd:以读取、写入方式打开指定文件,对于rw,还要求文件内容的每个更新都同步写入到底层设备
5、序列化
在远程调用、分布式开发中常用到序列化,将java对象序列化的文件中保存,然后在解析的时候又反序列化,其中需要用到Serializable接口;在实现了该接口的对象可以顺利序列化成一个文件保存在硬盘上,反序列化也是通过该接口的id来查找该对象的。
transient可以忽略对象中某个属性不被序列化。
6 新 NIO
nio常用的包介绍:
java.io 包:主要提供了一些和Buffer相关的类
java.io.channels:包括Selector和Channel的相关类
java.nio.charset:包含和字符集相关的类
java.nio.channels.spi:提供Channel服务类
java.nio.charset.spi:提供文字集的服务类
Buffer介绍
Buffer是提取数据的容器,这里有ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer。
上面这些类,除了ByteBuffer之外,都采用类似的方法管理数据,只是各自管理的数据类型不同而已。这些Buffer都没有构造器,通过使用如下方法来得到一个Buffer对象:
static XxxBuffer allocate(int capacity) 创建一个容量为capacity的XxxBuffer对象
ByteBuffer的子类MappedByteBuffer,它用于表示Chanael将硬盘文件的的部分或全部内容映射到内存中后得到结果,通常MappedByteBuffer对象由Chanael的map方法返回。
在Buffer中有三个重要的概念:容量(capacity)、界限(limit)、位置(position)
容量(capacity):缓冲区的容量(capacity)表示该Buffer的最大数据容量,即最多可以存储多少数据,缓冲区的容量不能为负数,在创建后是不能改变的。
界限(limit):第一个不应该被读出或写入的缓冲区位置索引。也就是说,位于limit后的数据既不能读,也不能写。
位置(position):用于指明下一个可以读出的后者写入的缓冲区位置索引。类似于IO流中的记录指针位置。当刚刚新建一个Buffer对象时,容器position为0,如果从Channel读取了2个数据到该Buffer中,则postion为2,指向Buffer中的第三个。
Buffer常用方法:
int capacity():返回Buffer的capacity的大小
boolean hasRemaining():判断当前位置(position)和界限(limit)之间是否还有元素可以提供处理。
int limit:返回Buffer的界限(limit)的位置
Buffer limit(int newLimit)重新设置界限的值,并返回一个具有新的limit的缓冲区的对象
Buffer mark():设置Buffer的mark位置,它只能在0和位置position之间做mark
int position():返回当前Buffer的position
Buffer postion(int newPostion):设置Buffer的新位置,并返回一个具有新的limit的缓冲区对象。
int remaining():返回当前位置和界限之间的元素个数
Buffer reset():将position位置设置到mark的位置
Buffer rewind():将位置(position)设置为0,取消设置的mark
当使用put和get来访问Buffer中的数据时,分为绝对和相对的2种:
相对的Relative:从Buffer中的当前位置读取和写入数据,然后将位置(position)的值按处理元素的个数相加。
绝对Absolute:直接根据索引来向Buffer中读取或写入数据,使用绝对方式来访问Buffer理的数据,并不会影响位置position的值。