---------------------------------------- android培训、java培训、期待与您交流! ------------------------------------
IO流体系的知识梳理与深化,用EditPlus以代码格式编写
IO流(重点) 设备之间的数据传输,由于会操作系统底层设备,io操作会抛 IOException,编程时要进行处理 要认清"流"的概念,read 是获取数据到流中,write是将流中的数据写出去 主要体系: 抽象基类,派生出的子类都是以父类作为后缀 ---------------------------------- IO继承体系"*"为较常用; "♂"&"♀"为成对使用 字符流继承体系 //Reader 实现接口Closeable, Flushable, 提供close()和flush()功能 Reader -->InputStreamReader "*"转换流又称桥梁流-->FileReader "*"//由此可见字符流在底层调用了字节流 -->BufferedReader "*"缓冲&装饰-->子类: LineNumberReader 行操作 -->CharArrayReader 操作字符数组 -->StringReader 操作字符串 //Writer 实现接口 Closeable, Flushable, Appendable 多一个append() Appendable接口出现于1.5版本 Writer -->OutputStreamWriter "*" -->FileWriter "*" //append方法用于添加字符 -->BufferedWriter "*" -->PipedWriter -->PrintWriter 打印流 "*" -->CharArrayWriter -->StringWriter 字节流继承体系: //InputStream 实现Closeable接口 close() InputStream -->FileInputStream "*" -->FilterInputStream 过滤流-->BufferedInputStream 字节流缓冲区(装饰) -->DataInputStream 操作基本数据类型 -->SequenceInputStream 合并流 -->ObjectInputStream"♀" 序列流 对象反序列化,将对象数据读取进流 -->PipedInputStream "♂" 管道流,发送 -->ByteArrayInputStream 操作字节数组 //OutputStream 实现Closeable, Flushable接口 close() & flush() OutputStream-->FileOutputStream "*" -->FilterOutputStream-->BufferedOutputStream -->PrintStream 打印流 -->DataOutputStream -->ObjectOutputStream "♂"对象序列化 持久化,对象需实现 Serializable "标记接口" -->PipedOutputStream"♀" 管道流,接收 -->ByteArrayOutputStream IO体系外其它相关类 File"*" 操作文件和目录 RandomAccessFile 随机读写 Properties 该类是 Hashtable 的子类,位于java.util包中 ----------------------------------- 字符流 FileReader 读入步骤: new FileReader(File)/*已有文件*/-->(循环)int read() ||int read(char[])-->操作--(-1 循环结束)-->close() FileWriter 写出步骤: new FileWriter(File)/*创建|覆盖*/-->(循环)write(...)-->flush()-->close() 文件续写: new FileWriter(File,true) ... //将true改为false就是覆盖原文件 字符文件的copy就是循环的read()和write() 字符流缓冲区:用于提高效率 "装饰类" BufferedReader 读入: new BufferedReader(new FileReader(File))-->(循环)String ReadLine()-->操作--(null 循环结束)-->close() 子类: LineNumberReader 使用方法: new LineNumberReader(new FileReader(File)) //.setLineNumber(int n)设置行号,从第n行开始readLine() || .getLineNumber()获取行号 BufferedWriter 写出: new BufferedWriter(new FileWriter(File))-->(循环)write(linne)-->newLine()-->flush()-->close() 使用缓冲区能够减少读写的次数,提高效率 设计模式之--"装饰设计模式"Decorator 当想要对已有对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,提供加强功能,自定的该类称为装饰类 readLine()是read()方法的增强,BufferedReader是FileReader类的增强类 相比继承扩展,装饰模式降低了两个类之间的关联度,避免了继承体系的臃肿,具有更高的灵活性和扩展性 ---------------------------------------------------------------- 字节流 FileInputStream 读入:new FileInputStream(File)-->(循环)int read() || read(byte[])-->操作--(-1 循环结束)-->close() 读入的第三种方式: new FileInputStream(File)--> new byte[ fis.available()]-->read(byte[])-->close() //available返回该文件对象有效的字节数,不使用循环而是一次性写入,文件较大时要慎用(JVM内存分配默认为64m) 在读写文件时,使用数组缓冲与单字节读写相比往往有数十倍的性能提升,可以使用"模板模式"来对比三种方法的效率 一般来说缓冲区大小为1M左右时效率是最高的,当然,不同系统环境也可能有差异 FileOutputStream 写出:new FileOutputStream(File)-->write(byte[])-->close() BufferedInputStream BufferedOutputStream 自定义字节流缓冲区 <==> read & write的特点 计算机中数据以二进制保存,以字节以byte为单位存取。读取时为便于操会将二进制转成十进制 这样问题就随之而来了,字节流的read方法以返回值 -1 来表示到达文件末尾 如果刚好有连续的8个1,直接转成int型就是-1,就意外满足了read 方法-1的控制条件 导致程序提前结束,所以字节流缓冲区的read方法必须避免这种情况的发生生 解决办法:将 byte 转为 int,然后通过二进制"&"运算补0 byte -1 --> int -1 11111111 11111111 11111111 11111111 &00000000 00000000 00000000 11111111 //255 ------------------------------------ 00000000 00000000 00000000 11111111 //int 这也是为什么读取单个字节时read 方法的返回值为 int 而不是 byte 的原因 --------------------------------------- 读取键盘录入 标准的输入输出流: System.in(InputStream) & System.out(PrintStream) 转换流: InputStreamReader OutputStreamWriter 键盘录入最常见形式: BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); //字符 <--字节 BufferedWriter bufw = new BufferedWriter(new InputStreamReader(System.out));//字符 -->字节 设置源和目的: System.setIn(new FileInputStream("ReadIn.java")); //源,也可直接将字节流传入转换流 System.setOut(new PrintStream("printStream.txt")); //目的, FileOutputStream 亦可 1)字节流和字符流最大的区别是,字节流可以操作任何数据,字符流只能操作文本数据。 2)字符流在底层调用了字节流的缓冲区,所以需要刷新动作;而字节流在操作时一般不需要刷新 数据在硬盘中是以二进制存储的,以 byte (8位)为单位,字节流的read 和write 操作单位对象是 byte 字符流在底层调用了字节流,专门用于操作字符,其read 和 write 方法操作的单位对象是 char 使用时要区分"源"&"目的",操作对象是否为纯文本? 字符:字节 -------------------------------------- IO流中另一个重要的类 "File" File 类是文件和目录路径名的抽象表示形式。 专门用于对文件进行增删改查的操作 File类的过滤,两个接口 FileFilter 过滤路径 FilenameFilter 过滤文件名 示例: 过滤获取指定文件夹下的 .java文件 File dir = new File("f:\\JAVA\\test"); String[] arr = dir.list(new FilenameFilter(){//匿名内部类 public boolean accept(File dir,String name) { return name.endsWith(".java"); //String类方法 } }); Java的跨平台分隔符"separator" "递归": 即函数自身调用自身 使用递归要注意: 1)要有明确的递归结束条件;避免死循环 2)控制调用次数,避免栈内存溢出 3)递归书写简洁,但效率较低,应避免在算法题中使用 应用: 遍历访问指定目录下文件及其子目录中内容,并对其进行操作 ----------------- Properties 类 Properties 是hashtable的子类,具备map集合的特点,里面存储的键值对都是字符串,该集合可以加载进IO流 该类提供了一些对io流进行操作的方法 较为熟悉的应用: 系统运行日志: Properties prop = System.getProperties(); 从配置文件中获取数据,以键值对的形式存入集合并进行操作。如限制软件运行次数 time = 5;时停止服务 "打印流" PrintWriter & PrintStream 可以直接操作输入流和文件。 PrintWriter out = new PrintWriter(new FileWriter("...") ,true) PrintWriter 的print & println 就是常用的输出语句sop,//该方法可以自动刷新,换行 "合并流" SequenceInputStream 对多个输入流进行合并,到同一个输出流 "文件的分割" 即将一个输入流对应到多个输出流, 输出大小以一个缓冲数组为单位 "文件的合并" 即将多个输入流指向同一个输出流 使用 Vector & Enumeration :List数组集合枚举 示例: Vector<FileInputStream> v = new Vector<FileInputStream> v.add(new FileInputStream(File))... //添加多个输入流 new SequenceInputStream(v.elements()); //该参数为枚举类型,合并源 定义输出目的...读写操作 使用 Vector+枚举 不够高效,使用 ArrayList+迭代 来代替 而 SequenceInputStream 的构造参数必须为枚举类型 所以就有必要复写 Enumeration 接口中的方法 示例: 定义 ArrayList 和 Iterator 合并: new SequenceInputStream(new Enumeration<FileInputStream>(){ public boolean hasMoreElements(){//接口型匿名内部类 return it.hashNext(); } //将迭代结果作为枚举结果,提高效率 public FileInputStream nextElement(){ return it.next(); } }) "序列流" ObjectInputStream & ObjectOutputStream 这两个类必须成对使用 方法: readObject() & writeObject() 为了在下次程序执行时继续使用对象数据,out序列化将对象保存到硬盘,in反序列化从硬盘读取数据 对对象进行序列化(持久化) 与反序列化操作, 被操作的对象所属类需要实现 Serializable "标记接口" 序列化示例: class Person implements Serializable //被操作的对象要实现标记接口 new ObjectOutputStream(new FileOutputStream("obj.txt")).writeObject(person)//对象写入文件 new ObjectInputStream(new FileInputStream("obj.txt")).readObject() //从文件中读取对象 注:序列化操作的是堆中的对象,所以 static 成员不能被序列化 private 成员无法被序列化,使用 UID 标识可打破该限制 非静态成员加上 transient 修饰就不会被序列化 "管道流" PipedInputStream & PipedOutputStream 这两个类也是一起使用的 一般流的输入输出没有太大关联,而管道流输入输出可以直接进行连接, 必须相互连接后创建通信管道;要结合多线程使用,单线程可能会死锁。 示例: class Read implements Runnable{} //封装线程任务 class Write implements Runnable{} main //主函数调用 PipedInputStream in = new PipedInputStream(); PipedOutputStream out = new PipedOutputStream(in);//构造参数in,也可空参,然后in.connect(out)关联 new Thread(new Read(in)).start(); //创建线程 new Thread(new Write(out)).start(); 阻塞式方法,使用中要预防线程死锁 Object-->RandomAccessFile 随机读写文件 强大的随机读写功能 特性: 构造对象时指定读写权限, "r"只读,"rw"读写 seek(8*x)方法,通过指针从文件的某个位置开始读写 该类是IO包中成员,具备文件读写功能,内部封装了字节输入流和输出流 随机是因为在内部封装了一个byte[] 数组,通过指针对数组的元素进行操作, 同时可以通过getFilePointer获取指针位置,通过seek改变指针位置。 创建对象示例: new RandomAccessFile("File","rw") //能够对该文件读写操作 RandomAccessFile 类如果引入多线程技术,就可以实现 P2P 下载,大大提高效率 其它类似的类区分 RandomAccess util包中的标记接口,被 List 实现,用来表明其支持快速随机访问, 该类与 RandomAccessFile 无直接联系,另见该包中 Random 类 另 java.lang 包中 Math 类的 random() 方法也提供了随机功能"骰子" ---------------------------------------------- 其他基本数据流 DataInputStream & DataOutputStream 专门操作基本数据类型的流对象 构造时要包装字节流使用 如使用 writeInt(22)方法写入; 再使用 readInt()读取到的 数据类型为 int 而不是 byte ByteArrayInputStream & ByteArrayOutputStream 专门用于操作"字节"数组 数据源 为字节数组 CharArrayReader & CharArrayWriter 操作"字符"数组 StringReader & StringWriter 操作字符串 ----------------------------------------------- 字符编码的问题:乱码 如果编码时的码表与解码时的码表不一致,就会导致乱码的情况 转换流 具有指定码表的构造方法 示例: new InputStreamReader(new FileInputStream("gbk.txt") , "utf-8")//指定utf-8来解码 编码与解码的过程: String --编码--> byte[] --解码-->String "中间码"的问题: 客户端与服务器端的码表不一致,例如一个服务器端(iso8859-1)的网页在客户端(中文 GBK)打开时就可能会产生乱码, 所以要加上二次编码与解码的过程,将乱码按照 iso8859-1 码表编码成 byte,再将该数据按照 GBK 解码 要注意的是: 作为中间码,一定要保持数据的精度, 即一个字符占据的字节要少于"终端码", 如: 终端码为GBK时,就不能用utf-8 作为中间码,因为utf-8 部分字符会占据3个字节,gbk为2个 在二次编码的过程中会按照utf-8 码表添加一些字节进去,导致数据精度损失,还会造成乱码。 "为什么不能用字符流copy 图片" 字节流可以操作任何文件,因为其操作单位是数据存储的最基本类型 byte 字符流只能用于操作文本,操作的基本数据类型为字符 char,一个字符一般占据 2(1~3)个字节 字符流在底层使用了字节流,通过查询编码表(java默认为 unicode)获取到字符,如果码表中能查到这两个字节对应的字符 就会返回该 char, 如果没有对应的字符就返回 -1; 所以,如果字符流操作的是文本,该文件内的字节在码表中都能够找到,就不会发生丢失数据的问题 而如果字符流操作的是非文本文件,就不可能保证所有的字节都能在码表中找到对应的字符,而这些找不到字符的字节 随着返回的-1 就消失了,从而造成字节丢失,数据损坏
---------------------------------------- android培训、java培训、期待与您交流! ------------------------------------
原文:http://blog.csdn.net/ie800/article/details/19012535