IO流
流(Stream)的概念:程序与数据来源之间的桥梁
“=============copy自网络========================
理解流的概念
流是字节序列的抽象概念,用于数据传输的字节序列都可以理解为流,流提供了一种用同一的方式从各种输入输出设备中读取和向其中写入字节数据的方法。
文件流
流和文件的差异:文件是数据的静态存储形式,而流是指数据传输时候的形态。文件是一些具有永久存储及特定顺序的字节组成的一个有序的具有名称的数据的集合,流提供了一种向IO设备写入字节和从IO设备读取字节的方式,文件只是流可操作的IO设备之一。除了文件流还存在内存流,网络流等等。
数据流
数据流是一串连续传输的数据的集合。用于写入数据的程序可以一段接一段的向数据流管道中写入数据,这些数据按照写入的先后顺序形成数据流,但对于读取数据流的程序来说,看不到数据写入时的分段情况,它每次可以读取数据流中的任意长度的数据,但只能先读取前面的数据,后读取后面的数据。读取的效果与写入的次数无关。
流类分为两个大类:节点流类和过滤流类(也叫处理流类)。
节点流类:用于直接操作目标设备所对应的类称为节点流类,节点流所对应的IO源(目标)称为流节点。
过滤流类:程序通过一个间接的流类去调用节点流类,可以更加方便的读写各种类型的数据。如要通过网络传输对象,则可以设计一种可以把对象写入到网络中的对象流类,对象流类实际上是要调用底层的网络流类,所以也叫包装类。
===================================================
流的分类:
按数据方向分 :输入流和输出流。输入流:InputStream/Reader ;输出流:OutputStream/Writer按数据类型分 :字节流(byte)和字符流(char)。
字节流:InputStream 、OutputStream。
字符流:Reader体系、Writer体系。
按流的功能分 :节点流和处理流,
1 节点流用操作数据的来源。
2 处理流用来封装节点流,从而给节点流增加一个功能,不能独立存在,在关闭流时如果使用了处理流,只需关闭最外层的流就可以了。
3 区分节点流和处理流的小方法:
4 看构造器,节点流参数为数据来源,而处理流参数为其他流。
选择流的思路:
1 先考虑是输入流还是输出流,理清流的 来源与目的 。2 再考虑是字节流还是字符流,需不需要用到桥梁流来转换。
3 最后考虑是节点流还是处理流。
字符流:Reader和Writer所有字符流的父类型
Java技术使用 Unicode 来表示字符串和字符,而且提供16位版本的流,以便用类似的方法处理字符。如果构造了一个连接到流的Reader和Writer,转换规则会使用平台所定义的 缺省 字节编码(如GBK)和Unicode之间切换。
桥梁流:InputStreamReader和OutputStreamWriter(字节流转化成字符流的桥转换器)
这两个类不是用于直接输入输出的,他是将字节流转换成字符流的桥转换器,并可以指定编解码方式。逐行读写流:BufferedReader/BufferedWriter
两个都有缓冲区的过滤流,需要用其他的节点流来作参数构造对象。BufferedReader的方法:readLine():String ,当他的返回值是null时,就表示读取完毕。要注意,再写入时要注意写换行符,否则会出现阻塞。
BufferedWriter的方法:newLine() ,这个方法会写出一个换行符。
管道流:线程交互的时候使用
PipedInputStream、PipedOutputStream传送输出流可以连接到传送输入流,以创建通信管道。
传送输出流是管道的发送端。
通常,数据由某个线程写入 PipedOutputStream 对象,并由其他线程从连接的 PipedInputStream 读取。
注意:管道输出流和管道输入流需要对接。数据流:DataInputStream和DataOutputStream
通过流来读写Java基本类,注意DataInputStream和DataOutputStream的方法是成对的。
支持直接输出输入各种数据类型。
注意:使用DataOutputStream/DataInputStream时,要注意写入顺序和读取顺序相同,否则会将没有分割写入的信息分割不正确而读取出错误的数据。Properties类:
针对属性文件(*.properties,内容是name=value的map集合)进行操作,在java.util包中
load(InputStream inStream) 从输入流中读取属性列表(键和元素对)。getProperty(String key) 用指定的键在此属性列表中搜索属性。
java编码方式:
编码:把字符转换成数字存储到计算机中,按ASCII将字符映射为整数(或者说,字符对应的编号)。解码:把数字从计算机转换成相应的字符的过程。
不同的国家有不同的编码,当编码方式和解码方式不统一时,产生乱码。
因为美国最早发展软件,所以每种的编码都向上兼容ASCII 所以英文没有乱码。
ASCII(英文) 1个字符占一个字节(所有的编码集都兼容ASCII)
ISO8859-1(拉丁文) 1个字符占一个字节
GB-2312/GBK 1个字符占两个字节(多用于中文)
Unicode 1个字符占两个字节(网络传输速度慢)
UTF-8 Unicode加强版,变长字节的编码,对于英文一个字节,对于汉字两个或三个字节。
中文编码时出现乱码的情况:
用流操作文件。网页(动态静态)。
网络传递消息。
解决乱码的方式:
String temp = 乱码的字符串temp = new String(temp.getBytes("ISO8859-1") , "GBK")
将temp按照ISO8859-1的方式进行解码生成一个字节序列,然后在按照GBK的方式解码字节序列生成字符串。
File类:可表示文件或者目录
File下的方法是对磁盘上的文件进行磁盘操作,但是 无法读写文件的内容 。构造方法:
File(String pathname) //以文件的路径做参数
File类的方法:
boolean createNewFile() 创建一个新文件
File createTempFile(String prefix, String suffix, File directory) 在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称。会在前缀和后缀之间加一个随机数
boolean mkdir() 创建一个新目录
boolean delete() 删除文件,删除的是创建File对象时指定与之关联创建的那个文件。
String[] List() 返回当前File对象下所有显文件和目录名(相对路径)
File[] ListFiles() 返回当前File对象(必须是目录)下的所有File对象,可以用getName()来访问到文件名。
boolean isDirectory()和boolean isFile() 判断究竟是目录还是文件。
boolean exists() 判断文件或文件夹是否存在。
String getPath() 获得相对路径。
String getAbsolutePath() 获得文件的绝对路径。
注意:
File类的对象表示一个文件,但并不是真正的文件,只是相当于一个代理而已,通过这个代理来操作文件
创建一个文件对象和创建一个文件在java中是两个不同的概念。前者是在虚拟机中创建了一个文件,但却并没有将它真正地创建到OS的文件系统中,随着虚拟机的关闭,这个创建的对象也就消失了。而创建一个文件才是在系统中真正地建立一个文件。
例如:File f=new File(“11.txt”); //创建一个名为11.txt的文件对象
f. CreateNewFile(); //真正地创建文件
RandomAccessFile:
允许随机访问文件,类支持直接输出输入各种数据类型。构造方法:
RandomAccessFile(File file, String mode) 创建从中读取和向其中写入(可选)的随机存取文件流,该文件由 File 参数指定。
RandomAccessFile(String name, String mode) 创建从中读取和向其中写入(可选)的随机存取文件流,该文件具有指定名称。
mode( r:以只读方式打开 rw:可读可写,不存在则创建)
相关方法:
long getFilePointer() 返回文件指针的当前位置。
void seek(long pos) 设置文件指针到给定的绝对位置。
long length() 返回文件的长度。
对象流:ObjectInputStream和ObjectOutputStream(实现对象序列化)
对象流是过滤流,需要节点流作参数来构造对象,用于直接把对象写入文件和从文件中读取对象。只有实现了 Serializable 接口的类型的对象才可以被读写,Serializable接口是个标记接口,其中没有定义方法。
对象会序列化成一个二进制代码,文件中保存对象的属性。
writeObject(o)、readObject() 这两个是对象读写操作时用的方法。
- Object o = new Object();
- FileOutputStream fos=new FileOutputStream("Object.txt");
- ObjectOutputStream oos=new ObjectOutputStream(fos);
- oos.writeObject(o);
- oos.close();
- FileInputStream fis =new FileInputStream(“Object.txt”);
- ObjectInputStream ois =new ObjectInputStream(fis);
- Object o = (Object)Ois.readObject();
- ois.close();
一个类中有其他类型的对象,那么,这个类实现了Serializable接口,在对象序列化时,也同样要求这个类中属性都能够对象序列化(基本类型除外)。
注意:
对于对象流的操作,在写对象时要一次写入完毕,如果使用追加模式写入,只会读取到上一次写入的对象,使用对象流写入时,会先写入一个头部,然后写入数据,最后加上结束符号,如果使用追加方式写入的话,那就会在结束符号继续向下写入,但是在读取时只会读到结束符为止,以后再次写入的数据就会丢失。
包名、类名和属性可以被序列化,方法和构造器不会被序列化的。
静态属性不会被序列化的。
属性会被递归序列化的,也就是一个类中有引用类型的属性,如果这个属性对应的类实现了Serializable接口,在对象序列化时,也同样会对这个类中的属性进行对象序列化,如果没有实现Serializable接口,则会抛出异常。
所有属性必须都是可序列化的,特别是当有些属性本身也是对象的时候,要尤其注意这一点。
网络中传递对象必须实现序列化。
nio无阻塞的I/O(优化的I/O)
java.nioBuffer类 :一种用于特定的基本类型数据的容器
缓冲 :就是块,用来存储内容。
容量 :内存开辟的大小,根据类型的不同,有不同的空间。
界限 :可用部分,即不应读取或写入的第一个元素的索引。
位置 :当前指针的位置,从0开始。
容量>=界限>=位置
相关方法:
int capacity() 返回此缓冲区的容量。
int limit() 返回此缓冲区的界限。
int position() 返回此缓冲区的位置。
Buffer flip() 相当于截断没有用的空间,然后把指针移向开头,使limit=position,position=0
Buffer position(int newPosition) 设置此缓冲区的位置。
当有大的文件需要处理的时候,为了不影响性能建议用直接缓冲。
Buffer有直接缓冲和间接缓冲两种。
只有ByteBuffer类提供了直接缓冲。使用直接缓冲,不影响程序。其它类想用直接缓冲需要进行转换。
java.nio.channels 对块进行读写的通道,类似于以前的流
Channel接口:用于 I/O 操作的连接
编程步骤:
a. 先创建一个I/O流,
b. 使用I/O流.getChannel()方法,获得通道,
c. 创建大小合适的ByteBUffer,
d. 通道的对象.read(buffer)/write(buffer)进行读写,
e. 关闭所有的流和通道,
f. 如果有多线程并发,可以使用"通道.lock()"获得FileLock对象,用FileLock.release() 释放此锁定。
g. 当遇到编码问题,使用CharSet、CharsetDecoder、CharsetEncoder三个类去解决
======================================================================================================
FileInputStream读取数据
A:操作步骤
a:创建字节输入流对象
b:调用read()方法
c:释放资源
B:代码体现:
FileInputStream fis = new FileInputStream("fos.txt");
//读的文件必须存在,不存在就会报错
//方式1
int by = 0;
while((by=fis.read())!=-1) {
System.out.print((char)by);
}
//方式2
byte[] bys = new byte[1024];//每次读取1024个字节
int len = 0;
while((len=fis.read(bys))!=-1) {
System.out.print(new String(bys,0,len));
/获取每次读取的实际有用的字节后,转换成字符串输出到控制台
缓冲流实现复制文本文档
面试题:如把c:\\a.txt中的内容复制到D:\\copy.txt中
A:高效字节流来实现
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("c:\\a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\copy.txt"));
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
}
bos.close();
bis.close();
B:高效字符流来实现。
//创建缓冲区读写对象
BufferedReader bfr=new BufferedReader(new FileReader("c:\\a.txt"));
BufferedWriter bfw=new BufferedWriter(new FileWriter("D:\\copy.txt"));
//复制
char[] chs=new char[1024];
int len=0;
while((len=bfr.read(chs))!=-1){
bfw.write(chs, 0, len);
}
//关闭资源
bfr.close();
bfw.close();
缓冲流特有方法来复制
//创建缓冲流对象
BufferedReader br=new BufferedReader(new FileReader("a.txt"));
BufferedWriter bw=new BufferedWriter(new FileWriter("copy.txt"));
//每次读写一行来复制
String str=null;
while((str=br.readLine())!=null){
bw.write(str);//写一行字符串
bw.newLine();//写换行符
bw.flush();
}
//关闭资源
br.close();
bw.close();